From: Michael Jeanson Date: Mon, 10 Jun 2019 21:44:09 +0000 (-0400) Subject: Re-organize sources X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=578e048b5debf169e286e5b5cc747b5d6c16886d Re-organize sources * Create a top-level 'src' directory. * Move all private headers from the public include directory to the 'src' directory. * Rename all those private headers to remove the 'internal' notation. * Use double quotes in `#include` directives when including a private header so that we can easily know. Change-Id: I05fbb81d969b3735aaf303ec2c222be7142c19ab Signed-off-by: Michael Jeanson Reviewed-on: https://review.lttng.org/c/babeltrace/+/1413 Tested-by: jenkins Reviewed-by: Philippe Proulx --- diff --git a/.gitignore b/.gitignore index 9c71bb9d..b460d111 100644 --- a/.gitignore +++ b/.gitignore @@ -58,18 +58,18 @@ ctf-lexer.c ctf-parser.c ctf-parser.h ctf-parser-test -plugins/ctf/common/metadata/lexer.c -plugins/ctf/common/metadata/parser.c -plugins/ctf/common/metadata/parser.h -plugins/ctf/common/metadata/parser.output -/cli/babeltrace2 -/cli/babeltrace2.bin -/cli/babeltrace2-log -/cli/babeltrace2-log.bin -/include/config.h -/include/config.h.in -/include/version.i -/include/version.i.tmp +/src/plugins/ctf/common/metadata/lexer.c +/src/plugins/ctf/common/metadata/parser.c +/src/plugins/ctf/common/metadata/parser.h +/src/plugins/ctf/common/metadata/parser.output +/src/cli/babeltrace2 +/src/cli/babeltrace2.bin +/src/cli/babeltrace2-log +/src/cli/babeltrace2-log.bin +/src/common/config.h +/src/common/config.h.in +/src/common/version.i +/src/common/version.i.tmp /config.status *.log aclocal.m4 @@ -87,8 +87,8 @@ config/ core stamp-h1 __pycache__ -/babeltrace2.pc -/babeltrace2-ctf-writer.pc +/src/babeltrace2.pc +/src/babeltrace2-ctf-writer.pc TAGS cscope* doc/api/Doxyfile diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 32fd7963..3489e0e1 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -284,18 +284,18 @@ The logging API headers are: Public header which a library user can use to control and read libbabeltrace2's current log level. -``:: +``:: Internal, generic logging API which you can use in any Babeltrace subproject. This is the translation of `zf_log.h`. -``:: +``:: Specific internal header to use within the library. This header defines `BT_LOG_OUTPUT_LEVEL` to a custom, library-wide hidden symbol which is the library's current log level before including - ``. + ``. -Do not include `` or -`` in a header which contains logging +Do not include `` or +`` in a header which contains logging statements: this header could be included in source files which define a different <>, for example. See <>. @@ -355,10 +355,10 @@ get the configured minimal log level. `_bt_log_global_output_lvl` symbol. In practice, we never use this symbol, and always make sure that `BT_LOG_OUTPUT_LEVEL` is defined to a module-wise or subproject-wise hidden symbol before including -``. In the library, -`` does this job: just include this +``. In the library, +`` does this job: just include this header which defines `BT_LOG_OUTPUT_LEVEL` to the appropriate symbol -before it includes ``. In plugins, for +before it includes ``. In plugins, for example, there is one log level per component class, which makes log filtering easier during execution. + @@ -593,7 +593,7 @@ Follow those steps to make your module loggable: */ #define BT_LOG_OUTPUT_LEVEL my_module_log_level -#include +#include "logging/log.h" BT_LOG_INIT_LOG_LEVEL(my_module_log_level, "BABELTRACE_MY_MODULE_LOG_LEVEL"); ---- @@ -639,7 +639,7 @@ the environment variable and sets the log level symbol accordingly. */ #define BT_LOG_OUTPUT_LEVEL my_module_log_level -#include +#include "logging/log.h" BT_LOG_LEVEL_EXTERN_SYMBOL(my_module_log_level); diff --git a/LICENSE b/LICENSE index 76475d5f..dae5d84d 100644 --- a/LICENSE +++ b/LICENSE @@ -16,7 +16,7 @@ per-file license. See gpl-2.0.txt for details. * LGPLv2.1 -The file include/babeltrace/list-internal.h is licensed under LGPLv2.1. It only +The file src/bt-list.h is licensed under LGPLv2.1. It only contains trivial static inline functions and macros, and, therefore, including it does not make babeltrace a derivative work on this header. Please refer to the LGPLv2.1 license for details. diff --git a/Makefile.am b/Makefile.am index 938dc75b..745ff31e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,18 +1,9 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = include \ - common \ - ctfser \ - fd-cache \ - compat \ - logging \ - lib \ - python-plugin-provider \ - ctf-writer \ - plugins \ - cli \ - bindings \ - doc \ +SUBDIRS = \ + include \ + src \ + doc \ tests # Directories added to EXTRA_DIST will be recursively copied to the distribution. @@ -24,9 +15,6 @@ dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \ dist_noinst_DATA = CodingStyle -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = babeltrace2.pc babeltrace2-ctf-writer.pc - # This is a convenience target, it's not part of the build process. CONTRIBUTING.html: CONTRIBUTING.adoc $(ASCIIDOC) --verbose -a source-highlighter=pygments $< diff --git a/babeltrace2-ctf-writer.pc.in b/babeltrace2-ctf-writer.pc.in deleted file mode 100644 index 504d8597..00000000 --- a/babeltrace2-ctf-writer.pc.in +++ /dev/null @@ -1,13 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Babeltrace CTF parser -Description: libbabeltrace2-ctf provides the specific bits necessary to write a Common Trace Format (CTF) trace. -Version: @PACKAGE_VERSION@ -Requires: -Requires.private: uuid popt -Libs: -L${libdir} -lbabeltrace2-ctf-writer -Cflags: -I${includedir} - diff --git a/babeltrace2.pc.in b/babeltrace2.pc.in deleted file mode 100644 index 646aeb47..00000000 --- a/babeltrace2.pc.in +++ /dev/null @@ -1,14 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Babeltrace -Description: libbabeltrace2 provides a reader for trace files, reading mainly the -Common Trace Format (CTF). -Version: @PACKAGE_VERSION@ -Requires: -Requires.private: uuid popt -Libs: -L${libdir} -lbabeltrace2 -Cflags: -I${includedir} - diff --git a/bindings/Makefile.am b/bindings/Makefile.am deleted file mode 100644 index 773e43ad..00000000 --- a/bindings/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -if ENABLE_PYTHON_BINDINGS -SUBDIRS = python -endif diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am deleted file mode 100644 index f3cec072..00000000 --- a/bindings/python/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -SUBDIRS = bt2 diff --git a/bindings/python/bt2/.gitignore b/bindings/python/bt2/.gitignore deleted file mode 100644 index b893bced..00000000 --- a/bindings/python/bt2/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -bt2/__init__.py -bt2/native_bt.py -bt2/native_bt_wrap.c -build -build-python-bindings.stamp -installed_files.txt -setup.py diff --git a/bindings/python/bt2/Makefile.am b/bindings/python/bt2/Makefile.am deleted file mode 100644 index 31d9e177..00000000 --- a/bindings/python/bt2/Makefile.am +++ /dev/null @@ -1,117 +0,0 @@ -# Since the shared object used by the python bindings is not built with -# libtool, we need to add the directory containing libbabeltrace2 to the -# linker path. -AM_LDFLAGS=-L$(top_builddir)/lib/.libs - -INSTALLED_FILES=$(builddir)/installed_files.txt - -STATIC_BINDINGS_DEPS = \ - bt2/logging.c \ - bt2/logging.h \ - bt2/native_bt_clock_class.i \ - bt2/native_bt_clock_snapshot.i \ - bt2/native_bt_component_class.i \ - bt2/native_bt_component.i \ - bt2/native_bt_connection.i \ - bt2/native_bt_event_class.i \ - bt2/native_bt_event.i \ - bt2/native_bt_field_class.i \ - bt2/native_bt_field_path.i \ - bt2/native_bt_field.i \ - bt2/native_bt_graph.i \ - bt2/native_bt.i \ - bt2/native_bt_logging.i \ - bt2/native_bt_message.i \ - bt2/native_bt_notifier.i \ - bt2/native_bt_packet.i \ - bt2/native_bt_plugin.i \ - bt2/native_bt_port.i \ - bt2/native_bt_query_exec.i \ - bt2/native_bt_stream_class.i \ - bt2/native_bt_stream.i \ - bt2/native_bt_trace_class.i \ - bt2/native_bt_trace.i \ - bt2/native_bt_value.i \ - bt2/native_bt_version.i \ - bt2/clock_class.py \ - bt2/clock_snapshot.py \ - bt2/component.py \ - bt2/connection.py \ - bt2/event_class.py \ - bt2/event.py \ - bt2/field.py \ - bt2/field_class.py \ - bt2/field_path.py \ - bt2/graph.py \ - bt2/logging.py \ - bt2/message_iterator.py \ - bt2/message.py \ - bt2/object.py \ - bt2/packet.py \ - bt2/plugin.py \ - bt2/port.py \ - bt2/py_plugin.py \ - bt2/query_executor.py \ - bt2/stream_class.py \ - bt2/stream.py \ - bt2/trace.py \ - bt2/trace_class.py \ - bt2/trace_collection_message_iterator.py \ - bt2/utils.py \ - bt2/value.py - -GENERATED_BINDINGS_DEPS = \ - bt2/__init__.py \ - setup.py - -BUILD_FLAGS=CC="$(CC)" \ - CFLAGS="$(GLIB_CFLAGS) $(AM_CFLAGS) $(CFLAGS)" \ - CPPFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS)" \ - LDFLAGS="$(AM_LDFLAGS) $(LDFLAGS) $(GLIB_LIBS) $(LIBS)" - -all-local: build-python-bindings.stamp - -copy-static-deps.stamp: $(addprefix $(srcdir)/, $(STATIC_BINDINGS_DEPS)) - @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ - for file in $(STATIC_BINDINGS_DEPS); do \ - cp -f $(srcdir)/$$file $(builddir)/$$file; \ - done; \ - fi - touch $@ - -build-python-bindings.stamp: copy-static-deps.stamp $(GENERATED_BINDINGS_DEPS) - $(BUILD_FLAGS) $(PYTHON) $(builddir)/setup.py build_ext --force --swig "$(SWIG)" - $(BUILD_FLAGS) $(PYTHON) $(builddir)/setup.py build --force - touch $@ - -install-exec-local: build-python-bindings.stamp - @opts="--prefix=$(prefix) --record $(INSTALLED_FILES) --verbose --no-compile $(DISTSETUPOPTS)"; \ - if [ "$(DESTDIR)" != "" ]; then \ - opts="$$opts --root=$(DESTDIR)"; \ - fi; \ - $(PYTHON) $(builddir)/setup.py install $$opts; - -clean-local: - rm -rf $(builddir)/build - @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ - for file in $(STATIC_BINDINGS_DEPS); do \ - rm -f $(builddir)/$$file; \ - done; \ - fi - -# Distutils' setup.py does not include an uninstall target, we thus need to do -# it manually. We save the path of the files that were installed during the install target -# and delete them during the uninstallation. -uninstall-local: - if [ "$(DESTDIR)" != "" ]; then \ - $(SED) -i "s|^|$(DESTDIR)/|g" $(INSTALLED_FILES); \ - fi - cat $(INSTALLED_FILES) | xargs rm -rf || true - $(GREP) "__init__.py" $(INSTALLED_FILES) | xargs dirname | xargs rm -rf || true - rm -f $(INSTALLED_FILES) - -# distribute: extra Python modules and SWIG interface files -EXTRA_DIST = $(STATIC_BINDINGS_DEPS) - -# clean: generated C and Python files (by SWIG) -CLEANFILES = bt2/native_bt.py bt2/native_bt_wrap.c build-python-bindings.stamp copy-static-deps.stamp diff --git a/bindings/python/bt2/bt2/__init__.py.in b/bindings/python/bt2/bt2/__init__.py.in deleted file mode 100644 index f80cf658..00000000 --- a/bindings/python/bt2/bt2/__init__.py.in +++ /dev/null @@ -1,133 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__version__ = '@PACKAGE_VERSION@' - - -from bt2.clock_class import * -from bt2.clock_snapshot import * -from bt2.component import * -from bt2.component import _FilterComponent -from bt2.component import _GenericFilterComponentClass -from bt2.component import _GenericSinkComponentClass -from bt2.component import _GenericSourceComponentClass -from bt2.component import _SinkComponent -from bt2.component import _SourceComponent -from bt2.component import _UserFilterComponent -from bt2.component import _UserSinkComponent -from bt2.component import _UserSourceComponent -from bt2.connection import * -from bt2.connection import _Connection -from bt2.event import _Event -from bt2.event_class import * -from bt2.field_class import * -from bt2.field_path import * -from bt2.field import * -from bt2.graph import * -from bt2.logging import * -from bt2.message import * -from bt2.message import _DiscardedEventsMessage -from bt2.message import _DiscardedPacketsMessage -from bt2.message_iterator import * -from bt2.message_iterator import _UserMessageIterator -from bt2.packet import _Packet -from bt2.plugin import * -from bt2.port import * -from bt2.py_plugin import * -from bt2.query_executor import * -from bt2.stream import _Stream -from bt2.stream_class import * -from bt2.trace import * -from bt2.trace_class import * -from bt2.trace_collection_message_iterator import * -from bt2.value import * -from bt2.value import _Value -from bt2.value import _IntegerValue - - -class Error(Exception): - pass - - -class CreationError(Error): - '''Raised when object creation fails due to memory issues.''' - - -class InvalidQueryObject(Error): - pass - - -class InvalidQueryParams(Error): - pass - - -class TryAgain(Exception): - pass - - -class Stop(StopIteration): - pass - - -class PortConnectionRefused(Exception): - pass - - -class IncompleteUserClass(Error): - pass - - -class GraphCanceled(Exception): - pass - - -class QueryExecutorCanceled(Exception): - pass - - -class NonexistentClockSnapshot(Error): - pass - - -class _ListenerHandle: - def __init__(self, listener_id, obj): - self._listener_id = listener_id - self._obj = obj - - -def _init_and_register_exit(): - import bt2.native_bt as _native_bt - import atexit - - atexit.register(_native_bt.py3_cc_exit_handler) - version = (_native_bt.version_get_major(), _native_bt.version_get_minor(), - _native_bt.version_get_patch(), _native_bt.version_get_extra()) - _native_bt.py3_cc_init_from_bt2() - - -_init_and_register_exit() - - -try: - del native_bt -except: - pass diff --git a/bindings/python/bt2/bt2/clock_class.py b/bindings/python/bt2/bt2/clock_class.py deleted file mode 100644 index 59a93cd7..00000000 --- a/bindings/python/bt2/bt2/clock_class.py +++ /dev/null @@ -1,144 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2 -import uuid as uuidp - - -class ClockClassOffset: - def __init__(self, seconds=0, cycles=0): - utils._check_int64(seconds) - utils._check_int64(cycles) - self._seconds = seconds - self._cycles = cycles - - @property - def seconds(self): - return self._seconds - - @property - def cycles(self): - return self._cycles - - def __eq__(self, other): - if not isinstance(other, self.__class__): - # not comparing apples to apples - return False - - return (self.seconds, self.cycles) == (other.seconds, other.cycles) - - -class _ClockClass(object._SharedObject): - _get_ref = staticmethod(native_bt.clock_class_get_ref) - _put_ref = staticmethod(native_bt.clock_class_put_ref) - - @property - def name(self): - return native_bt.clock_class_get_name(self._ptr) - - def _name(self, name): - utils._check_str(name) - ret = native_bt.clock_class_set_name(self._ptr, name) - utils._handle_ret(ret, "cannot set clock class object's name") - - _name = property(fset=_name) - - @property - def description(self): - return native_bt.clock_class_get_description(self._ptr) - - def _description(self, description): - utils._check_str(description) - ret = native_bt.clock_class_set_description(self._ptr, description) - utils._handle_ret(ret, "cannot set clock class object's description") - - _description = property(fset=_description) - - @property - def frequency(self): - return native_bt.clock_class_get_frequency(self._ptr) - - def _frequency(self, frequency): - utils._check_uint64(frequency) - native_bt.clock_class_set_frequency(self._ptr, frequency) - - _frequency = property(fset=_frequency) - - @property - def precision(self): - precision = native_bt.clock_class_get_precision(self._ptr) - return precision - - def _precision(self, precision): - utils._check_uint64(precision) - native_bt.clock_class_set_precision(self._ptr, precision) - - _precision = property(fset=_precision) - - @property - def offset(self): - offset_s, offset_cycles = native_bt.clock_class_get_offset(self._ptr) - return ClockClassOffset(offset_s, offset_cycles) - - def _offset(self, offset): - utils._check_type(offset, ClockClassOffset) - native_bt.clock_class_set_offset(self._ptr, offset.seconds, offset.cycles) - - _offset = property(fset=_offset) - - @property - def origin_is_unix_epoch(self): - return native_bt.clock_class_origin_is_unix_epoch(self._ptr) - - def _origin_is_unix_epoch(self, origin_is_unix_epoch): - utils._check_bool(origin_is_unix_epoch) - native_bt.clock_class_set_origin_is_unix_epoch(self._ptr, int(origin_is_unix_epoch)) - - _origin_is_unix_epoch = property(fset=_origin_is_unix_epoch) - - @property - def uuid(self): - uuid_bytes = native_bt.clock_class_get_uuid(self._ptr) - - if uuid_bytes is None: - return - - return uuidp.UUID(bytes=uuid_bytes) - - def _uuid(self, uuid): - utils._check_type(uuid, uuidp.UUID) - native_bt.clock_class_set_uuid(self._ptr, uuid.bytes) - - _uuid = property(fset=_uuid) - - def cycles_to_ns_from_origin(self, cycles): - utils._check_uint64(cycles) - ret, ns = native_bt.clock_class_cycles_to_ns_from_origin(self._ptr, cycles) - - error_msg = "cannot convert clock value to nanoseconds from origin for given clock class" - - if ret == native_bt.CLOCK_CLASS_STATUS_OVERFLOW: - raise OverflowError(error_msg) - - utils._handle_ret(ret, error_msg) - return ns diff --git a/bindings/python/bt2/bt2/clock_snapshot.py b/bindings/python/bt2/bt2/clock_snapshot.py deleted file mode 100644 index 4bf37d62..00000000 --- a/bindings/python/bt2/bt2/clock_snapshot.py +++ /dev/null @@ -1,70 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import numbers -import bt2 -import functools - - -class _BaseClockSnapshot(object._UniqueObject): - @property - def clock_class(self): - cc_ptr = native_bt.clock_snapshot_borrow_clock_class_const(self._ptr) - assert cc_ptr is not None - return bt2.clock_class._ClockClass._create_from_ptr_and_get_ref(cc_ptr) - - -@functools.total_ordering -class _ClockSnapshot(_BaseClockSnapshot): - @property - def value(self): - return native_bt.clock_snapshot_get_value(self._ptr) - - @property - def ns_from_origin(self): - ret, ns = native_bt.clock_snapshot_get_ns_from_origin(self._ptr) - - if ret == native_bt.CLOCK_SNAPSHOT_STATUS_OVERFLOW: - raise OverflowError("cannot get clock snapshot's nanoseconds from origin") - - return ns - - def __eq__(self, other): - if not isinstance(other, numbers.Integral): - return NotImplemented - - return self.value == int(other) - - def __lt__(self, other): - if not isinstance(other, numbers.Integral): - return NotImplemented - - return self.value < int(other) - - -class _UnknownClockSnapshot(_BaseClockSnapshot): - pass - - -class _InfiniteClockSnapshot(_BaseClockSnapshot): - pass diff --git a/bindings/python/bt2/bt2/component.py b/bindings/python/bt2/bt2/component.py deleted file mode 100644 index 3480f657..00000000 --- a/bindings/python/bt2/bt2/component.py +++ /dev/null @@ -1,785 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.message_iterator -import collections.abc -import bt2.value -import traceback -import bt2.port -import sys -import bt2 -import os - - -_env_var = os.environ.get('BABELTRACE_PYTHON_BT2_NO_TRACEBACK') -_NO_PRINT_TRACEBACK = _env_var == '1' - - -# This class wraps a component class pointer. This component class could -# have been created by Python code, but since we only have the pointer, -# we can only wrap it in a generic way and lose the original Python -# class. -# -# Subclasses must implement some methods that this base class uses: -# -# - _as_component_class_ptr: static method, convert the passed component class -# pointer to a 'bt_component_class *'. - -class _GenericComponentClass(object._SharedObject): - @property - def name(self): - ptr = self._as_component_class_ptr(self._ptr) - name = native_bt.component_class_get_name(ptr) - assert name is not None - return name - - @property - def description(self): - ptr = self._as_component_class_ptr(self._ptr) - return native_bt.component_class_get_description(ptr) - - @property - def help(self): - ptr = self._as_component_class_ptr(self._ptr) - return native_bt.component_class_get_help(ptr) - - def _component_class_ptr(self): - return self._as_component_class_ptr(self._ptr) - - def __eq__(self, other): - if not isinstance(other, _GenericComponentClass): - try: - if not issubclass(other, _UserComponent): - return False - except TypeError: - return False - - return self.addr == other.addr - - -class _GenericSourceComponentClass(_GenericComponentClass): - _get_ref = staticmethod(native_bt.component_class_source_get_ref) - _put_ref = staticmethod(native_bt.component_class_source_put_ref) - _as_component_class_ptr = staticmethod(native_bt.component_class_source_as_component_class) - - -class _GenericFilterComponentClass(_GenericComponentClass): - _get_ref = staticmethod(native_bt.component_class_filter_get_ref) - _put_ref = staticmethod(native_bt.component_class_filter_put_ref) - _as_component_class_ptr = staticmethod(native_bt.component_class_filter_as_component_class) - - -class _GenericSinkComponentClass(_GenericComponentClass): - _get_ref = staticmethod(native_bt.component_class_sink_get_ref) - _put_ref = staticmethod(native_bt.component_class_sink_put_ref) - _as_component_class_ptr = staticmethod(native_bt.component_class_sink_as_component_class) - - -def _handle_component_status(status, gen_error_msg): - if status == native_bt.SELF_COMPONENT_STATUS_END: - raise bt2.Stop - elif status == native_bt.SELF_COMPONENT_STATUS_AGAIN: - raise bt2.TryAgain - elif status == native_bt.SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION: - raise bt2.PortConnectionRefused - elif status < 0: - raise bt2.Error(gen_error_msg) - - -class _PortIterator(collections.abc.Iterator): - def __init__(self, comp_ports): - self._comp_ports = comp_ports - self._at = 0 - - def __next__(self): - if self._at == len(self._comp_ports): - raise StopIteration - - comp_ports = self._comp_ports - comp_ptr = comp_ports._component_ptr - - port_ptr = comp_ports._borrow_port_ptr_at_index(comp_ptr, self._at) - assert port_ptr is not None - - name = native_bt.port_get_name(comp_ports._port_pycls._as_port_ptr(port_ptr)) - assert name is not None - - self._at += 1 - return name - - -class _ComponentPorts(collections.abc.Mapping): - - # component_ptr is a bt_component_source *, bt_component_filter * or - # bt_component_sink *. Its type must match the type expected by the - # functions passed as arguments. - - def __init__(self, component_ptr, - borrow_port_ptr_by_name, - borrow_port_ptr_at_index, - get_port_count, - port_pycls): - self._component_ptr = component_ptr - self._borrow_port_ptr_by_name = borrow_port_ptr_by_name - self._borrow_port_ptr_at_index = borrow_port_ptr_at_index - self._get_port_count = get_port_count - self._port_pycls = port_pycls - - def __getitem__(self, key): - utils._check_str(key) - port_ptr = self._borrow_port_ptr_by_name(self._component_ptr, key) - - if port_ptr is None: - raise KeyError(key) - - return self._port_pycls._create_from_ptr_and_get_ref(port_ptr) - - def __len__(self): - count = self._get_port_count(self._component_ptr) - assert count >= 0 - return count - - def __iter__(self): - return _PortIterator(self) - - -# This class holds the methods which are common to both generic -# component objects and Python user component objects. -# -# Subclasses must provide these methods or property: -# -# - _borrow_component_class_ptr: static method, must return a pointer to the -# specialized component class (e.g. 'bt_component_class_sink *') of the -# passed specialized component pointer (e.g. 'bt_component_sink *'). -# - _comp_cls_type: property, one of the native_bt.COMPONENT_CLASS_TYPE_* -# constants. -# - _as_component_ptr: static method, must return the passed specialized -# component pointer (e.g. 'bt_component_sink *') as a 'bt_component *'. - -class _Component: - @property - def name(self): - ptr = self._as_component_ptr(self._ptr) - name = native_bt.component_get_name(ptr) - assert name is not None - return name - - @property - def cls(self): - cc_ptr = self._borrow_component_class_ptr(self._ptr) - assert cc_ptr is not None - return _create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) - - def __eq__(self, other): - if not hasattr(other, 'addr'): - return False - - return self.addr == other.addr - - -class _SourceComponent(_Component): - _borrow_component_class_ptr = staticmethod(native_bt.component_source_borrow_class_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE - _as_component_class_ptr = staticmethod(native_bt.component_class_source_as_component_class) - _as_component_ptr = staticmethod(native_bt.component_source_as_component_const) - - -class _FilterComponent(_Component): - _borrow_component_class_ptr = staticmethod(native_bt.component_filter_borrow_class_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_FILTER - _as_component_class_ptr = staticmethod(native_bt.component_class_filter_as_component_class) - _as_component_ptr = staticmethod(native_bt.component_filter_as_component_const) - - -class _SinkComponent(_Component): - _borrow_component_class_ptr = staticmethod(native_bt.component_sink_borrow_class_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SINK - _as_component_class_ptr = staticmethod(native_bt.component_class_sink_as_component_class) - _as_component_ptr = staticmethod(native_bt.component_sink_as_component_const) - - -# This is analogous to _GenericSourceComponentClass, but for source -# component objects. -class _GenericSourceComponent(object._SharedObject, _SourceComponent): - _get_ref = staticmethod(native_bt.component_source_get_ref) - _put_ref = staticmethod(native_bt.component_source_put_ref) - - @property - def output_ports(self): - return _ComponentPorts(self._ptr, - native_bt.component_source_borrow_output_port_by_name_const, - native_bt.component_source_borrow_output_port_by_index_const, - native_bt.component_source_get_output_port_count, - bt2.port._OutputPort) - - -# This is analogous to _GenericFilterComponentClass, but for filter -# component objects. -class _GenericFilterComponent(object._SharedObject, _FilterComponent): - _get_ref = staticmethod(native_bt.component_filter_get_ref) - _put_ref = staticmethod(native_bt.component_filter_put_ref) - - @property - def output_ports(self): - return _ComponentPorts(self._ptr, - native_bt.component_filter_borrow_output_port_by_name_const, - native_bt.component_filter_borrow_output_port_by_index_const, - native_bt.component_filter_get_output_port_count, - bt2.port._OutputPort) - - @property - def input_ports(self): - return _ComponentPorts(self._ptr, - native_bt.component_filter_borrow_input_port_by_name_const, - native_bt.component_filter_borrow_input_port_by_index_const, - native_bt.component_filter_get_input_port_count, - bt2.port._InputPort) - - -# This is analogous to _GenericSinkComponentClass, but for sink -# component objects. -class _GenericSinkComponent(object._SharedObject, _SinkComponent): - _get_ref = staticmethod(native_bt.component_sink_get_ref) - _put_ref = staticmethod(native_bt.component_sink_put_ref) - - @property - def input_ports(self): - return _ComponentPorts(self._ptr, - native_bt.component_sink_borrow_input_port_by_name_const, - native_bt.component_sink_borrow_input_port_by_index_const, - native_bt.component_sink_get_input_port_count, - bt2.port._InputPort) - - -_COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS = { - native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponent, - native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponent, - native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponent, -} - - -_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS = { - native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponentClass, - native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponentClass, - native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponentClass, -} - - -# Create a component Python object of type _GenericSourceComponent, -# _GenericFilterComponent or _GenericSinkComponent, depending on -# comp_cls_type. -# -# Steals the reference to ptr from the caller. - -def _create_component_from_ptr(ptr, comp_cls_type): - return _COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS[comp_cls_type]._create_from_ptr(ptr) - - -# Same as the above, but acquire a new reference instead of stealing the -# reference from the caller. - -def _create_component_from_ptr_and_get_ref(ptr, comp_cls_type): - return _COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS[comp_cls_type]._create_from_ptr_and_get_ref(ptr) - - -# Create a component class Python object of type -# _GenericSourceComponentClass, _GenericFilterComponentClass or -# _GenericSinkComponentClass, depending on comp_cls_type. -# -# Acquires a new reference to ptr. - -def _create_component_class_from_ptr_and_get_ref(ptr, comp_cls_type): - return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS[comp_cls_type]._create_from_ptr_and_get_ref(ptr) - - -def _trim_docstring(docstring): - lines = docstring.expandtabs().splitlines() - indent = sys.maxsize - - for line in lines[1:]: - stripped = line.lstrip() - - if stripped: - indent = min(indent, len(line) - len(stripped)) - - trimmed = [lines[0].strip()] - - if indent < sys.maxsize: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - - while trimmed and not trimmed[-1]: - trimmed.pop() - - while trimmed and not trimmed[0]: - trimmed.pop(0) - - return '\n'.join(trimmed) - - -# Metaclass for component classes defined by Python code. -# -# The Python user can create a standard Python class which inherits one -# of the three base classes (_UserSourceComponent, _UserFilterComponent, -# or _UserSinkComponent). Those base classes set this class -# (_UserComponentType) as their metaclass. -# -# Once the body of a user-defined component class is executed, this -# metaclass is used to create and initialize the class. The metaclass -# creates a native BT component class of the corresponding type and -# associates it with this user-defined class. The metaclass also defines -# class methods like the `name` and `description` properties to match -# the _GenericComponentClass interface. -# -# The component class name which is used is either: -# -# * The `name` parameter of the class: -# -# class MySink(bt2.SinkComponent, name='my-custom-sink'): -# ... -# -# * If the `name` class parameter is not used: the name of the class -# itself (`MySink` in the example above). -# -# The component class description which is used is the user-defined -# class's docstring: -# -# class MySink(bt2.SinkComponent): -# 'Description goes here' -# ... -# -# A user-defined Python component class can have an __init__() method -# which must at least accept the `params` and `name` arguments: -# -# def __init__(self, params, name, something_else): -# ... -# -# The user-defined component class can also have a _finalize() method -# (do NOT use __del__()) to be notified when the component object is -# finalized. -# -# User-defined source and filter component classes must use the -# `message_iterator_class` class parameter to specify the -# message iterator class to use for this component class: -# -# class MyMessageIterator(bt2._UserMessageIterator): -# ... -# -# class MySource(bt2._UserSourceComponent, -# message_iterator_class=MyMessageIterator): -# ... -# -# This message iterator class must inherit -# bt2._UserMessageIterator, and it must define the _get() and -# _next() methods. The message iterator class can also define an -# __init__() method: this method has access to the original Python -# component object which was used to create it as the `component` -# property. The message iterator class can also define a -# _finalize() method (again, do NOT use __del__()): this is called when -# the message iterator is (really) destroyed. -# -# When the user-defined class is destroyed, this metaclass's __del__() -# method is called: the native BT component class pointer is put (not -# needed anymore, at least not by any Python code since all references -# are dropped for __del__() to be called). -class _UserComponentType(type): - # __new__() is used to catch custom class parameters - def __new__(meta_cls, class_name, bases, attrs, **kwargs): - return super().__new__(meta_cls, class_name, bases, attrs) - - def __init__(cls, class_name, bases, namespace, **kwargs): - super().__init__(class_name, bases, namespace) - - # skip our own bases; they are never directly instantiated by the user - own_bases = ( - '_UserComponent', - '_UserFilterSinkComponent', - '_UserSourceComponent', - '_UserFilterComponent', - '_UserSinkComponent', - ) - - if class_name in own_bases: - return - - comp_cls_name = kwargs.get('name', class_name) - utils._check_str(comp_cls_name) - comp_cls_descr = None - comp_cls_help = None - - if hasattr(cls, '__doc__') and cls.__doc__ is not None: - utils._check_str(cls.__doc__) - docstring = _trim_docstring(cls.__doc__) - lines = docstring.splitlines() - - if len(lines) >= 1: - comp_cls_descr = lines[0] - - if len(lines) >= 3: - comp_cls_help = '\n'.join(lines[2:]) - - iter_cls = kwargs.get('message_iterator_class') - - if _UserSourceComponent in bases: - _UserComponentType._set_iterator_class(cls, iter_cls) - cc_ptr = native_bt.py3_component_class_source_create(cls, - comp_cls_name, - comp_cls_descr, - comp_cls_help) - elif _UserFilterComponent in bases: - _UserComponentType._set_iterator_class(cls, iter_cls) - cc_ptr = native_bt.py3_component_class_filter_create(cls, - comp_cls_name, - comp_cls_descr, - comp_cls_help) - elif _UserSinkComponent in bases: - if not hasattr(cls, '_consume'): - raise bt2.IncompleteUserClass("cannot create component class '{}': missing a _consume() method".format(class_name)) - - cc_ptr = native_bt.py3_component_class_sink_create(cls, - comp_cls_name, - comp_cls_descr, - comp_cls_help) - else: - raise bt2.IncompleteUserClass("cannot find a known component class base in the bases of '{}'".format(class_name)) - - if cc_ptr is None: - raise bt2.CreationError("cannot create component class '{}'".format(class_name)) - - cls._cc_ptr = cc_ptr - - def _init_from_native(cls, comp_ptr, params_ptr): - # create instance, not user-initialized yet - self = cls.__new__(cls) - - # pointer to native self component object (weak/borrowed) - self._ptr = comp_ptr - - # call user's __init__() method - if params_ptr is not None: - params = bt2.value._create_from_ptr_and_get_ref(params_ptr) - else: - params = None - - self.__init__(params) - return self - - def __call__(cls, *args, **kwargs): - raise bt2.Error('cannot directly instantiate a user component from a Python module') - - @staticmethod - def _set_iterator_class(cls, iter_cls): - if iter_cls is None: - raise bt2.IncompleteUserClass("cannot create component class '{}': missing message iterator class".format(cls.__name__)) - - if not issubclass(iter_cls, bt2.message_iterator._UserMessageIterator): - raise bt2.IncompleteUserClass("cannot create component class '{}': message iterator class does not inherit bt2._UserMessageIterator".format(cls.__name__)) - - if not hasattr(iter_cls, '__next__'): - raise bt2.IncompleteUserClass("cannot create component class '{}': message iterator class is missing a __next__() method".format(cls.__name__)) - - cls._iter_cls = iter_cls - - @property - def name(cls): - ptr = cls._as_component_class_ptr(cls._cc_ptr) - return native_bt.component_class_get_name(ptr) - - @property - def description(cls): - ptr = cls._as_component_class_ptr(cls._cc_ptr) - return native_bt.component_class_get_description(ptr) - - @property - def help(cls): - ptr = cls._as_component_class_ptr(cls._cc_ptr) - return native_bt.component_class_get_help(ptr) - - @property - def addr(cls): - return int(cls._cc_ptr) - - def _query_from_native(cls, query_exec_ptr, obj, params_ptr): - # this can raise, in which case the native call to - # bt_component_class_query() returns NULL - if params_ptr is not None: - params = bt2.value._create_from_ptr_and_get_ref(params_ptr) - else: - params = None - - query_exec = bt2.QueryExecutor._create_from_ptr_and_get_ref( - query_exec_ptr) - - # this can raise, but the native side checks the exception - results = cls._query(query_exec, obj, params) - - # this can raise, but the native side checks the exception - results = bt2.create_value(results) - - if results is None: - results_addr = int(native_bt.value_null) - else: - # return new reference - results_addr = int(results._release()) - - return results_addr - - def _query(cls, query_executor, obj, params): - raise NotImplementedError - - def _component_class_ptr(self): - return self._as_component_class_ptr(self._cc_ptr) - - def __del__(cls): - if hasattr(cls, '_cc_ptr'): - cc_ptr = cls._as_component_class_ptr(cls._cc_ptr) - native_bt.component_class_put_ref(cc_ptr) - -# Subclasses must provide these methods or property: -# -# - _as_not_self_specific_component_ptr: static method, must return the passed -# specialized self component pointer (e.g. 'bt_self_component_sink *') as a -# specialized non-self pointer (e.g. 'bt_component_sink *'). -# - _borrow_component_class_ptr: static method, must return a pointer to the -# specialized component class (e.g. 'bt_component_class_sink *') of the -# passed specialized component pointer (e.g. 'bt_component_sink *'). -# - _comp_cls_type: property, one of the native_bt.COMPONENT_CLASS_TYPE_* -# constants. - -class _UserComponent(metaclass=_UserComponentType): - @property - def name(self): - ptr = self._as_not_self_specific_component_ptr(self._ptr) - ptr = self._as_component_ptr(ptr) - name = native_bt.component_get_name(ptr) - assert name is not None - return name - - @property - def cls(self): - comp_ptr = self._as_not_self_specific_component_ptr(self._ptr) - cc_ptr = self._borrow_component_class_ptr(comp_ptr) - return _create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) - - @property - def addr(self): - return int(self._ptr) - - def __init__(self, params=None): - pass - - def _finalize(self): - pass - - def _accept_port_connection(self, port, other_port): - return True - - def _accept_port_connection_from_native(self, self_port_ptr, self_port_type, other_port_ptr): - port = bt2.port._create_self_from_ptr_and_get_ref( - self_port_ptr, self_port_type) - - if self_port_type == native_bt.PORT_TYPE_OUTPUT: - other_port_type = native_bt.PORT_TYPE_INPUT - else: - other_port_type = native_bt.PORT_TYPE_OUTPUT - - other_port = bt2.port._create_from_ptr_and_get_ref( - other_port_ptr, other_port_type) - res = self._accept_port_connection(port, other_port_ptr) - - if type(res) is not bool: - raise TypeError("'{}' is not a 'bool' object") - - return res - - def _port_connected(self, port, other_port): - pass - - def _port_connected_from_native(self, self_port_ptr, self_port_type, other_port_ptr): - port = bt2.port._create_self_from_ptr_and_get_ref( - self_port_ptr, self_port_type) - - if self_port_type == native_bt.PORT_TYPE_OUTPUT: - other_port_type = native_bt.PORT_TYPE_INPUT - else: - other_port_type = native_bt.PORT_TYPE_OUTPUT - - other_port = bt2.port._create_from_ptr_and_get_ref( - other_port_ptr, other_port_type) - self._port_connected(port, other_port) - - def _graph_is_configured_from_native(self): - self._graph_is_configured() - - def _create_trace_class(self, env=None, uuid=None, - assigns_automatic_stream_class_id=True): - ptr = self._as_self_component_ptr(self._ptr) - tc_ptr = native_bt.trace_class_create(ptr) - - if tc_ptr is None: - raise bt2.CreationError('could not create trace class') - - tc = bt2._TraceClass._create_from_ptr(tc_ptr) - - if env is not None: - for key, value in env.items(): - tc.env[key] = value - - if uuid is not None: - tc._uuid = uuid - - tc._assigns_automatic_stream_class_id = assigns_automatic_stream_class_id - - return tc - - def _create_clock_class(self, frequency=None, name=None, description=None, - precision=None, offset=None, origin_is_unix_epoch=True, - uuid=None): - ptr = self._as_self_component_ptr(self._ptr) - cc_ptr = native_bt.clock_class_create(ptr) - - if cc_ptr is None: - raise bt2.CreationError('could not create clock class') - - cc = bt2.clock_class._ClockClass._create_from_ptr(cc_ptr) - - if frequency is not None: - cc._frequency = frequency - - if name is not None: - cc._name = name - - if description is not None: - cc._description = description - - if precision is not None: - cc._precision = precision - - if offset is not None: - cc._offset = offset - - cc._origin_is_unix_epoch = origin_is_unix_epoch - - if uuid is not None: - cc._uuid = uuid - - return cc - - -class _UserSourceComponent(_UserComponent, _SourceComponent): - _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_source_as_component_source) - _as_self_component_ptr = staticmethod(native_bt.self_component_source_as_self_component) - - @property - def _output_ports(self): - def get_output_port_count(self_ptr): - ptr = self._as_not_self_specific_component_ptr(self_ptr) - return native_bt.component_source_get_output_port_count(ptr) - - return _ComponentPorts(self._ptr, - native_bt.self_component_source_borrow_output_port_by_name, - native_bt.self_component_source_borrow_output_port_by_index, - get_output_port_count, - bt2.port._UserComponentOutputPort) - - def _add_output_port(self, name, user_data=None): - utils._check_str(name) - fn = native_bt.self_component_source_add_output_port - comp_status, self_port_ptr = fn(self._ptr, name, user_data) - _handle_component_status(comp_status, - 'cannot add output port to source component object') - assert self_port_ptr is not None - return bt2.port._UserComponentOutputPort._create_from_ptr(self_port_ptr) - - -class _UserFilterComponent(_UserComponent, _FilterComponent): - _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_filter_as_component_filter) - _as_self_component_ptr = staticmethod(native_bt.self_component_filter_as_self_component) - - @property - def _output_ports(self): - def get_output_port_count(self_ptr): - ptr = self._as_not_self_specific_component_ptr(self_ptr) - return native_bt.component_filter_get_output_port_count(ptr) - - return _ComponentPorts(self._ptr, - native_bt.self_component_filter_borrow_output_port_by_name, - native_bt.self_component_filter_borrow_output_port_by_index, - get_output_port_count, - bt2.port._UserComponentOutputPort) - - @property - def _input_ports(self): - def get_input_port_count(self_ptr): - ptr = self._as_not_self_specific_component_ptr(self_ptr) - return native_bt.component_filter_get_input_port_count(ptr) - - return _ComponentPorts(self._ptr, - native_bt.self_component_filter_borrow_input_port_by_name, - native_bt.self_component_filter_borrow_input_port_by_index, - get_input_port_count, - bt2.port._UserComponentInputPort) - - def _add_output_port(self, name, user_data=None): - utils._check_str(name) - fn = native_bt.self_component_filter_add_output_port - comp_status, self_port_ptr = fn(self._ptr, name, user_data) - _handle_component_status(comp_status, - 'cannot add output port to filter component object') - assert self_port_ptr - return bt2.port._UserComponentOutputPort._create_from_ptr(self_port_ptr) - - def _add_input_port(self, name, user_data=None): - utils._check_str(name) - fn = native_bt.self_component_filter_add_input_port - comp_status, self_port_ptr = fn(self._ptr, name, user_data) - _handle_component_status(comp_status, - 'cannot add input port to filter component object') - assert self_port_ptr - return bt2.port._UserComponentInputPort._create_from_ptr(self_port_ptr) - - -class _UserSinkComponent(_UserComponent, _SinkComponent): - _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_sink_as_component_sink) - _as_self_component_ptr = staticmethod(native_bt.self_component_sink_as_self_component) - - @property - def _input_ports(self): - def get_input_port_count(self_ptr): - ptr = self._as_not_self_specific_component_ptr(self_ptr) - return native_bt.component_sink_get_input_port_count(ptr) - - return _ComponentPorts(self._ptr, - native_bt.self_component_sink_borrow_input_port_by_name, - native_bt.self_component_sink_borrow_input_port_by_index, - get_input_port_count, - bt2.port._UserComponentInputPort) - - def _add_input_port(self, name, user_data=None): - utils._check_str(name) - fn = native_bt.self_component_sink_add_input_port - comp_status, self_port_ptr = fn(self._ptr, name, user_data) - _handle_component_status(comp_status, - 'cannot add input port to sink component object') - assert self_port_ptr - return bt2.port._UserComponentInputPort._create_from_ptr(self_port_ptr) diff --git a/bindings/python/bt2/bt2/connection.py b/bindings/python/bt2/bt2/connection.py deleted file mode 100644 index 4c52a776..00000000 --- a/bindings/python/bt2/bt2/connection.py +++ /dev/null @@ -1,43 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, utils -import bt2.message_iterator -import bt2.port -import bt2 - - -class _Connection(bt2.object._SharedObject): - _get_ref = staticmethod(native_bt.connection_get_ref) - _put_ref = staticmethod(native_bt.connection_put_ref) - - @property - def downstream_port(self): - port_ptr = native_bt.connection_borrow_downstream_port_const(self._ptr) - utils._handle_ptr(port_ptr, "cannot get connection object's downstream port object") - return bt2.port._create_from_ptr_and_get_ref(port_ptr, native_bt.PORT_TYPE_INPUT) - - @property - def upstream_port(self): - port_ptr = native_bt.connection_borrow_upstream_port_const(self._ptr) - utils._handle_ptr(port_ptr, "cannot get connection object's upstream port object") - return bt2.port._create_from_ptr_and_get_ref(port_ptr, native_bt.PORT_TYPE_OUTPUT) diff --git a/bindings/python/bt2/bt2/event.py b/bindings/python/bt2/bt2/event.py deleted file mode 100644 index 4ff398b7..00000000 --- a/bindings/python/bt2/bt2/event.py +++ /dev/null @@ -1,115 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2016-2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.clock_class -import bt2.event_class -import bt2.packet -import bt2.stream -import bt2.field -import bt2.clock_snapshot -import bt2 - - -class _Event(object._UniqueObject): - @property - def cls(self): - event_class_ptr = native_bt.event_borrow_class(self._ptr) - assert event_class_ptr is not None - return bt2.event_class._EventClass._create_from_ptr_and_get_ref(event_class_ptr) - - @property - def name(self): - return self.cls.name - - @property - def id(self): - return self.cls.id - - @property - def packet(self): - packet_ptr = native_bt.event_borrow_packet(self._ptr) - assert packet_ptr is not None - return bt2.packet._Packet._create_from_ptr_and_get_ref(packet_ptr) - - @property - def stream(self): - stream_ptr = native_bt.event_borrow_stream(self._ptr) - assert stream_ptr is not None - return bt2._Stream._create_from_ptr_and_get_ref(stream_ptr) - - @property - def common_context_field(self): - field_ptr = native_bt.event_borrow_common_context_field(self._ptr) - - if field_ptr is None: - return - - return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - @property - def specific_context_field(self): - field_ptr = native_bt.event_borrow_specific_context_field(self._ptr) - - if field_ptr is None: - return - - return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - @property - def payload_field(self): - field_ptr = native_bt.event_borrow_payload_field(self._ptr) - - if field_ptr is None: - return - - return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - def __getitem__(self, key): - utils._check_str(key) - payload_field = self.payload_field - - if payload_field is not None and key in payload_field: - return payload_field[key] - - specific_context_field = self.specific_context_field - - if specific_context_field is not None and key in specific_context_field: - return specific_context_field[key] - - common_context_field = self.common_context_field - - if common_context_field is not None and key in common_context_field: - return common_context_field[key] - - packet_context_field = self.packet.context_field - - if packet_context_field is not None and key in packet_context_field: - return packet_context_field[key] - - raise KeyError(key) diff --git a/bindings/python/bt2/bt2/event_class.py b/bindings/python/bt2/bt2/event_class.py deleted file mode 100644 index d3141b86..00000000 --- a/bindings/python/bt2/bt2/event_class.py +++ /dev/null @@ -1,171 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.field_class -import bt2.value -import bt2.event -import bt2 - - -class EventClassLogLevel: - EMERGENCY = native_bt.EVENT_CLASS_LOG_LEVEL_EMERGENCY - ALERT = native_bt.EVENT_CLASS_LOG_LEVEL_ALERT - CRITICAL = native_bt.EVENT_CLASS_LOG_LEVEL_CRITICAL - ERROR = native_bt.EVENT_CLASS_LOG_LEVEL_ERROR - WARNING = native_bt.EVENT_CLASS_LOG_LEVEL_WARNING - NOTICE = native_bt.EVENT_CLASS_LOG_LEVEL_NOTICE - INFO = native_bt.EVENT_CLASS_LOG_LEVEL_INFO - DEBUG_SYSTEM = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM - DEBUG_PROGRAM = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM - DEBUG_PROCESS = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS - DEBUG_MODULE = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE - DEBUG_UNIT = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT - DEBUG_FUNCTION = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION - DEBUG_LINE = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_LINE - DEBUG = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG - - -class _EventClass(object._SharedObject): - _get_ref = staticmethod(native_bt.event_class_get_ref) - _put_ref = staticmethod(native_bt.event_class_put_ref) - - @property - def stream_class(self): - sc_ptr = native_bt.event_class_borrow_stream_class(self._ptr) - - if sc_ptr is not None: - return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(sc_ptr) - - @property - def name(self): - return native_bt.event_class_get_name(self._ptr) - - def _name(self, name): - utils._check_str(name) - return native_bt.event_class_set_name(self._ptr, name) - - _name = property(fset=_name) - - @property - def id(self): - id = native_bt.event_class_get_id(self._ptr) - return id if id >= 0 else None - - @property - def log_level(self): - is_available, log_level = native_bt.event_class_get_log_level(self._ptr) - - if is_available != native_bt.PROPERTY_AVAILABILITY_AVAILABLE: - return None - - return _EVENT_CLASS_LOG_LEVEL_TO_OBJ[log_level] - - def _log_level(self, log_level): - log_levels = ( - EventClassLogLevel.EMERGENCY, - EventClassLogLevel.ALERT, - EventClassLogLevel.CRITICAL, - EventClassLogLevel.ERROR, - EventClassLogLevel.WARNING, - EventClassLogLevel.NOTICE, - EventClassLogLevel.INFO, - EventClassLogLevel.DEBUG_SYSTEM, - EventClassLogLevel.DEBUG_PROGRAM, - EventClassLogLevel.DEBUG_PROCESS, - EventClassLogLevel.DEBUG_MODULE, - EventClassLogLevel.DEBUG_UNIT, - EventClassLogLevel.DEBUG_FUNCTION, - EventClassLogLevel.DEBUG_LINE, - EventClassLogLevel.DEBUG, - ) - - if log_level not in log_levels: - raise ValueError("'{}' is not a valid log level".format(log_level)) - - native_bt.event_class_set_log_level(self._ptr, log_level) - - _log_level = property(fset=_log_level) - - @property - def emf_uri(self): - return native_bt.event_class_get_emf_uri(self._ptr) - - def _emf_uri(self, emf_uri): - utils._check_str(emf_uri) - ret = native_bt.event_class_set_emf_uri(self._ptr, emf_uri) - utils._handle_ret(ret, "cannot set event class object's EMF URI") - - _emf_uri = property(fset=_emf_uri) - - @property - def specific_context_field_class(self): - fc_ptr = native_bt.event_class_borrow_specific_context_field_class_const(self._ptr) - - if fc_ptr is None: - return - - return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) - - def _specific_context_field_class(self, context_field_class): - if context_field_class is not None: - utils._check_type(context_field_class, bt2.field_class._StructureFieldClass) - ret = native_bt.event_class_set_specific_context_field_class(self._ptr, context_field_class._ptr) - utils._handle_ret(ret, "cannot set event class object's context field class") - - _specific_context_field_class = property(fset=_specific_context_field_class) - - @property - def payload_field_class(self): - fc_ptr = native_bt.event_class_borrow_payload_field_class_const(self._ptr) - - if fc_ptr is None: - return - - return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) - - def _payload_field_class(self, payload_field_class): - if payload_field_class is not None: - utils._check_type(payload_field_class, bt2.field_class._StructureFieldClass) - ret = native_bt.event_class_set_payload_field_class(self._ptr, payload_field_class._ptr) - utils._handle_ret(ret, "cannot set event class object's payload field class") - - _payload_field_class = property(fset=_payload_field_class) - - -_EVENT_CLASS_LOG_LEVEL_TO_OBJ = { - native_bt.EVENT_CLASS_LOG_LEVEL_EMERGENCY: EventClassLogLevel.EMERGENCY, - native_bt.EVENT_CLASS_LOG_LEVEL_ALERT: EventClassLogLevel.ALERT, - native_bt.EVENT_CLASS_LOG_LEVEL_CRITICAL: EventClassLogLevel.CRITICAL, - native_bt.EVENT_CLASS_LOG_LEVEL_ERROR: EventClassLogLevel.ERROR, - native_bt.EVENT_CLASS_LOG_LEVEL_WARNING: EventClassLogLevel.WARNING, - native_bt.EVENT_CLASS_LOG_LEVEL_NOTICE: EventClassLogLevel.NOTICE, - native_bt.EVENT_CLASS_LOG_LEVEL_INFO: EventClassLogLevel.INFO, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: EventClassLogLevel.DEBUG_SYSTEM, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: EventClassLogLevel.DEBUG_PROGRAM, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: EventClassLogLevel.DEBUG_PROCESS, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: EventClassLogLevel.DEBUG_MODULE, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: EventClassLogLevel.DEBUG_UNIT, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: EventClassLogLevel.DEBUG_FUNCTION, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: EventClassLogLevel.DEBUG_LINE, - native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG: EventClassLogLevel.DEBUG, -} diff --git a/bindings/python/bt2/bt2/field.py b/bindings/python/bt2/bt2/field.py deleted file mode 100644 index 62dac5ff..00000000 --- a/bindings/python/bt2/bt2/field.py +++ /dev/null @@ -1,648 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.field_class -import collections.abc -import functools -import numbers -import math -import bt2 - - -def _create_field_from_ptr(ptr, owner_ptr, owner_get_ref, owner_put_ref): - field_class_ptr = native_bt.field_borrow_class_const(ptr) - utils._handle_ptr(field_class_ptr, "cannot get field object's class") - typeid = native_bt.field_class_get_type(field_class_ptr) - field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr_and_get_ref( - ptr, owner_ptr, owner_get_ref, owner_put_ref) - return field - - -# Get the "effective" field of `field`. If `field` is a variant, return the -# currently selected field. If `field` is of any other type, return `field` -# directly. - -def _get_leaf_field(field): - if not isinstance(field, _VariantField): - return field - - return _get_leaf_field(field.selected_option) - - -class _Field(object._UniqueObject): - def __eq__(self, other): - other = _get_leaf_field(other) - return self._spec_eq(other) - - @property - def field_class(self): - field_class_ptr = native_bt.field_borrow_class_const(self._ptr) - assert field_class_ptr is not None - return bt2.field_class._create_field_class_from_ptr_and_get_ref(field_class_ptr) - - def _repr(self): - raise NotImplementedError - - def __repr__(self): - return self._repr() - - -@functools.total_ordering -class _NumericField(_Field): - @staticmethod - def _extract_value(other): - if other is True or other is False: - return other - - if isinstance(other, numbers.Integral): - return int(other) - - if isinstance(other, numbers.Real): - return float(other) - - if isinstance(other, numbers.Complex): - return complex(other) - - raise TypeError("'{}' object is not a number object".format(other.__class__.__name__)) - - def __int__(self): - return int(self._value) - - def __float__(self): - return float(self._value) - - def _repr(self): - return repr(self._value) - - def __lt__(self, other): - if not isinstance(other, numbers.Number): - raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__, - other.__class__.__name__)) - - return self._value < float(other) - - def __le__(self, other): - if not isinstance(other, numbers.Number): - raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__, - other.__class__.__name__)) - - return self._value <= float(other) - - def _spec_eq(self, other): - if not isinstance(other, numbers.Number): - return NotImplemented - - return self._value == complex(other) - - def __rmod__(self, other): - return self._extract_value(other) % self._value - - def __mod__(self, other): - return self._value % self._extract_value(other) - - def __rfloordiv__(self, other): - return self._extract_value(other) // self._value - - def __floordiv__(self, other): - return self._value // self._extract_value(other) - - def __round__(self, ndigits=None): - if ndigits is None: - return round(self._value) - else: - return round(self._value, ndigits) - - def __ceil__(self): - return math.ceil(self._value) - - def __floor__(self): - return math.floor(self._value) - - def __trunc__(self): - return int(self._value) - - def __abs__(self): - return abs(self._value) - - def __add__(self, other): - return self._value + self._extract_value(other) - - def __radd__(self, other): - return self.__add__(other) - - def __neg__(self): - return -self._value - - def __pos__(self): - return +self._value - - def __mul__(self, other): - return self._value * self._extract_value(other) - - def __rmul__(self, other): - return self.__mul__(other) - - def __truediv__(self, other): - return self._value / self._extract_value(other) - - def __rtruediv__(self, other): - return self._extract_value(other) / self._value - - def __pow__(self, exponent): - return self._value ** self._extract_value(exponent) - - def __rpow__(self, base): - return self._extract_value(base) ** self._value - - def __iadd__(self, other): - self.value = self + other - return self - - def __isub__(self, other): - self.value = self - other - return self - - def __imul__(self, other): - self.value = self * other - return self - - def __itruediv__(self, other): - self.value = self / other - return self - - def __ifloordiv__(self, other): - self.value = self // other - return self - - def __imod__(self, other): - self.value = self % other - return self - - def __ipow__(self, other): - self.value = self ** other - return self - - -class _IntegralField(_NumericField, numbers.Integral): - def __lshift__(self, other): - return self._value << self._extract_value(other) - - def __rlshift__(self, other): - return self._extract_value(other) << self._value - - def __rshift__(self, other): - return self._value >> self._extract_value(other) - - def __rrshift__(self, other): - return self._extract_value(other) >> self._value - - def __and__(self, other): - return self._value & self._extract_value(other) - - def __rand__(self, other): - return self._extract_value(other) & self._value - - def __xor__(self, other): - return self._value ^ self._extract_value(other) - - def __rxor__(self, other): - return self._extract_value(other) ^ self._value - - def __or__(self, other): - return self._value | self._extract_value(other) - - def __ror__(self, other): - return self._extract_value(other) | self._value - - def __invert__(self): - return ~self._value - - def __ilshift__(self, other): - self.value = self << other - return self - - def __irshift__(self, other): - self.value = self >> other - return self - - def __iand__(self, other): - self.value = self & other - return self - - def __ixor__(self, other): - self.value = self ^ other - return self - - def __ior__(self, other): - self.value = self | other - return self - - -class _IntegerField(_IntegralField, _Field): - pass - - -class _UnsignedIntegerField(_IntegerField, _Field): - _NAME = 'Unsigned integer' - - def _value_to_int(self, value): - if not isinstance(value, numbers.Real): - raise TypeError('expecting a real number object') - - value = int(value) - utils._check_uint64(value) - - return value - - @property - def _value(self): - return native_bt.field_unsigned_integer_get_value(self._ptr) - - def _set_value(self, value): - value = self._value_to_int(value) - native_bt.field_unsigned_integer_set_value(self._ptr, value) - - value = property(fset=_set_value) - - -class _SignedIntegerField(_IntegerField, _Field): - _NAME = 'Signed integer' - - def _value_to_int(self, value): - if not isinstance(value, numbers.Real): - raise TypeError('expecting a real number object') - - value = int(value) - utils._check_int64(value) - - return value - - @property - def _value(self): - return native_bt.field_signed_integer_get_value(self._ptr) - - def _set_value(self, value): - value = self._value_to_int(value) - native_bt.field_signed_integer_set_value(self._ptr, value) - - value = property(fset=_set_value) - - -class _RealField(_NumericField, numbers.Real): - _NAME = 'Real' - - def _value_to_float(self, value): - if not isinstance(value, numbers.Real): - raise TypeError("expecting a real number object") - - return float(value) - - @property - def _value(self): - return native_bt.field_real_get_value(self._ptr) - - def _set_value(self, value): - value = self._value_to_float(value) - native_bt.field_real_set_value(self._ptr, value) - - value = property(fset=_set_value) - - -class _EnumerationField(_IntegerField): - def _repr(self): - return '{} ({})'.format(self._value, ', '.join(self.labels)) - - @property - def labels(self): - ret, labels = self._get_mapping_labels(self._ptr) - utils._handle_ret(ret, "cannot get label for enumeration field") - - assert labels is not None - return labels - - -class _UnsignedEnumerationField(_EnumerationField, _UnsignedIntegerField): - _NAME = 'Unsigned Enumeration' - _get_mapping_labels = staticmethod(native_bt.field_unsigned_enumeration_get_mapping_labels) - - -class _SignedEnumerationField(_EnumerationField, _SignedIntegerField): - _NAME = 'Signed Enumeration' - _get_mapping_labels = staticmethod(native_bt.field_signed_enumeration_get_mapping_labels) - - -@functools.total_ordering -class _StringField(_Field): - _NAME = 'String' - - def _value_to_str(self, value): - if isinstance(value, self.__class__): - value = value._value - - if not isinstance(value, str): - raise TypeError("expecting a 'str' object") - - return value - - @property - def _value(self): - return native_bt.field_string_get_value(self._ptr) - - def _set_value(self, value): - value = self._value_to_str(value) - native_bt.field_string_set_value(self._ptr, value) - - value = property(fset=_set_value) - - def _spec_eq(self, other): - try: - other = self._value_to_str(other) - except Exception: - return False - - return self._value == other - - def __le__(self, other): - return self._value <= self._value_to_str(other) - - def __lt__(self, other): - return self._value < self._value_to_str(other) - - def __bool__(self): - return bool(self._value) - - def _repr(self): - return repr(self._value) - - def __str__(self): - return str(self._value) - - def __getitem__(self, index): - return self._value[index] - - def __len__(self): - return native_bt.field_string_get_length(self._ptr) - - def __iadd__(self, value): - value = self._value_to_str(value) - ret = native_bt.field_string_append(self._ptr, value) - utils._handle_ret(ret, "cannot append to string field object's value") - return self - - -class _ContainerField(_Field): - def __bool__(self): - return len(self) != 0 - - def __len__(self): - count = self._count() - assert count >= 0 - return count - - def __delitem__(self, index): - raise NotImplementedError - - -class _StructureField(_ContainerField, collections.abc.MutableMapping): - _NAME = 'Structure' - - def _count(self): - return len(self.field_class) - - def __setitem__(self, key, value): - # raises if key is somehow invalid - field = self[key] - - # the field's property does the appropriate conversion or raises - # the appropriate exception - field.value = value - - def __iter__(self): - # same name iterator - return iter(self.field_class) - - def _spec_eq(self, other): - try: - if len(self) != len(other): - return False - - for self_key, self_value in self.items(): - if self_key not in other: - return False - - other_value = other[self_key] - - if self_value != other_value: - return False - - return True - except Exception: - return False - - def _set_value(self, values): - try: - for key, value in values.items(): - self[key].value = value - except Exception: - raise - - value = property(fset=_set_value) - - def _repr(self): - items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] - return '{{{}}}'.format(', '.join(items)) - - def __getitem__(self, key): - utils._check_str(key) - field_ptr = native_bt.field_structure_borrow_member_field_by_name(self._ptr, key) - - if field_ptr is None: - raise KeyError(key) - - return _create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - def member_at_index(self, index): - utils._check_uint64(index) - - if index >= len(self): - raise IndexError - - field_ptr = native_bt.field_structure_borrow_member_field_by_index(self._ptr, index) - assert field_ptr is not None - return _create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - -class _VariantField(_ContainerField, _Field): - _NAME = 'Variant' - - @property - def selected_option_index(self): - return native_bt.field_variant_get_selected_option_field_index(self._ptr) - - @selected_option_index.setter - def selected_option_index(self, index): - native_bt.field_variant_select_option_field(self._ptr, index) - - @property - def selected_option(self): - field_ptr = native_bt.field_variant_borrow_selected_option_field(self._ptr) - utils._handle_ptr(field_ptr, "cannot get variant field's selected option") - - return _create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - def _spec_eq(self, other): - new_self = _get_leaf_field(self) - return new_self == other - - def __bool__(self): - raise NotImplementedError - - def __str__(self): - return str(self.selected_option) - - def _repr(self): - return repr(self.selected_option) - - def _set_value(self, value): - self.selected_option.value = value - - value = property(fset=_set_value) - - -class _ArrayField(_ContainerField, _Field): - - def _get_length(self): - return native_bt.field_array_get_length(self._ptr) - - length = property(fget=_get_length) - - def __getitem__(self, index): - if not isinstance(index, numbers.Integral): - raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__)) - - index = int(index) - - if index < 0 or index >= len(self): - raise IndexError('{} field object index is out of range'.format(self._NAME)) - - field_ptr = native_bt.field_array_borrow_element_field_by_index(self._ptr, index) - assert(field_ptr) - return _create_field_from_ptr(field_ptr, self._owner_ptr, - self._owner_get_ref, - self._owner_put_ref) - - def __setitem__(self, index, value): - # we can only set numbers and strings - if not isinstance(value, (numbers.Number, _StringField, str)): - raise TypeError('expecting number or string object') - - # raises if index is somehow invalid - field = self[index] - - if not isinstance(field, (_NumericField, _StringField)): - raise TypeError('can only set the value of a number or string field') - - # the field's property does the appropriate conversion or raises - # the appropriate exception - field.value = value - - def insert(self, index, value): - raise NotImplementedError - - def _spec_eq(self, other): - try: - if len(self) != len(other): - return False - - for self_field, other_field in zip(self, other): - if self_field != other_field: - return False - - return True - except Exception: - return False - - def _repr(self): - return '[{}]'.format(', '.join([repr(v) for v in self])) - - -class _StaticArrayField(_ArrayField, _Field): - _NAME = 'Static array' - - def _count(self): - return native_bt.field_array_get_length(self._ptr) - - def _set_value(self, values): - if len(self) != len(values): - raise ValueError( - 'expected length of value and array field to match') - - for index, value in enumerate(values): - if value is not None: - self[index].value = value - - value = property(fset=_set_value) - - -class _DynamicArrayField(_ArrayField, _Field): - _NAME = 'Dynamic array' - - def _count(self): - return self.length - - def _set_length(self, length): - utils._check_uint64(length) - ret = native_bt.field_dynamic_array_set_length(self._ptr, length) - utils._handle_ret(ret, "cannot set dynamic array length") - - length = property(fget=_ArrayField._get_length, fset=_set_length) - - def _set_value(self, values): - if len(values) != self.length: - self.length = len(values) - - for index, value in enumerate(values): - if value is not None: - self[index].value = value - - value = property(fset=_set_value) - - -_TYPE_ID_TO_OBJ = { - native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerField, - native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerField, - native_bt.FIELD_CLASS_TYPE_REAL: _RealField, - native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationField, - native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationField, - native_bt.FIELD_CLASS_TYPE_STRING: _StringField, - native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureField, - native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayField, - native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayField, - native_bt.FIELD_CLASS_TYPE_VARIANT: _VariantField, -} diff --git a/bindings/python/bt2/bt2/field_class.py b/bindings/python/bt2/bt2/field_class.py deleted file mode 100644 index 27a37d96..00000000 --- a/bindings/python/bt2/bt2/field_class.py +++ /dev/null @@ -1,397 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import collections.abc -import bt2.field -import bt2.field_path -import bt2 - - -def _create_field_class_from_ptr_and_get_ref(ptr): - typeid = native_bt.field_class_get_type(ptr) - return _FIELD_CLASS_TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) - - -class IntegerDisplayBase: - BINARY = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY - OCTAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL - DECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL - HEXADECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL - - -class _FieldClass(object._SharedObject): - _get_ref = staticmethod(native_bt.field_class_get_ref) - _put_ref = staticmethod(native_bt.field_class_put_ref) - - def _check_create_status(self, ptr): - if ptr is None: - raise bt2.CreationError('cannot create {} field class object'.format(self._NAME.lower())) - - -class _IntegerFieldClass(_FieldClass): - @property - def field_value_range(self): - size = native_bt.field_class_integer_get_field_value_range(self._ptr) - assert(size >= 1) - return size - - def _field_value_range(self, size): - if size < 1 or size > 64: - raise ValueError("Value is outside valid range [1, 64] ({})".format(size)) - native_bt.field_class_integer_set_field_value_range(self._ptr, size) - - _field_value_range = property(fset=_field_value_range) - - @property - def preferred_display_base(self): - base = native_bt.field_class_integer_get_preferred_display_base( - self._ptr) - assert(base >= 0) - return base - - def _preferred_display_base(self, base): - utils._check_uint64(base) - - if base not in (IntegerDisplayBase.BINARY, - IntegerDisplayBase.OCTAL, - IntegerDisplayBase.DECIMAL, - IntegerDisplayBase.HEXADECIMAL): - raise ValueError("Display base is not a valid IntegerDisplayBase value") - - native_bt.field_class_integer_set_preferred_display_base( - self._ptr, base) - - _preferred_display_base = property(fset=_preferred_display_base) - - -class _UnsignedIntegerFieldClass(_IntegerFieldClass): - _NAME = 'Unsigned integer' - - -class _SignedIntegerFieldClass(_IntegerFieldClass): - _NAME = 'Signed integer' - - -class _RealFieldClass(_FieldClass): - _NAME = 'Real' - - @property - def is_single_precision(self): - return native_bt.field_class_real_is_single_precision(self._ptr) - - def _is_single_precision(self, is_single_precision): - utils._check_bool(is_single_precision) - native_bt.field_class_real_set_is_single_precision( - self._ptr, is_single_precision) - - _is_single_precision = property(fset=_is_single_precision) - - -class _EnumerationFieldClassMappingRange: - def __init__(self, lower, upper): - self._lower = lower - self._upper = upper - - @property - def lower(self): - return self._lower - - @property - def upper(self): - return self._upper - - def __eq__(self, other): - return self.lower == other.lower and self.upper == other.upper - - -class _EnumerationFieldClassMapping(collections.abc.Set): - def __init__(self, mapping_ptr): - self._mapping_ptr = mapping_ptr - - @property - def label(self): - mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr) - label = native_bt.field_class_enumeration_mapping_get_label(mapping_ptr) - assert label is not None - return label - - def __len__(self): - mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr) - return native_bt.field_class_enumeration_mapping_get_range_count(mapping_ptr) - - def __contains__(self, other_range): - for curr_range in self: - if curr_range == other_range: - return True - return False - - def __iter__(self): - for idx in range(len(self)): - lower, upper = self._get_range_by_index(self._mapping_ptr, idx) - yield _EnumerationFieldClassMappingRange(lower, upper) - - -class _UnsignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping): - _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_as_mapping_const) - _get_range_by_index = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_get_range_by_index) - - -class _SignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping): - _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_signed_enumeration_mapping_as_mapping_const) - _get_range_by_index = staticmethod(native_bt.field_class_signed_enumeration_mapping_get_range_by_index) - - -class _EnumerationFieldClass(_IntegerFieldClass, collections.abc.Mapping): - def __len__(self): - count = native_bt.field_class_enumeration_get_mapping_count(self._ptr) - assert(count >= 0) - return count - - def map_range(self, label, lower, upper=None): - utils._check_str(label) - - if upper is None: - upper = lower - - ret = self._map_range(self._ptr, label, lower, upper) - utils._handle_ret(ret, "cannot add mapping to enumeration field class object") - - def labels_by_value(self, value): - ret, labels = self._get_mapping_labels_by_value(self._ptr, value) - utils._handle_ret(ret, "cannot get mapping labels") - return labels - - def __iter__(self): - for idx in range(len(self)): - mapping = self._get_mapping_by_index(self._ptr, idx) - yield mapping.label - - def __getitem__(self, key): - utils._check_str(key) - for idx in range(len(self)): - mapping = self._get_mapping_by_index(self._ptr, idx) - if mapping.label == key: - return mapping - - raise KeyError(key) - - def __iadd__(self, mappings): - for mapping in mappings.values(): - for range in mapping: - self.map_range(mapping.label, range.lower, range.upper) - - return self - - -class _UnsignedEnumerationFieldClass(_EnumerationFieldClass, _UnsignedIntegerFieldClass): - _NAME = 'Unsigned enumeration' - - @staticmethod - def _get_mapping_by_index(enum_ptr, index): - mapping_ptr = native_bt.field_class_unsigned_enumeration_borrow_mapping_by_index_const(enum_ptr, index) - assert mapping_ptr is not None - return _UnsignedEnumerationFieldClassMapping(mapping_ptr) - - @staticmethod - def _map_range(enum_ptr, label, lower, upper): - utils._check_uint64(lower) - utils._check_uint64(upper) - return native_bt.field_class_unsigned_enumeration_map_range(enum_ptr, label, lower, upper) - - @staticmethod - def _get_mapping_labels_by_value(enum_ptr, value): - utils._check_uint64(value) - return native_bt.field_class_unsigned_enumeration_get_mapping_labels_by_value(enum_ptr, value) - - -class _SignedEnumerationFieldClass(_EnumerationFieldClass, _SignedIntegerFieldClass): - _NAME = 'Signed enumeration' - - @staticmethod - def _get_mapping_by_index(enum_ptr, index): - mapping_ptr = native_bt.field_class_signed_enumeration_borrow_mapping_by_index_const(enum_ptr, index) - assert mapping_ptr is not None - return _SignedEnumerationFieldClassMapping(mapping_ptr) - - @staticmethod - def _map_range(enum_ptr, label, lower, upper): - utils._check_int64(lower) - utils._check_int64(upper) - return native_bt.field_class_signed_enumeration_map_range(enum_ptr, label, lower, upper) - - @staticmethod - def _get_mapping_labels_by_value(enum_ptr, value): - utils._check_int64(value) - return native_bt.field_class_signed_enumeration_get_mapping_labels_by_value(enum_ptr, value) - - -class _StringFieldClass(_FieldClass): - _NAME = 'String' - - -class _FieldContainer(collections.abc.Mapping): - def __len__(self): - count = self._get_element_count(self._ptr) - assert count >= 0 - return count - - def __getitem__(self, key): - if not isinstance(key, str): - raise TypeError("key should be a 'str' object, got {}".format(key.__class__.__name__)) - - ptr = self._borrow_field_class_ptr_by_name(key) - - if ptr is None: - raise KeyError(key) - - return _create_field_class_from_ptr_and_get_ref(ptr) - - def _borrow_field_class_ptr_by_name(self, key): - element_ptr = self._borrow_element_by_name(self._ptr, key) - if element_ptr is None: - return - - return self._element_borrow_field_class(element_ptr) - - def __iter__(self): - for idx in range(len(self)): - element_ptr = self._borrow_element_by_index(self._ptr, idx) - assert element_ptr is not None - - yield self._element_get_name(element_ptr) - - def _append_element_common(self, name, field_class): - utils._check_str(name) - utils._check_type(field_class, _FieldClass) - ret = self._append_element(self._ptr, name, field_class._ptr) - utils._handle_ret(ret, "cannot add field to {} field class object".format(self._NAME.lower())) - - def __iadd__(self, fields): - for name, field_class in fields.items(): - self._append_element_common(name, field_class) - - return self - - def _at_index(self, index): - utils._check_uint64(index) - - if index < 0 or index >= len(self): - raise IndexError - - element_ptr = self._borrow_element_by_index(self._ptr, index) - assert element_ptr is not None - - field_class_ptr = self._element_borrow_field_class(element_ptr) - - return _create_field_class_from_ptr_and_get_ref(field_class_ptr) - - -class _StructureFieldClass(_FieldClass, _FieldContainer): - _NAME = 'Structure' - _borrow_element_by_index = staticmethod(native_bt.field_class_structure_borrow_member_by_index_const) - _borrow_element_by_name = staticmethod(native_bt.field_class_structure_borrow_member_by_name_const) - _element_get_name = staticmethod(native_bt.field_class_structure_member_get_name) - _element_borrow_field_class = staticmethod(native_bt.field_class_structure_member_borrow_field_class_const) - _get_element_count = staticmethod(native_bt.field_class_structure_get_member_count) - _append_element = staticmethod(native_bt.field_class_structure_append_member) - - def append_member(self, name, field_class): - return self._append_element_common(name, field_class) - - def member_at_index(self, index): - return self._at_index(index) - - -class _VariantFieldClass(_FieldClass, _FieldContainer): - _NAME = 'Variant' - _borrow_element_by_index = staticmethod(native_bt.field_class_variant_borrow_option_by_index_const) - _borrow_element_by_name = staticmethod(native_bt.field_class_variant_borrow_option_by_name_const) - _element_get_name = staticmethod(native_bt.field_class_variant_option_get_name) - _element_borrow_field_class = staticmethod(native_bt.field_class_variant_option_borrow_field_class_const) - _get_element_count = staticmethod(native_bt.field_class_variant_get_option_count) - _append_element = staticmethod(native_bt.field_class_variant_append_option) - - def append_option(self, name, field_class): - return self._append_element_common(name, field_class) - - def option_at_index(self, index): - return self._at_index(index) - - @property - def selector_field_path(self): - ptr = native_bt.field_class_variant_borrow_selector_field_path_const(self._ptr) - if ptr is None: - return - - return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr) - - def _set_selector_field_class(self, selector_fc): - utils._check_type(selector_fc, bt2.field_class._EnumerationFieldClass) - ret = native_bt.field_class_variant_set_selector_field_class(self._ptr, selector_fc._ptr) - utils._handle_ret(ret, "cannot set variant selector field type") - - _selector_field_class = property(fset=_set_selector_field_class) - - -class _ArrayFieldClass(_FieldClass): - @property - def element_field_class(self): - elem_fc_ptr = native_bt.field_class_array_borrow_element_field_class_const(self._ptr) - return _create_field_class_from_ptr_and_get_ref(elem_fc_ptr) - - -class _StaticArrayFieldClass(_ArrayFieldClass): - @property - def length(self): - return native_bt.field_class_static_array_get_length(self._ptr) - - -class _DynamicArrayFieldClass(_ArrayFieldClass): - @property - def length_field_path(self): - ptr = native_bt.field_class_dynamic_array_borrow_length_field_path_const(self._ptr) - if ptr is None: - return - - return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr) - - def _set_length_field_class(self, length_fc): - utils._check_type(length_fc, _UnsignedIntegerFieldClass) - ret = native_bt.field_class_dynamic_array_set_length_field_class(self._ptr, length_fc._ptr) - utils._handle_ret(ret, "cannot set dynamic array length field type") - - _length_field_class = property(fset=_set_length_field_class) - - -_FIELD_CLASS_TYPE_TO_OBJ = { - native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerFieldClass, - native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerFieldClass, - native_bt.FIELD_CLASS_TYPE_REAL: _RealFieldClass, - native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationFieldClass, - native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationFieldClass, - native_bt.FIELD_CLASS_TYPE_STRING: _StringFieldClass, - native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureFieldClass, - native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayFieldClass, - native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayFieldClass, - native_bt.FIELD_CLASS_TYPE_VARIANT: _VariantFieldClass, -} diff --git a/bindings/python/bt2/bt2/field_path.py b/bindings/python/bt2/bt2/field_path.py deleted file mode 100644 index 2191171e..00000000 --- a/bindings/python/bt2/bt2/field_path.py +++ /dev/null @@ -1,82 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2018 Francis Deslauriers -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import collections -from bt2 import native_bt, object - - -class Scope: - PACKET_CONTEXT = native_bt.SCOPE_PACKET_CONTEXT - EVENT_COMMON_CONTEXT = native_bt.SCOPE_EVENT_COMMON_CONTEXT - EVENT_SPECIFIC_CONTEXT = native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT - EVENT_PAYLOAD = native_bt.SCOPE_EVENT_PAYLOAD - - -class _FieldPathItem: - pass - - -class _IndexFieldPathItem(_FieldPathItem): - def __init__(self, index): - self._index = index - - @property - def index(self): - return self._index - - -class _CurrentArrayElementFieldPathItem(_FieldPathItem): - pass - - -class _FieldPath(object._SharedObject, collections.abc.Iterable): - _get_ref = staticmethod(native_bt.field_path_get_ref) - _put_ref = staticmethod(native_bt.field_path_put_ref) - - @property - def root_scope(self): - scope = native_bt.field_path_get_root_scope(self._ptr) - return _SCOPE_TO_OBJ[scope] - - def __len__(self): - return native_bt.field_path_get_item_count(self._ptr) - - def __iter__(self): - for idx in range(len(self)): - item_ptr = native_bt.field_path_borrow_item_by_index_const(self._ptr, idx) - assert item_ptr is not None - item_type = native_bt.field_path_item_get_type(item_ptr) - if item_type == native_bt.FIELD_PATH_ITEM_TYPE_INDEX: - idx = native_bt.field_path_item_index_get_index(item_ptr) - yield _IndexFieldPathItem(idx) - elif item_type == native_bt.FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: - yield _CurrentArrayElementFieldPathItem() - else: - assert False - - -_SCOPE_TO_OBJ = { - native_bt.SCOPE_PACKET_CONTEXT: Scope.PACKET_CONTEXT, - native_bt.SCOPE_EVENT_COMMON_CONTEXT: Scope.EVENT_COMMON_CONTEXT, - native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT: Scope.EVENT_SPECIFIC_CONTEXT, - native_bt.SCOPE_EVENT_PAYLOAD: Scope.EVENT_PAYLOAD -} diff --git a/bindings/python/bt2/bt2/graph.py b/bindings/python/bt2/bt2/graph.py deleted file mode 100644 index 998d310b..00000000 --- a/bindings/python/bt2/bt2/graph.py +++ /dev/null @@ -1,179 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.connection -import bt2.component -import functools -import bt2.port -import bt2 - - -def _graph_port_added_listener_from_native(user_listener, - component_ptr, component_type, - port_ptr, port_type): - component = bt2.component._create_component_from_ptr_and_get_ref(component_ptr, component_type) - port = bt2.port._create_from_ptr_and_get_ref(port_ptr, port_type) - user_listener(component, port) - - -def _graph_ports_connected_listener_from_native(user_listener, - upstream_component_ptr, upstream_component_type, - upstream_port_ptr, - downstream_component_ptr, downstream_component_type, - downstream_port_ptr): - upstream_component = bt2.component._create_component_from_ptr_and_get_ref( - upstream_component_ptr, upstream_component_type) - upstream_port = bt2.port._create_from_ptr_and_get_ref( - upstream_port_ptr, native_bt.PORT_TYPE_OUTPUT) - downstream_component = bt2.component._create_component_from_ptr_and_get_ref( - downstream_component_ptr, downstream_component_type) - downstream_port = bt2.port._create_from_ptr_and_get_ref( - downstream_port_ptr, native_bt.PORT_TYPE_INPUT) - user_listener(upstream_component, upstream_port, downstream_component, downstream_port) - - -class Graph(object._SharedObject): - _get_ref = staticmethod(native_bt.graph_get_ref) - _put_ref = staticmethod(native_bt.graph_put_ref) - - def __init__(self): - ptr = native_bt.graph_create() - - if ptr is None: - raise bt2.CreationError('cannot create graph object') - - super().__init__(ptr) - - def _handle_status(self, status, gen_error_msg): - if status == native_bt.GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - raise bt2.PortConnectionRefused - elif status == native_bt.GRAPH_STATUS_CANCELED: - raise bt2.GraphCanceled - elif status == native_bt.GRAPH_STATUS_END: - raise bt2.Stop - elif status == native_bt.GRAPH_STATUS_AGAIN: - raise bt2.TryAgain - elif status < 0: - raise bt2.Error(gen_error_msg) - - def add_component(self, component_class, name, params=None): - if isinstance(component_class, bt2.component._GenericSourceComponentClass): - cc_ptr = component_class._ptr - add_fn = native_bt.graph_add_source_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE - elif isinstance(component_class, bt2.component._GenericFilterComponentClass): - cc_ptr = component_class._ptr - add_fn = native_bt.graph_add_filter_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_FILTER - elif isinstance(component_class, bt2.component._GenericSinkComponentClass): - cc_ptr = component_class._ptr - add_fn = native_bt.graph_add_sink_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_SINK - elif issubclass(component_class, bt2.component._UserSourceComponent): - cc_ptr = component_class._cc_ptr - add_fn = native_bt.graph_add_source_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE - elif issubclass(component_class, bt2.component._UserSinkComponent): - cc_ptr = component_class._cc_ptr - add_fn = native_bt.graph_add_sink_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_SINK - elif issubclass(component_class, bt2.component._UserFilterComponent): - cc_ptr = component_class._cc_ptr - add_fn = native_bt.graph_add_filter_component - cc_type = native_bt.COMPONENT_CLASS_TYPE_FILTER - else: - raise TypeError("'{}' is not a component class".format( - component_class.__class__.__name__)) - - utils._check_str(name) - params = bt2.create_value(params) - - params_ptr = params._ptr if params is not None else None - - status, comp_ptr = add_fn(self._ptr, cc_ptr, name, params_ptr) - self._handle_status(status, 'cannot add component to graph') - assert comp_ptr - return bt2.component._create_component_from_ptr(comp_ptr, cc_type) - - def connect_ports(self, upstream_port, downstream_port): - utils._check_type(upstream_port, bt2.port._OutputPort) - utils._check_type(downstream_port, bt2.port._InputPort) - status, conn_ptr = native_bt.graph_connect_ports(self._ptr, - upstream_port._ptr, - downstream_port._ptr) - self._handle_status(status, 'cannot connect component ports within graph') - assert(conn_ptr) - return bt2.connection._Connection._create_from_ptr(conn_ptr) - - def add_port_added_listener(self, listener): - if not callable(listener): - raise TypeError("'listener' parameter is not callable") - - fn = native_bt.py3_graph_add_port_added_listener - listener_from_native = functools.partial(_graph_port_added_listener_from_native, - listener) - - listener_ids = fn(self._ptr, listener_from_native) - if listener_ids is None: - utils._raise_bt2_error('cannot add listener to graph object') - return bt2._ListenerHandle(listener_ids, self) - - def add_ports_connected_listener(self, listener): - if not callable(listener): - raise TypeError("'listener' parameter is not callable") - - fn = native_bt.py3_graph_add_ports_connected_listener - listener_from_native = functools.partial(_graph_ports_connected_listener_from_native, - listener) - - listener_ids = fn(self._ptr, listener_from_native) - if listener_ids is None: - utils._raise_bt2_error('cannot add listener to graph object') - return bt2._ListenerHandle(listener_ids, self) - - def run(self): - status = native_bt.graph_run(self._ptr) - - if status == native_bt.GRAPH_STATUS_END: - return - - self._handle_status(status, 'graph object stopped running because of an unexpected error') - - def cancel(self): - status = native_bt.graph_cancel(self._ptr) - self._handle_status(status, 'cannot cancel graph object') - - @property - def is_canceled(self): - is_canceled = native_bt.graph_is_canceled(self._ptr) - assert(is_canceled >= 0) - return is_canceled > 0 - - def create_output_port_message_iterator(self, output_port): - utils._check_type(output_port, bt2.port._OutputPort) - msg_iter_ptr = native_bt.port_output_message_iterator_create(self._ptr, output_port._ptr) - - if msg_iter_ptr is None: - raise bt2.CreationError('cannot create output port message iterator') - - return bt2.message_iterator._OutputPortMessageIterator(msg_iter_ptr) diff --git a/bindings/python/bt2/bt2/logging.c b/bindings/python/bt2/bt2/logging.c deleted file mode 100644 index 698baf9f..00000000 --- a/bindings/python/bt2/bt2/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_python_bindings_bt2_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_python_bindings_bt2_log_level, - "BABELTRACE_PYTHON_BT2_LOG_LEVEL"); diff --git a/bindings/python/bt2/bt2/logging.h b/bindings/python/bt2/bt2/logging.h deleted file mode 100644 index 21b58503..00000000 --- a/bindings/python/bt2/bt2/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H -#define BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_python_bindings_bt2_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_python_bindings_bt2_log_level); - -#endif /* BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H */ diff --git a/bindings/python/bt2/bt2/logging.py b/bindings/python/bt2/bt2/logging.py deleted file mode 100644 index 51d898cc..00000000 --- a/bindings/python/bt2/bt2/logging.py +++ /dev/null @@ -1,59 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2 - - -class LoggingLevel: - VERBOSE = native_bt.LOGGING_LEVEL_VERBOSE - DEBUG = native_bt.LOGGING_LEVEL_DEBUG - INFO = native_bt.LOGGING_LEVEL_INFO - WARN = native_bt.LOGGING_LEVEL_WARN - ERROR = native_bt.LOGGING_LEVEL_ERROR - FATAL = native_bt.LOGGING_LEVEL_FATAL - NONE = native_bt.LOGGING_LEVEL_NONE - - -def get_minimal_logging_level(): - return native_bt.logging_get_minimal_level() - - -def get_global_logging_level(): - return native_bt.logging_get_global_level() - - -def set_global_logging_level(level): - levels = ( - LoggingLevel.VERBOSE, - LoggingLevel.DEBUG, - LoggingLevel.INFO, - LoggingLevel.WARN, - LoggingLevel.ERROR, - LoggingLevel.FATAL, - LoggingLevel.NONE, - ) - - if level not in levels: - raise TypeError("'{}' is not a valid logging level".format(level)) - - return native_bt.logging_set_global_level(level) diff --git a/bindings/python/bt2/bt2/message.py b/bindings/python/bt2/bt2/message.py deleted file mode 100644 index 56b545da..00000000 --- a/bindings/python/bt2/bt2/message.py +++ /dev/null @@ -1,236 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.clock_snapshot -import bt2.packet -import bt2.stream -import bt2.event -import bt2 - - -def _create_from_ptr(ptr): - msg_type = native_bt.message_get_type(ptr) - - if msg_type not in _MESSAGE_TYPE_TO_CLS: - raise bt2.Error('unknown message type: {}'.format(msg_type)) - - return _MESSAGE_TYPE_TO_CLS[msg_type]._create_from_ptr(ptr) - - -class _Message(object._SharedObject): - _get_ref = staticmethod(native_bt.message_get_ref) - _put_ref = staticmethod(native_bt.message_put_ref) - - @staticmethod - def _check_has_default_clock_class(clock_class): - if clock_class is None: - raise bt2.NonexistentClockSnapshot('cannot get default clock snapshot: stream class has no default clock class') - - -class _MessageWithDefaultClockSnapshot: - def _get_default_clock_snapshot(self, borrow_clock_snapshot_ptr): - snapshot_ptr = borrow_clock_snapshot_ptr(self._ptr) - - return bt2.clock_snapshot._ClockSnapshot._create_from_ptr_and_get_ref( - snapshot_ptr, self._ptr, self._get_ref, self._put_ref) - - -class _EventMessage(_Message, _MessageWithDefaultClockSnapshot): - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_event_borrow_default_clock_snapshot_const) - - @property - def default_clock_snapshot(self): - self._check_has_default_clock_class(self.event.packet.stream.cls.default_clock_class) - return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) - - @property - def event(self): - event_ptr = native_bt.message_event_borrow_event(self._ptr) - assert event_ptr is not None - return bt2.event._Event._create_from_ptr_and_get_ref( - event_ptr, self._ptr, self._get_ref, self._put_ref) - - -class _PacketMessage(_Message, _MessageWithDefaultClockSnapshot): - @property - def default_clock_snapshot(self): - self._check_has_default_clock_class(self.packet.stream.cls.default_clock_class) - return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) - - @property - def packet(self): - packet_ptr = self._borrow_packet_ptr(self._ptr) - assert packet_ptr is not None - return bt2.packet._Packet._create_from_ptr_and_get_ref(packet_ptr) - - -class _PacketBeginningMessage(_PacketMessage): - _borrow_packet_ptr = staticmethod(native_bt.message_packet_beginning_borrow_packet) - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_packet_beginning_borrow_default_clock_snapshot_const) - - -class _PacketEndMessage(_PacketMessage): - _borrow_packet_ptr = staticmethod(native_bt.message_packet_end_borrow_packet) - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_packet_end_borrow_default_clock_snapshot_const) - - -class _StreamMessage(_Message): - @property - def stream(self): - stream_ptr = self._borrow_stream_ptr(self._ptr) - assert stream_ptr - return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) - - -class _StreamBeginningMessage(_StreamMessage): - _borrow_stream_ptr = staticmethod(native_bt.message_stream_beginning_borrow_stream) - - -class _StreamEndMessage(_StreamMessage): - _borrow_stream_ptr = staticmethod(native_bt.message_stream_end_borrow_stream) - - -class _StreamActivityMessage(_Message): - @property - def default_clock_snapshot(self): - self._check_has_default_clock_class(self.stream.cls.default_clock_class) - status, snapshot_ptr = self._borrow_default_clock_snapshot_ptr(self._ptr) - - if status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: - snapshot_type = bt2.clock_snapshot._ClockSnapshot - elif status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: - snapshot_type = bt2.clock_snapshot._UnknownClockSnapshot - elif status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: - snapshot_type = bt2.clock_snapshot._InfiniteClockSnapshot - else: - raise bt2.Error('cannot borrow default clock snapshot from message') - - assert snapshot_ptr is not None - - return snapshot_type._create_from_ptr_and_get_ref( - snapshot_ptr, self._ptr, self._get_ref, self._put_ref) - - def _default_clock_snapshot(self, value): - self._set_default_clock_snapshot_ptr(self._ptr, value) - - _default_clock_snapshot = property(fset=_default_clock_snapshot) - - @property - def stream(self): - stream_ptr = self._borrow_stream_ptr(self._ptr) - assert stream_ptr - return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) - - -class _StreamActivityBeginningMessage(_StreamActivityMessage): - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_beginning_borrow_default_clock_snapshot_const) - _set_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_beginning_set_default_clock_snapshot) - _borrow_stream_ptr = staticmethod(native_bt.message_stream_activity_beginning_borrow_stream) - - -class _StreamActivityEndMessage(_StreamActivityMessage): - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_end_borrow_default_clock_snapshot_const) - _set_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_end_set_default_clock_snapshot) - _borrow_stream_ptr = staticmethod(native_bt.message_stream_activity_end_borrow_stream) - - -class _MessageIteratorInactivityMessage(_Message, _MessageWithDefaultClockSnapshot): - _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_message_iterator_inactivity_borrow_default_clock_snapshot_const) - - @property - def default_clock_snapshot(self): - # This kind of message always has a default clock class: no - # need to call self._check_has_default_clock_class() here. - return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) - - -class _DiscardedMessage(_Message, _MessageWithDefaultClockSnapshot): - @property - def stream(self): - stream_ptr = self._borrow_stream_ptr(self._ptr) - assert stream_ptr - return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) - - @property - def count(self): - avail, count = self._get_count(self._ptr) - if avail is native_bt.PROPERTY_AVAILABILITY_AVAILABLE: - return count - - def _set_count(self, count): - utils._check_uint64(count) - self._set_count(self._ptr, count) - - _count = property(fset=_set_count) - - def _check_has_default_clock_snapshots(self): - if not self._has_default_clock_snapshots: - raise bt2.NonexistentClockSnapshot('cannot get default clock snapshot: such a message has no clock snapshots for this stream class') - - @property - def beginning_default_clock_snapshot(self): - self._check_has_default_clock_snapshots() - return self._get_default_clock_snapshot(self._borrow_beginning_clock_snapshot_ptr) - - @property - def end_default_clock_snapshot(self): - self._check_has_default_clock_snapshots() - return self._get_default_clock_snapshot(self._borrow_end_clock_snapshot_ptr) - - -class _DiscardedEventsMessage(_DiscardedMessage): - _borrow_stream_ptr = staticmethod(native_bt.message_discarded_events_borrow_stream_const) - _get_count = staticmethod(native_bt.message_discarded_events_get_count) - _set_count = staticmethod(native_bt.message_discarded_events_set_count) - _borrow_beginning_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_events_borrow_beginning_default_clock_snapshot_const) - _borrow_end_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_events_borrow_end_default_clock_snapshot_const) - - @property - def _has_default_clock_snapshots(self): - return self.stream.cls.discarded_events_have_default_clock_snapshots - - -class _DiscardedPacketsMessage(_DiscardedMessage): - _borrow_stream_ptr = staticmethod(native_bt.message_discarded_packets_borrow_stream_const) - _get_count = staticmethod(native_bt.message_discarded_packets_get_count) - _set_count = staticmethod(native_bt.message_discarded_packets_set_count) - _borrow_beginning_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_packets_borrow_beginning_default_clock_snapshot_const) - _borrow_end_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_packets_borrow_end_default_clock_snapshot_const) - - @property - def _has_default_clock_snapshots(self): - return self.stream.cls.discarded_packets_have_default_clock_snapshots - - -_MESSAGE_TYPE_TO_CLS = { - native_bt.MESSAGE_TYPE_EVENT: _EventMessage, - native_bt.MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: _MessageIteratorInactivityMessage, - native_bt.MESSAGE_TYPE_STREAM_BEGINNING: _StreamBeginningMessage, - native_bt.MESSAGE_TYPE_STREAM_END: _StreamEndMessage, - native_bt.MESSAGE_TYPE_PACKET_BEGINNING: _PacketBeginningMessage, - native_bt.MESSAGE_TYPE_PACKET_END: _PacketEndMessage, - native_bt.MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: _StreamActivityBeginningMessage, - native_bt.MESSAGE_TYPE_STREAM_ACTIVITY_END: _StreamActivityEndMessage, - native_bt.MESSAGE_TYPE_DISCARDED_EVENTS: _DiscardedEventsMessage, - native_bt.MESSAGE_TYPE_DISCARDED_PACKETS: _DiscardedPacketsMessage, -} diff --git a/bindings/python/bt2/bt2/message_iterator.py b/bindings/python/bt2/bt2/message_iterator.py deleted file mode 100644 index 7f996b00..00000000 --- a/bindings/python/bt2/bt2/message_iterator.py +++ /dev/null @@ -1,337 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.message -import collections.abc -import bt2.component -import bt2 - - -class _MessageIterator(collections.abc.Iterator): - def _handle_status(self, status, gen_error_msg): - if status == native_bt.MESSAGE_ITERATOR_STATUS_AGAIN: - raise bt2.TryAgain - elif status == native_bt.MESSAGE_ITERATOR_STATUS_END: - raise bt2.Stop - elif status < 0: - raise bt2.Error(gen_error_msg) - - def __next__(self): - raise NotImplementedError - - -class _GenericMessageIterator(object._SharedObject, _MessageIterator): - def __init__(self, ptr): - self._current_msgs = [] - self._at = 0 - super().__init__(ptr) - - def __next__(self): - if len(self._current_msgs) == self._at: - status, msgs = self._get_msg_range(self._ptr) - self._handle_status(status, - 'unexpected error: cannot advance the message iterator') - self._current_msgs = msgs - self._at = 0 - - msg_ptr = self._current_msgs[self._at] - self._at += 1 - - return bt2.message._create_from_ptr(msg_ptr) - - -# This is created when a component wants to iterate on one of its input ports. -class _UserComponentInputPortMessageIterator(_GenericMessageIterator): - _get_msg_range = staticmethod(native_bt.py3_self_component_port_input_get_msg_range) - _get_ref = staticmethod(native_bt.self_component_port_input_message_iterator_get_ref) - _put_ref = staticmethod(native_bt.self_component_port_input_message_iterator_put_ref) - - -# This is created when the user wants to iterate on a component's output port, -# from outside the graph. -class _OutputPortMessageIterator(_GenericMessageIterator): - _get_msg_range = staticmethod(native_bt.py3_port_output_get_msg_range) - _get_ref = staticmethod(native_bt.port_output_message_iterator_get_ref) - _put_ref = staticmethod(native_bt.port_output_message_iterator_put_ref) - - -# This is extended by the user to implement component classes in Python. It -# is created for a given output port when an input port message iterator is -# created on the input port on the other side of the connection. It is also -# created when an output port message iterator is created on this output port. -# -# Its purpose is to feed the messages that should go out through this output -# port. -class _UserMessageIterator(_MessageIterator): - def __new__(cls, ptr): - # User iterator objects are always created by the native side, - # that is, never instantiated directly by Python code. - # - # The native code calls this, then manually calls - # self.__init__() without the `ptr` argument. The user has - # access to self.component during this call, thanks to this - # self._ptr argument being set. - # - # self._ptr is NOT owned by this object here, so there's nothing - # to do in __del__(). - self = super().__new__(cls) - self._ptr = ptr - return self - - def _init_from_native(self, self_output_port_ptr): - self_output_port = bt2.port._create_self_from_ptr_and_get_ref( - self_output_port_ptr, native_bt.PORT_TYPE_OUTPUT) - self.__init__(self_output_port) - - def __init__(self, output_port): - pass - - @property - def _component(self): - return native_bt.py3_get_user_component_from_user_msg_iter(self._ptr) - - @property - def addr(self): - return int(self._ptr) - - def _finalize(self): - pass - - def __next__(self): - raise bt2.Stop - - def _next_from_native(self): - # this can raise anything: it's catched by the native part - try: - msg = next(self) - except StopIteration: - raise bt2.Stop - except: - raise - - utils._check_type(msg, bt2.message._Message) - - # Release the reference to the native part. - ptr = msg._release() - return int(ptr) - - # Validate that the presence or lack of presence of a - # `default_clock_snapshot` value is valid in the context of `stream_class`. - @staticmethod - def _validate_default_clock_snapshot(stream_class, default_clock_snapshot): - stream_class_has_default_clock_class = stream_class.default_clock_class is not None - - if stream_class_has_default_clock_class and default_clock_snapshot is None: - raise bt2.Error( - 'stream class has a default clock class, default_clock_snapshot should not be None') - - if not stream_class_has_default_clock_class and default_clock_snapshot is not None: - raise bt2.Error( - 'stream class has no default clock class, default_clock_snapshot should be None') - - def _create_event_message(self, event_class, packet, default_clock_snapshot=None): - utils._check_type(event_class, bt2.event_class._EventClass) - utils._check_type(packet, bt2.packet._Packet) - self._validate_default_clock_snapshot(packet.stream.cls, default_clock_snapshot) - - if default_clock_snapshot is not None: - utils._check_uint64(default_clock_snapshot) - ptr = native_bt.message_event_create_with_default_clock_snapshot( - self._ptr, event_class._ptr, packet._ptr, default_clock_snapshot) - else: - ptr = native_bt.message_event_create( - self._ptr, event_class._ptr, packet._ptr) - - if ptr is None: - raise bt2.CreationError('cannot create event message object') - - return bt2.message._EventMessage(ptr) - - def _create_message_iterator_inactivity_message(self, clock_class, clock_snapshot): - utils._check_type(clock_class, bt2.clock_class._ClockClass) - ptr = native_bt.message_message_iterator_inactivity_create( - self._ptr, clock_class._ptr, clock_snapshot) - - if ptr is None: - raise bt2.CreationError('cannot create inactivity message object') - - return bt2.message._MessageIteratorInactivityMessage(ptr) - - def _create_stream_beginning_message(self, stream): - utils._check_type(stream, bt2.stream._Stream) - - ptr = native_bt.message_stream_beginning_create(self._ptr, stream._ptr) - if ptr is None: - raise bt2.CreationError('cannot create stream beginning message object') - - return bt2.message._StreamBeginningMessage(ptr) - - def _create_stream_activity_beginning_message(self, stream, default_clock_snapshot=None): - utils._check_type(stream, bt2.stream._Stream) - self._validate_default_clock_snapshot(stream.cls, default_clock_snapshot) - - ptr = native_bt.message_stream_activity_beginning_create(self._ptr, stream._ptr) - - if ptr is None: - raise bt2.CreationError( - 'cannot create stream activity beginning message object') - - msg = bt2.message._StreamActivityBeginningMessage(ptr) - - if default_clock_snapshot is not None: - msg._default_clock_snapshot = default_clock_snapshot - - return msg - - def _create_stream_activity_end_message(self, stream, default_clock_snapshot=None): - utils._check_type(stream, bt2.stream._Stream) - self._validate_default_clock_snapshot(stream.cls, default_clock_snapshot) - - ptr = native_bt.message_stream_activity_end_create(self._ptr, stream._ptr) - - if ptr is None: - raise bt2.CreationError( - 'cannot create stream activity end message object') - - msg = bt2.message._StreamActivityEndMessage(ptr) - - if default_clock_snapshot is not None: - msg._default_clock_snapshot = default_clock_snapshot - - return msg - - def _create_stream_end_message(self, stream): - utils._check_type(stream, bt2.stream._Stream) - - ptr = native_bt.message_stream_end_create(self._ptr, stream._ptr) - if ptr is None: - raise bt2.CreationError('cannot create stream end message object') - - return bt2.message._StreamEndMessage(ptr) - - def _create_packet_beginning_message(self, packet, default_clock_snapshot=None): - utils._check_type(packet, bt2.packet._Packet) - - if packet.stream.cls.packets_have_beginning_default_clock_snapshot: - if default_clock_snapshot is None: - raise ValueError("packet beginning messages in this stream must have a default clock snapshots") - - utils._check_uint64(default_clock_snapshot) - ptr = native_bt.message_packet_beginning_create_with_default_clock_snapshot( - self._ptr, packet._ptr, default_clock_snapshot) - else: - if default_clock_snapshot is not None: - raise ValueError("packet beginning messages in this stream must not have a default clock snapshots") - - ptr = native_bt.message_packet_beginning_create(self._ptr, packet._ptr) - - if ptr is None: - raise bt2.CreationError('cannot create packet beginning message object') - - return bt2.message._PacketBeginningMessage(ptr) - - def _create_packet_end_message(self, packet, default_clock_snapshot=None): - utils._check_type(packet, bt2.packet._Packet) - - if packet.stream.cls.packets_have_end_default_clock_snapshot: - if default_clock_snapshot is None: - raise ValueError("packet end messages in this stream must have a default clock snapshots") - - utils._check_uint64(default_clock_snapshot) - ptr = native_bt.message_packet_end_create_with_default_clock_snapshot( - self._ptr, packet._ptr, default_clock_snapshot) - else: - if default_clock_snapshot is not None: - raise ValueError("packet end messages in this stream must not have a default clock snapshots") - - ptr = native_bt.message_packet_end_create(self._ptr, packet._ptr) - - if ptr is None: - raise bt2.CreationError('cannot create packet end message object') - - return bt2.message._PacketEndMessage(ptr) - - def _create_discarded_events_message(self, stream, count=None, - beg_clock_snapshot=None, - end_clock_snapshot=None): - utils._check_type(stream, bt2.stream._Stream) - - if not stream.cls.supports_discarded_events: - raise ValueError('stream class does not support discarded events') - - if stream.cls.discarded_events_have_default_clock_snapshots: - if beg_clock_snapshot is None or end_clock_snapshot is None: - raise ValueError('discarded events have default clock snapshots for this stream class') - - utils._check_uint64(beg_clock_snapshot) - utils._check_uint64(end_clock_snapshot) - ptr = native_bt.message_discarded_events_create_with_default_clock_snapshots( - self._ptr, stream._ptr, beg_clock_snapshot, end_clock_snapshot) - else: - if beg_clock_snapshot is not None or end_clock_snapshot is not None: - raise ValueError('discarded events have no default clock snapshots for this stream class') - - ptr = native_bt.message_discarded_events_create( - self._ptr, stream._ptr) - - if ptr is None: - raise bt2.CreationError('cannot discarded events message object') - - msg = bt2.message._DiscardedEventsMessage(ptr) - - if count is not None: - msg._count = count - - return msg - - def _create_discarded_packets_message(self, stream, count=None, beg_clock_snapshot=None, end_clock_snapshot=None): - utils._check_type(stream, bt2.stream._Stream) - - if not stream.cls.supports_discarded_packets: - raise ValueError('stream class does not support discarded packets') - - if stream.cls.discarded_packets_have_default_clock_snapshots: - if beg_clock_snapshot is None or end_clock_snapshot is None: - raise ValueError('discarded packets have default clock snapshots for this stream class') - - utils._check_uint64(beg_clock_snapshot) - utils._check_uint64(end_clock_snapshot) - ptr = native_bt.message_discarded_packets_create_with_default_clock_snapshots( - self._ptr, stream._ptr, beg_clock_snapshot, end_clock_snapshot) - else: - if beg_clock_snapshot is not None or end_clock_snapshot is not None: - raise ValueError('discarded packets have no default clock snapshots for this stream class') - - ptr = native_bt.message_discarded_packets_create( - self._ptr, stream._ptr) - - if ptr is None: - raise bt2.CreationError('cannot discarded packets message object') - - msg = bt2.message._DiscardedPacketsMessage(ptr) - - if count is not None: - msg._count = count - - return msg - diff --git a/bindings/python/bt2/bt2/native_bt.i b/bindings/python/bt2/bt2/native_bt.i deleted file mode 100644 index 8a43edf0..00000000 --- a/bindings/python/bt2/bt2/native_bt.i +++ /dev/null @@ -1,213 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef SWIGPYTHON -# error Unsupported output language -#endif - -%module native_bt - -%{ -#define BT_LOG_TAG "PY-NATIVE" -#include "logging.h" - -#include -#include -#include - -typedef const uint8_t *bt_uuid; -%} - -typedef int bt_bool; - -/* For uint*_t/int*_t */ -%include "stdint.i" - -/* - * Remove `bt_` and `BT_` prefixes from function names, global variables and - * enumeration items - */ -%rename("%(strip:[bt_])s", %$isfunction) ""; -%rename("%(strip:[bt_])s", %$isvariable) ""; -%rename("%(strip:[BT_])s", %$isenumitem) ""; - -/* - * Output argument typemap for string output (always appends) - * - * We initialize the output parameter `temp_value` to an invalid but non-zero - * pointer value. This is to make sure we don't rely on its initial value in - * the epilogue (where we call SWIG_Python_str_FromChar). When they fail, - * functions on which we apply this typemap don't guarantee that the value of - * `temp_value` will be unchanged or valid. - */ -%typemap(in, numinputs=0) (const char **OUT) (char *temp_value = (void *) 1) { - $1 = &temp_value; -} - -%typemap(argout) (const char **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, SWIG_Python_str_FromChar(*$1)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for value output (always appends) */ -%typemap(in, numinputs=0) (bt_value **OUT) (struct bt_value *temp_value = NULL) { - $1 = &temp_value; -} - -%typemap(argout) (bt_value **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_value, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for initialized uint64_t output parameter (always appends) */ -%typemap(in, numinputs=0) (uint64_t *OUT) (uint64_t temp) { - $1 = &temp; -} - -%typemap(argout) uint64_t *OUT { - $result = SWIG_Python_AppendOutput(resultobj, - SWIG_From_unsigned_SS_long_SS_long((*$1))); -} - -/* Output argument typemap for initialized int64_t output parameter (always appends) */ -%typemap(in, numinputs=0) (int64_t *OUT) (int64_t temp) { - $1 = &temp; -} - -%typemap(argout) (int64_t *OUT) { - $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_long_SS_long((*$1))); -} - -/* Output argument typemap for initialized unsigned int output parameter (always appends) */ -%typemap(in, numinputs=0) (unsigned int *OUT) (unsigned int temp) { - $1 = &temp; -} - -%typemap(argout) (unsigned int *OUT) { - $result = SWIG_Python_AppendOutput(resultobj, - SWIG_From_unsigned_SS_long_SS_long((uint64_t) (*$1))); -} -/* Output argument typemap for initialized double output parameter (always appends) */ -%typemap(in, numinputs=0) (double *OUT) (double temp) { - $1 = &temp; -} - -%typemap(argout) (double *OUT) { - $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*$1))); -} - -/* Input argument typemap for UUID bytes */ -%typemap(in) bt_uuid { - $1 = (unsigned char *) PyBytes_AsString($input); -} - -/* Output argument typemap for UUID bytes */ -%typemap(out) bt_uuid { - if (!$1) { - Py_INCREF(Py_None); - $result = Py_None; - } else { - $result = PyBytes_FromStringAndSize((const char *) $1, 16); - } -} - -/* Input argument typemap for bt_bool */ -%typemap(in) bt_bool { - $1 = PyObject_IsTrue($input); -} - -/* Output argument typemap for bt_bool */ -%typemap(out) bt_bool { - if ($1 > 0) { - $result = Py_True; - } else { - $result = Py_False; - } - Py_INCREF($result); - return $result; -} - -/* - * Input and output argument typemaps for raw Python objects (direct). - * - * Those typemaps honor the convention of Python C function calls with - * respect to reference counting: parameters are passed as borrowed - * references, and objects are returned as new references. The wrapped - * C function must ensure that the return value is always a new - * reference, and never steal parameter references. - */ -%typemap(in) PyObject * { - $1 = $input; -} - -%typemap(out) PyObject * { - $result = $1; -} - -/* From property.h */ - -typedef enum bt_property_availability { - BT_PROPERTY_AVAILABILITY_AVAILABLE, - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, -} bt_property_availability; - -/* Per-module interface files */ -%include "native_bt_clock_class.i" -%include "native_bt_clock_snapshot.i" -%include "native_bt_component.i" -%include "native_bt_component_class.i" -%include "native_bt_connection.i" -%include "native_bt_event.i" -%include "native_bt_event_class.i" -%include "native_bt_field.i" -%include "native_bt_field_class.i" -%include "native_bt_field_path.i" -%include "native_bt_graph.i" -%include "native_bt_logging.i" -%include "native_bt_message.i" -%include "native_bt_notifier.i" -%include "native_bt_packet.i" -%include "native_bt_plugin.i" -%include "native_bt_port.i" -%include "native_bt_query_exec.i" -%include "native_bt_stream.i" -%include "native_bt_stream_class.i" -%include "native_bt_trace.i" -%include "native_bt_trace_class.i" -%include "native_bt_value.i" -%include "native_bt_version.i" diff --git a/bindings/python/bt2/bt2/native_bt_clock_class.i b/bindings/python/bt2/bt2/native_bt_clock_class.i deleted file mode 100644 index bf8ea437..00000000 --- a/bindings/python/bt2/bt2/native_bt_clock_class.i +++ /dev/null @@ -1,85 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From clock-class-const.h */ - -typedef enum bt_clock_class_status { - BT_CLOCK_CLASS_STATUS_OK = 0, - BT_CLOCK_CLASS_STATUS_NOMEM = -12, - BT_CLOCK_CLASS_STATUS_OVERFLOW = -75, -} bt_clock_class_status; - -extern const char *bt_clock_class_get_name( - const bt_clock_class *clock_class); - -extern const char *bt_clock_class_get_description( - const bt_clock_class *clock_class); - -extern uint64_t bt_clock_class_get_frequency( - const bt_clock_class *clock_class); - -extern uint64_t bt_clock_class_get_precision( - const bt_clock_class *clock_class); - -extern void bt_clock_class_get_offset(const bt_clock_class *clock_class, - int64_t *OUT, uint64_t *OUT); - -extern bt_bool bt_clock_class_origin_is_unix_epoch( - const bt_clock_class *clock_class); - -extern bt_uuid bt_clock_class_get_uuid( - const bt_clock_class *clock_class); - -extern bt_clock_class_status bt_clock_class_cycles_to_ns_from_origin( - const bt_clock_class *clock_class, - uint64_t cycles, int64_t *OUT); - -extern void bt_clock_class_get_ref(const bt_clock_class *clock_class); - -extern void bt_clock_class_put_ref(const bt_clock_class *clock_class); - -/* From clock-class.h */ - -extern bt_clock_class *bt_clock_class_create(bt_self_component *self_comp); - -extern bt_clock_class_status bt_clock_class_set_name( - bt_clock_class *clock_class, const char *name); - -extern bt_clock_class_status bt_clock_class_set_description( - bt_clock_class *clock_class, const char *description); - -extern void bt_clock_class_set_frequency(bt_clock_class *clock_class, - uint64_t freq); - -extern void bt_clock_class_set_precision(bt_clock_class *clock_class, - uint64_t precision); - -extern void bt_clock_class_set_offset(bt_clock_class *clock_class, - int64_t seconds, uint64_t cycles); - -extern void bt_clock_class_set_origin_is_unix_epoch(bt_clock_class *clock_class, - bt_bool origin_is_unix_epoch); - -extern void bt_clock_class_set_uuid(bt_clock_class *clock_class, - bt_uuid uuid); diff --git a/bindings/python/bt2/bt2/native_bt_clock_snapshot.i b/bindings/python/bt2/bt2/native_bt_clock_snapshot.i deleted file mode 100644 index a2ca8e86..00000000 --- a/bindings/python/bt2/bt2/native_bt_clock_snapshot.i +++ /dev/null @@ -1,40 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From clock-snapshot-const.h */ - -typedef enum bt_clock_snapshot_status { - BT_CLOCK_SNAPSHOT_STATUS_OK = 0, - BT_CLOCK_SNAPSHOT_STATUS_OVERFLOW = -75, -} bt_clock_snapshot_status; - -extern const bt_clock_class *bt_clock_snapshot_borrow_clock_class_const( - const bt_clock_snapshot *clock_snapshot); - -extern uint64_t bt_clock_snapshot_get_value( - const bt_clock_snapshot *clock_snapshot); - -extern bt_clock_snapshot_status bt_clock_snapshot_get_ns_from_origin( - const bt_clock_snapshot *clock_snapshot, - int64_t *OUT); diff --git a/bindings/python/bt2/bt2/native_bt_component.i b/bindings/python/bt2/bt2/native_bt_component.i deleted file mode 100644 index f484263e..00000000 --- a/bindings/python/bt2/bt2/native_bt_component.i +++ /dev/null @@ -1,305 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* Output argument typemap for self port output (always appends) */ -%typemap(in, numinputs=0) - (bt_self_component_port_input **OUT) - (bt_self_component_port_input *temp_self_port = NULL) { - $1 = &temp_self_port; -} - -%typemap(argout) bt_self_component_port_input **OUT { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_self_component_port_input, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for self port output (always appends) */ -%typemap(in, numinputs=0) - (bt_self_component_port_output **OUT) - (bt_self_component_port_output *temp_self_port = NULL) { - $1 = &temp_self_port; -} - -%typemap(argout) (bt_self_component_port_output **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_self_component_port_output, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Typemaps used for user data attached to self component ports. */ - -/* - * The user data Python object is kept as the user data of the port, we pass - * the PyObject pointer directly to the port creation function. - */ -%typemap(in) void *PY_SELF_PORT_USER_DATA { - $1 = $input; -} - -/* - * The port, if created successfully, now owns a reference to the Python object, - * we reflect that here. - */ -%typemap(argout) void *PY_SELF_PORT_USER_DATA { - if (PyLong_AsLong($result) == BT_SELF_COMPONENT_STATUS_OK) { - Py_INCREF($1); - } -} - -/* From component-const.h */ - -extern const char *bt_component_get_name(const bt_component *component); - -extern const bt_component_class *bt_component_borrow_class_const( - const bt_component *component); - -extern bt_component_class_type bt_component_get_class_type( - const bt_component *component); - -bt_bool bt_component_is_source(const bt_component *component); - -bt_bool bt_component_is_filter(const bt_component *component); - -bt_bool bt_component_is_sink(const bt_component *component); - -extern bt_bool bt_component_graph_is_canceled( - const bt_component *component); - -extern void bt_component_get_ref(const bt_component *component); - -extern void bt_component_put_ref(const bt_component *component); - -/* From component-source-const.h */ - -const bt_component *bt_component_source_as_component_const( - const bt_component_source *component); - -extern const bt_component_class_source * -bt_component_source_borrow_class_const( - const bt_component_source *component); - -extern uint64_t bt_component_source_get_output_port_count( - const bt_component_source *component); - -extern const bt_port_output * -bt_component_source_borrow_output_port_by_name_const( - const bt_component_source *component, const char *name); - -extern const bt_port_output * -bt_component_source_borrow_output_port_by_index_const( - const bt_component_source *component, uint64_t index); - -extern void bt_component_source_get_ref( - const bt_component_source *component_source); - -extern void bt_component_source_put_ref( - const bt_component_source *component_source); - -/* From component-filter-const.h */ - -const bt_component *bt_component_filter_as_component_const( - const bt_component_filter *component); - -extern const bt_component_class_filter * -bt_component_filter_borrow_class_const( - const bt_component_filter *component); - -extern uint64_t bt_component_filter_get_input_port_count( - const bt_component_filter *component); - -extern const bt_port_input * -bt_component_filter_borrow_input_port_by_name_const( - const bt_component_filter *component, const char *name); - -extern const bt_port_input * -bt_component_filter_borrow_input_port_by_index_const( - const bt_component_filter *component, uint64_t index); - -extern uint64_t bt_component_filter_get_output_port_count( - const bt_component_filter *component); - -extern const bt_port_output * -bt_component_filter_borrow_output_port_by_name_const( - const bt_component_filter *component, const char *name); - -extern const bt_port_output * -bt_component_filter_borrow_output_port_by_index_const( - const bt_component_filter *component, uint64_t index); - -extern void bt_component_filter_get_ref( - const bt_component_filter *component_filter); - -extern void bt_component_filter_put_ref( - const bt_component_filter *component_filter); - -/* From component-sink-const.h */ - -const bt_component *bt_component_sink_as_component_const( - const bt_component_sink *component); - -extern const bt_component_class_sink * -bt_component_sink_borrow_class_const( - const bt_component_sink *component); - -extern uint64_t bt_component_sink_get_input_port_count( - const bt_component_sink *component); - -extern const bt_port_input * -bt_component_sink_borrow_input_port_by_name_const( - const bt_component_sink *component, const char *name); - -extern const bt_port_input * -bt_component_sink_borrow_input_port_by_index_const( - const bt_component_sink *component, uint64_t index); - -extern void bt_component_sink_get_ref( - const bt_component_sink *component_sink); - -extern void bt_component_sink_put_ref( - const bt_component_sink *component_sink); - -/* From self-component.h */ - -typedef enum bt_self_component_status { - BT_SELF_COMPONENT_STATUS_OK = 0, - BT_SELF_COMPONENT_STATUS_END = 1, - BT_SELF_COMPONENT_STATUS_AGAIN = 11, - BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION = 111, - BT_SELF_COMPONENT_STATUS_ERROR = -1, - BT_SELF_COMPONENT_STATUS_NOMEM = -12, -} bt_self_component_status; - -const bt_component *bt_self_component_as_component( - bt_self_component *self_component); - -extern void *bt_self_component_get_data( - const bt_self_component *self_component); - -extern void bt_self_component_set_data( - bt_self_component *self_component, void *data); - -/* From self-component-source.h */ - -bt_self_component *bt_self_component_source_as_self_component( - bt_self_component_source *self_comp_source); - -const bt_component_source * -bt_self_component_source_as_component_source( - bt_self_component_source *self_comp_source); - -extern bt_self_component_port_output * -bt_self_component_source_borrow_output_port_by_name( - bt_self_component_source *self_component, - const char *name); - -extern bt_self_component_port_output * -bt_self_component_source_borrow_output_port_by_index( - bt_self_component_source *self_component, - uint64_t index); - -extern bt_self_component_status -bt_self_component_source_add_output_port( - bt_self_component_source *self_component, - const char *name, void *PY_SELF_PORT_USER_DATA, - bt_self_component_port_output **OUT); - -/* From self-component-filter.h */ - -bt_self_component *bt_self_component_filter_as_self_component( - bt_self_component_filter *self_comp_filter); - -const bt_component_filter * -bt_self_component_filter_as_component_filter( - bt_self_component_filter *self_comp_filter); - -extern bt_self_component_port_output * -bt_self_component_filter_borrow_output_port_by_name( - bt_self_component_filter *self_component, - const char *name); - -extern bt_self_component_port_output * -bt_self_component_filter_borrow_output_port_by_index( - bt_self_component_filter *self_component, - uint64_t index); - -extern bt_self_component_status -bt_self_component_filter_add_output_port( - bt_self_component_filter *self_component, - const char *name, void *PY_SELF_PORT_USER_DATA, - bt_self_component_port_output **OUT); - -extern bt_self_component_port_input * -bt_self_component_filter_borrow_input_port_by_name( - bt_self_component_filter *self_component, - const char *name); - -extern bt_self_component_port_input * -bt_self_component_filter_borrow_input_port_by_index( - bt_self_component_filter *self_component, - uint64_t index); - -extern bt_self_component_status -bt_self_component_filter_add_input_port( - bt_self_component_filter *self_component, - const char *name, void *PY_SELF_PORT_USER_DATA, - bt_self_component_port_input **OUT); - -/* From self-component-sink.h */ - -bt_self_component *bt_self_component_sink_as_self_component( - bt_self_component_sink *self_comp_sink); - -const bt_component_sink * -bt_self_component_sink_as_component_sink( - bt_self_component_sink *self_comp_sink); - -extern bt_self_component_port_input * -bt_self_component_sink_borrow_input_port_by_name( - bt_self_component_sink *self_component, - const char *name); - -extern bt_self_component_port_input * -bt_self_component_sink_borrow_input_port_by_index( - bt_self_component_sink *self_component, uint64_t index); - -extern bt_self_component_status -bt_self_component_sink_add_input_port( - bt_self_component_sink *self_component, - const char *name, void *PY_SELF_PORT_USER_DATA, - bt_self_component_port_input **OUT); diff --git a/bindings/python/bt2/bt2/native_bt_component_class.i b/bindings/python/bt2/bt2/native_bt_component_class.i deleted file mode 100644 index 1dfa0110..00000000 --- a/bindings/python/bt2/bt2/native_bt_component_class.i +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From component-class-const.h */ - -typedef enum bt_component_class_status { - BT_COMPONENT_CLASS_STATUS_OK = 0, - BT_COMPONENT_CLASS_STATUS_NOMEM = -12, -} bt_component_class_status; - -typedef enum bt_component_class_type { - BT_COMPONENT_CLASS_TYPE_SOURCE = 0, - BT_COMPONENT_CLASS_TYPE_FILTER = 1, - BT_COMPONENT_CLASS_TYPE_SINK = 2, -} bt_component_class_type; - -extern const char *bt_component_class_get_name( - const bt_component_class *component_class); - -extern const char *bt_component_class_get_description( - const bt_component_class *component_class); - -extern const char *bt_component_class_get_help( - const bt_component_class *component_class); - -extern bt_component_class_type bt_component_class_get_type( - const bt_component_class *component_class); - -bt_bool bt_component_class_is_source( - const bt_component_class *component_class); - -bt_bool bt_component_class_is_filter( - const bt_component_class *component_class); - -bt_bool bt_component_class_is_sink( - const bt_component_class *component_class); - -extern void bt_component_class_get_ref( - const bt_component_class *component_class); - -extern void bt_component_class_put_ref( - const bt_component_class *component_class); - -/* From component-class-source-const.h */ - -const bt_component_class * -bt_component_class_source_as_component_class_const( - const bt_component_class_source *comp_cls_source); - -extern void bt_component_class_source_get_ref( - const bt_component_class_source *component_class_source); - -extern void bt_component_class_source_put_ref( - const bt_component_class_source *component_class_source); - -/* From component-class-source.h */ - -typedef bt_self_component_status -(*bt_component_class_source_init_method)( - bt_self_component_source *self_component, - const bt_value *params, void *init_method_data); - -typedef void (*bt_component_class_source_finalize_method)( - bt_self_component_source *self_component); - -typedef bt_self_message_iterator_status -(*bt_component_class_source_message_iterator_init_method)( - bt_self_message_iterator *message_iterator, - bt_self_component_source *self_component, - bt_self_component_port_output *port); - -typedef void -(*bt_component_class_source_message_iterator_finalize_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_self_message_iterator_status -(*bt_component_class_source_message_iterator_next_method)( - bt_self_message_iterator *message_iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -typedef bt_self_message_iterator_status -(*bt_component_class_source_message_iterator_seek_ns_from_origin_method)( - bt_self_message_iterator *message_iterator, - int64_t ns_from_origin); - -typedef bt_self_message_iterator_status -(*bt_component_class_source_message_iterator_seek_beginning_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_bool -(*bt_component_class_source_message_iterator_can_seek_ns_from_origin_method)( - bt_self_message_iterator *message_iterator, - int64_t ns_from_origin); - -typedef bt_bool -(*bt_component_class_source_message_iterator_can_seek_beginning_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_query_status (*bt_component_class_source_query_method)( - bt_self_component_class_source *comp_class, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result); - -typedef bt_self_component_status -(*bt_component_class_source_accept_output_port_connection_method)( - bt_self_component_source *self_component, - bt_self_component_port_output *self_port, - const bt_port_input *other_port); - -typedef bt_self_component_status -(*bt_component_class_source_output_port_connected_method)( - bt_self_component_source *self_component, - bt_self_component_port_output *self_port, - const bt_port_input *other_port); - -bt_component_class *bt_component_class_source_as_component_class( - bt_component_class_source *comp_cls_source); - -extern -bt_component_class_source *bt_component_class_source_create( - const char *name, - bt_component_class_source_message_iterator_next_method method); - -extern bt_component_class_status -bt_component_class_source_set_init_method( - bt_component_class_source *comp_class, - bt_component_class_source_init_method method); - -extern bt_component_class_status -bt_component_class_source_set_finalize_method( - bt_component_class_source *comp_class, - bt_component_class_source_finalize_method method); - -extern bt_component_class_status -bt_component_class_source_set_accept_output_port_connection_method( - bt_component_class_source *comp_class, - bt_component_class_source_accept_output_port_connection_method method); - -extern bt_component_class_status -bt_component_class_source_set_output_port_connected_method( - bt_component_class_source *comp_class, - bt_component_class_source_output_port_connected_method method); - -extern bt_component_class_status -bt_component_class_source_set_query_method( - bt_component_class_source *comp_class, - bt_component_class_source_query_method method); - -extern bt_component_class_status -bt_component_class_source_set_message_iterator_init_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_init_method method); - -extern bt_component_class_status -bt_component_class_source_set_message_iterator_finalize_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_finalize_method method); - -extern bt_component_class_status -bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_seek_ns_from_origin_method method); - -extern bt_component_class_status -bt_component_class_source_set_message_iterator_seek_beginning_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_seek_beginning_method method); - -extern bt_bool -bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_can_seek_ns_from_origin_method method); - -extern bt_bool -bt_component_class_source_set_message_iterator_can_seek_beginning_method( - bt_component_class_source *comp_class, - bt_component_class_source_message_iterator_can_seek_beginning_method method); - -/* From component-class-filter-const.h */ - -const bt_component_class * -bt_component_class_filter_as_component_class_const( - const bt_component_class_filter *comp_cls_filter); - -extern void bt_component_class_filter_get_ref( - const bt_component_class_filter *component_class_filter); - -extern void bt_component_class_filter_put_ref( - const bt_component_class_filter *component_class_filter); - -/* From component-class-filter.h */ - -typedef bt_self_component_status -(*bt_component_class_filter_init_method)( - bt_self_component_filter *self_component, - const bt_value *params, void *init_method_data); - -typedef void (*bt_component_class_filter_finalize_method)( - bt_self_component_filter *self_component); - -typedef bt_self_message_iterator_status -(*bt_component_class_filter_message_iterator_init_method)( - bt_self_message_iterator *message_iterator, - bt_self_component_filter *self_component, - bt_self_component_port_output *port); - -typedef void -(*bt_component_class_filter_message_iterator_finalize_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_self_message_iterator_status -(*bt_component_class_filter_message_iterator_next_method)( - bt_self_message_iterator *message_iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -typedef bt_self_message_iterator_status -(*bt_component_class_filter_message_iterator_seek_ns_from_origin_method)( - bt_self_message_iterator *message_iterator, - int64_t ns_from_origin); - -typedef bt_self_message_iterator_status -(*bt_component_class_filter_message_iterator_seek_beginning_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_bool -(*bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method)( - bt_self_message_iterator *message_iterator, - int64_t ns_from_origin); - -typedef bt_bool -(*bt_component_class_filter_message_iterator_can_seek_beginning_method)( - bt_self_message_iterator *message_iterator); - -typedef bt_query_status -(*bt_component_class_filter_query_method)( - bt_self_component_class_filter *comp_class, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result); - -typedef bt_self_component_status -(*bt_component_class_filter_accept_input_port_connection_method)( - bt_self_component_filter *self_component, - bt_self_component_port_input *self_port, - const bt_port_output *other_port); - -typedef bt_self_component_status -(*bt_component_class_filter_accept_output_port_connection_method)( - bt_self_component_filter *self_component, - bt_self_component_port_output *self_port, - const bt_port_input *other_port); - -typedef bt_self_component_status -(*bt_component_class_filter_input_port_connected_method)( - bt_self_component_filter *self_component, - bt_self_component_port_input *self_port, - const bt_port_output *other_port); - -typedef bt_self_component_status -(*bt_component_class_filter_output_port_connected_method)( - bt_self_component_filter *self_component, - bt_self_component_port_output *self_port, - const bt_port_input *other_port); - -bt_component_class *bt_component_class_filter_as_component_class( - bt_component_class_filter *comp_cls_filter); - -extern -bt_component_class_filter *bt_component_class_filter_create( - const char *name, - bt_component_class_filter_message_iterator_next_method method); - -extern bt_component_class_status -bt_component_class_filter_set_init_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_init_method method); - -extern bt_component_class_status -bt_component_class_filter_set_finalize_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_finalize_method method); - -extern bt_component_class_status -bt_component_class_filter_set_accept_input_port_connection_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_accept_input_port_connection_method method); - -extern bt_component_class_status -bt_component_class_filter_set_accept_output_port_connection_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_accept_output_port_connection_method method); - -extern bt_component_class_status -bt_component_class_filter_set_input_port_connected_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_input_port_connected_method method); - -extern bt_component_class_status -bt_component_class_filter_set_output_port_connected_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_output_port_connected_method method); - -extern bt_component_class_status -bt_component_class_filter_set_query_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_query_method method); - -extern bt_component_class_status -bt_component_class_filter_set_message_iterator_init_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_init_method method); - -extern bt_component_class_status -bt_component_class_filter_set_message_iterator_finalize_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_finalize_method method); - -extern bt_component_class_status -bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_seek_ns_from_origin_method method); - -extern bt_component_class_status -bt_component_class_filter_set_message_iterator_seek_beginning_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_seek_beginning_method method); - -extern bt_bool -bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method method); - -extern bt_bool -bt_component_class_filter_set_message_iterator_can_seek_beginning_method( - bt_component_class_filter *comp_class, - bt_component_class_filter_message_iterator_can_seek_beginning_method method); - -/* From component-class-sink-const.h */ - -const bt_component_class * -bt_component_class_sink_as_component_class_const( - const bt_component_class_sink *comp_cls_sink); - -extern void bt_component_class_sink_get_ref( - const bt_component_class_sink *component_class_sink); - -extern void bt_component_class_sink_put_ref( - const bt_component_class_sink *component_class_sink); - -/* From component-class-sink.h */ - -typedef bt_self_component_status (*bt_component_class_sink_init_method)( - bt_self_component_sink *self_component, - const bt_value *params, void *init_method_data); - -typedef void (*bt_component_class_sink_finalize_method)( - bt_self_component_sink *self_component); - -typedef bt_query_status -(*bt_component_class_sink_query_method)( - bt_self_component_class_sink *comp_class, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result); - -typedef bt_self_component_status -(*bt_component_class_sink_accept_input_port_connection_method)( - bt_self_component_sink *self_component, - bt_self_component_port_input *self_port, - const bt_port_output *other_port); - -typedef bt_self_component_status -(*bt_component_class_sink_input_port_connected_method)( - bt_self_component_sink *self_component, - bt_self_component_port_input *self_port, - const bt_port_output *other_port); - -typedef bt_self_component_status -(*bt_component_class_sink_graph_is_configured_method)( - bt_self_component_sink *self_component); - -typedef bt_self_component_status (*bt_component_class_sink_consume_method)( - bt_self_component_sink *self_component); - -bt_component_class *bt_component_class_sink_as_component_class( - bt_component_class_sink *comp_cls_sink); - -extern -bt_component_class_sink *bt_component_class_sink_create( - const char *name, - bt_component_class_sink_consume_method method); - -extern bt_component_class_status bt_component_class_sink_set_init_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_init_method method); - -extern bt_component_class_status bt_component_class_sink_set_finalize_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_finalize_method method); - -extern bt_component_class_status -bt_component_class_sink_set_accept_input_port_connection_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_accept_input_port_connection_method method); - -extern bt_component_class_status -bt_component_class_sink_set_input_port_connected_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_input_port_connected_method method); - -extern bt_component_class_status -bt_component_class_sink_set_graph_is_configured_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_graph_is_configured_method method); - -extern bt_component_class_status bt_component_class_sink_set_query_method( - bt_component_class_sink *comp_class, - bt_component_class_sink_query_method method); - -/* From self-component-class-source.h */ - -const bt_component_class_source * -bt_self_component_class_source_as_component_class_source( - bt_self_component_class_source *self_comp_cls_source); - -/* From self-component-class-filter.h */ - -const bt_component_class_filter * -bt_self_component_class_filter_as_component_class_filter( - bt_self_component_class_filter *self_comp_cls_filter); - -/* From self-component-class-sink.h */ - -const bt_component_class_sink * -bt_self_component_class_sink_as_component_class_sink( - bt_self_component_class_sink *self_comp_cls_sink); - -%{ -/* - * This hash table associates a BT component class object address to a - * user-defined Python class (PyObject *). The keys and values are NOT - * owned by this hash table. The Python class objects are owned by the - * Python module, which should not be unloaded until it is not possible - * to create a user Python component anyway. - * - * This hash table is written to when a user-defined Python component - * class is created by one of the bt_py3_component_class_*_create() - * functions. - * - * This function is read from when a user calls bt_component_create() - * with a component class pointer created by one of the functions above. - * In this case, the original Python class needs to be found to - * instantiate it and associate the created Python component object with - * a BT component object instance. - */ - -static GHashTable *bt_cc_ptr_to_py_cls; - -static void register_cc_ptr_to_py_cls(struct bt_component_class *bt_cc, - PyObject *py_cls) -{ - if (!bt_cc_ptr_to_py_cls) { - /* - * Lazy-initializing this GHashTable because GLib - * might not be initialized yet and it needs to be - * before we call g_hash_table_new() - */ - BT_LOGD_STR("Creating native component class to Python component class hash table."); - bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal); - BT_ASSERT(bt_cc_ptr_to_py_cls); - } - - g_hash_table_insert(bt_cc_ptr_to_py_cls, (gpointer) bt_cc, - (gpointer) py_cls); -} - -static PyObject *lookup_cc_ptr_to_py_cls(const bt_component_class *bt_cc) -{ - if (!bt_cc_ptr_to_py_cls) { - BT_LOGW("Cannot look up Python component class because hash table is NULL: " - "comp-cls-addr=%p", bt_cc); - return NULL; - } - - return (PyObject *) g_hash_table_lookup(bt_cc_ptr_to_py_cls, - (gconstpointer) bt_cc); -} - - -/* - * Useful Python objects. - */ - -static PyObject *py_mod_bt2 = NULL; -static PyObject *py_mod_bt2_exc_error_type = NULL; -static PyObject *py_mod_bt2_exc_try_again_type = NULL; -static PyObject *py_mod_bt2_exc_stop_type = NULL; -static PyObject *py_mod_bt2_exc_port_connection_refused_type = NULL; -static PyObject *py_mod_bt2_exc_msg_iter_canceled_type = NULL; -static PyObject *py_mod_bt2_exc_invalid_query_object_type = NULL; -static PyObject *py_mod_bt2_exc_invalid_query_params_type = NULL; - -static void bt_py3_cc_init_from_bt2(void) -{ - /* - * This is called once the bt2 package is loaded. - * - * Those modules and functions are needed while the package is - * used. Loading them here is safe because we know the bt2 - * package is imported, and we know that the user cannot use the - * code here without importing bt2 first. - */ - py_mod_bt2 = PyImport_ImportModule("bt2"); - BT_ASSERT(py_mod_bt2); - py_mod_bt2_exc_error_type = - PyObject_GetAttrString(py_mod_bt2, "Error"); - BT_ASSERT(py_mod_bt2_exc_error_type); - py_mod_bt2_exc_try_again_type = - PyObject_GetAttrString(py_mod_bt2, "TryAgain"); - BT_ASSERT(py_mod_bt2_exc_try_again_type); - py_mod_bt2_exc_stop_type = - PyObject_GetAttrString(py_mod_bt2, "Stop"); - BT_ASSERT(py_mod_bt2_exc_stop_type); - py_mod_bt2_exc_port_connection_refused_type = - PyObject_GetAttrString(py_mod_bt2, "PortConnectionRefused"); - BT_ASSERT(py_mod_bt2_exc_port_connection_refused_type); - py_mod_bt2_exc_invalid_query_object_type = - PyObject_GetAttrString(py_mod_bt2, "InvalidQueryObject"); - BT_ASSERT(py_mod_bt2_exc_invalid_query_object_type); - py_mod_bt2_exc_invalid_query_params_type = - PyObject_GetAttrString(py_mod_bt2, "InvalidQueryParams"); - BT_ASSERT(py_mod_bt2_exc_invalid_query_params_type); -} - -static void bt_py3_cc_exit_handler(void) -{ - /* - * This is an exit handler (set by the bt2 package). - * - * We only give back the references that we took in - * bt_py3_cc_init_from_bt2() here. The global variables continue - * to exist for the code of this file, but they are now borrowed - * references. If this code is executed, it means that somehow - * the modules are still loaded, so it should be safe to use - * them even without a strong reference. - * - * We cannot do this in the library's destructor because it - * gets executed once Python is already finalized. - */ - Py_XDECREF(py_mod_bt2); - Py_XDECREF(py_mod_bt2_exc_error_type); - Py_XDECREF(py_mod_bt2_exc_try_again_type); - Py_XDECREF(py_mod_bt2_exc_stop_type); - Py_XDECREF(py_mod_bt2_exc_port_connection_refused_type); - Py_XDECREF(py_mod_bt2_exc_msg_iter_canceled_type); - Py_XDECREF(py_mod_bt2_exc_invalid_query_object_type); - Py_XDECREF(py_mod_bt2_exc_invalid_query_params_type); -} - - -/* Library destructor */ - -__attribute__((destructor)) -static void bt_py3_native_comp_class_dtor(void) { - /* Destroy component class association hash table */ - if (bt_cc_ptr_to_py_cls) { - BT_LOGD_STR("Destroying native component class to Python component class hash table."); - g_hash_table_destroy(bt_cc_ptr_to_py_cls); - } -} - - -// TODO: maybe we can wrap code in the Python methods (e.g. _query_from_native) -// in a try catch and print the error there instead, it would be simpler. -static -void bt2_py_loge_exception(void) -{ - PyObject *type = NULL; - PyObject *value = NULL; - PyObject *traceback = NULL; - PyObject *traceback_module = NULL; - PyObject *format_exception_func = NULL; - PyObject *exc_str_list = NULL; - GString *msg_buf = NULL; - Py_ssize_t i; - - BT_ASSERT(PyErr_Occurred() != NULL); - - PyErr_Fetch(&type, &value, &traceback); - - BT_ASSERT(type != NULL); - - /* - * traceback can be NULL, when we fail to call a Python function from the - * native code (there is not Python stack at that point). E.g.: - * - * TypeError: _accept_port_connection_from_native() takes 3 positional arguments but 4 were given - */ - - - /* Make sure `value` is what we expected - an instance of `type`. */ - PyErr_NormalizeException(&type, &value, &traceback); - - traceback_module = PyImport_ImportModule("traceback"); - if (!traceback_module) { - BT_LOGE_STR("Failed to log Python exception (could not import traceback module)."); - goto end; - } - - format_exception_func = PyObject_GetAttrString(traceback_module, - traceback ? "format_exception" : "format_exception_only"); - if (!format_exception_func) { - BT_LOGE_STR("Failed to log Python exception (could not find format_exception)."); - goto end; - } - - if (!PyCallable_Check(format_exception_func)) { - BT_LOGE_STR("Failed to log Python exception (format_exception is not callable)."); - goto end; - } - - exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func, type, value, traceback, NULL); - if (!exc_str_list) { - PyErr_Print(); - BT_LOGE_STR("Failed to log Python exception (call to format_exception failed)."); - goto end; - } - - msg_buf = g_string_new(NULL); - - for (i = 0; i < PyList_Size(exc_str_list); i++) { - PyObject *exc_str = PyList_GetItem(exc_str_list, i); - const char *str = PyUnicode_AsUTF8(exc_str); - if (!str) { - BT_LOGE_STR("Failed to log Python exception (failed to convert exception to string)."); - goto end; - } - - g_string_append(msg_buf, str); - } - - BT_LOGE_STR(msg_buf->str); - -end: - if (msg_buf) { - g_string_free(msg_buf, TRUE); - } - Py_XDECREF(exc_str_list); - Py_XDECREF(format_exception_func); - Py_XDECREF(traceback_module); - - /* PyErr_Restore takes our references. */ - PyErr_Restore(type, value, traceback); -} - -static bt_self_component_status bt_py3_exc_to_self_component_status(void) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - PyObject *exc = PyErr_Occurred(); - - if (!exc) { - goto end; - } - - if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_try_again_type)) { - status = BT_SELF_COMPONENT_STATUS_AGAIN; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_stop_type)) { - status = BT_SELF_COMPONENT_STATUS_END; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_port_connection_refused_type)) { - status = BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; - } else { - bt2_py_loge_exception(); - status = BT_SELF_COMPONENT_STATUS_ERROR; - } - -end: - PyErr_Clear(); - return status; -} - -/* Component class proxy methods (delegate to the attached Python object) */ - -static bt_self_message_iterator_status -bt_py3_exc_to_self_message_iterator_status(void) -{ - enum bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - PyObject *exc = PyErr_Occurred(); - - if (!exc) { - goto end; - } - - if (PyErr_GivenExceptionMatches(exc, py_mod_bt2_exc_stop_type)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - } else if (PyErr_GivenExceptionMatches(exc, py_mod_bt2_exc_try_again_type)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; - } else { - bt2_py_loge_exception(); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } - -end: - PyErr_Clear(); - return status; -} - -static enum bt_query_status bt_py3_exc_to_query_status(void) -{ - enum bt_query_status status = BT_QUERY_STATUS_OK; - PyObject *exc = PyErr_Occurred(); - - if (!exc) { - goto end; - } - - if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_invalid_query_object_type)) { - status = BT_QUERY_STATUS_INVALID_OBJECT; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_invalid_query_params_type)) { - status = BT_QUERY_STATUS_INVALID_PARAMS; - } else if (PyErr_GivenExceptionMatches(exc, - py_mod_bt2_exc_try_again_type)) { - status = BT_QUERY_STATUS_AGAIN; - } else { - bt2_py_loge_exception(); - status = BT_QUERY_STATUS_ERROR; - } - -end: - PyErr_Clear(); - return status; -} - -static bt_self_component_status -bt_py3_component_class_init( - bt_self_component *self_component, - void *self_component_v, - swig_type_info *self_comp_cls_type_swig_type, - const bt_value *params, - void *init_method_data) -{ - const bt_component *component = bt_self_component_as_component(self_component); - const bt_component_class *component_class = bt_component_borrow_class_const(component); - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - PyObject *py_cls = NULL; - PyObject *py_comp = NULL; - PyObject *py_params_ptr = NULL; - PyObject *py_comp_ptr = NULL; - - (void) init_method_data; - - BT_ASSERT(self_component); - BT_ASSERT(self_component_v); - BT_ASSERT(self_comp_cls_type_swig_type); - - /* - * Get the user-defined Python class which created this - * component's class in the first place (borrowed - * reference). - */ - py_cls = lookup_cc_ptr_to_py_cls(component_class); - if (!py_cls) { - BT_LOGE("Cannot find Python class associated to native component class: " - "comp-cls-addr=%p", component_class); - goto error; - } - - /* Parameters pointer -> SWIG pointer Python object */ - py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), - SWIGTYPE_p_bt_value, 0); - if (!py_params_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_v), - self_comp_cls_type_swig_type, 0); - if (!py_comp_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - /* - * Do the equivalent of this: - * - * py_comp = py_cls._init_from_native(py_comp_ptr, py_params_ptr) - * - * _UserComponentType._init_from_native() calls the Python - * component object's __init__() function. - */ - py_comp = PyObject_CallMethod(py_cls, - "_init_from_native", "(OO)", py_comp_ptr, py_params_ptr); - if (!py_comp) { - bt2_py_loge_exception(); - BT_LOGE("Failed to call Python class's _init_from_native() method: " - "py-cls-addr=%p", py_cls); - - goto error; - } - - /* - * Our user Python component object is now fully created and - * initialized by the user. Since we just created it, this - * native component is its only (persistent) owner. - */ - bt_self_component_set_data(self_component, py_comp); - py_comp = NULL; - goto end; - -error: - status = BT_SELF_COMPONENT_STATUS_ERROR; - - /* - * Clear any exception: we're returning a bad status anyway. If - * this call originated from Python (creation from a plugin's - * component class, for example), then the user gets an - * appropriate creation error. - */ - PyErr_Clear(); - -end: - Py_XDECREF(py_comp); - Py_XDECREF(py_params_ptr); - Py_XDECREF(py_comp_ptr); - return status; -} - -/* - * Method of bt_component_class_source to initialize a bt_self_component_source - * of that class. - */ - -static bt_self_component_status -bt_py3_component_class_source_init(bt_self_component_source *self_component_source, - const bt_value *params, void *init_method_data) -{ - bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); - return bt_py3_component_class_init( - self_component, - self_component_source, - SWIGTYPE_p_bt_self_component_source, - params, init_method_data); -} - -static bt_self_component_status -bt_py3_component_class_filter_init(bt_self_component_filter *self_component_filter, - const bt_value *params, void *init_method_data) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - return bt_py3_component_class_init( - self_component, - self_component_filter, - SWIGTYPE_p_bt_self_component_filter, - params, init_method_data); -} - -static bt_self_component_status -bt_py3_component_class_sink_init(bt_self_component_sink *self_component_sink, - const bt_value *params, void *init_method_data) -{ - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - return bt_py3_component_class_init( - self_component, - self_component_sink, - SWIGTYPE_p_bt_self_component_sink, - params, init_method_data); -} - -static void bt_py3_component_class_finalize(bt_self_component *self_component) -{ - PyObject *py_comp = bt_self_component_get_data(self_component); - BT_ASSERT(py_comp); - - /* Call user's _finalize() method */ - PyObject *py_method_result = PyObject_CallMethod(py_comp, - "_finalize", NULL); - - if (PyErr_Occurred()) { - BT_LOGW("User's _finalize() method raised an exception: ignoring."); - } - - /* - * Ignore any exception raised by the _finalize() method because - * it won't change anything at this point: the component is - * being destroyed anyway. - */ - PyErr_Clear(); - Py_XDECREF(py_method_result); - Py_DECREF(py_comp); -} - -static void -bt_py3_component_class_source_finalize(bt_self_component_source *self_component_source) -{ - bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); - bt_py3_component_class_finalize(self_component); -} - -static void -bt_py3_component_class_filter_finalize(bt_self_component_filter *self_component_filter) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - bt_py3_component_class_finalize(self_component); -} - -static void -bt_py3_component_class_sink_finalize(bt_self_component_sink *self_component_sink) -{ - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - bt_py3_component_class_finalize(self_component); -} - -static bt_self_component_status -bt_py3_component_class_accept_port_connection( - bt_self_component *self_component, - bt_self_component_port *self_component_port, - bt_port_type self_component_port_type, - const bt_port *other_port) -{ - enum bt_self_component_status status; - PyObject *py_comp = NULL; - PyObject *py_self_port_ptr = NULL; - PyObject *py_other_port_ptr = NULL; - PyObject *py_method_result = NULL; - - py_comp = bt_self_component_get_data(self_component); - BT_ASSERT(py_comp); - - swig_type_info *self_component_port_swig_type = NULL; - swig_type_info *other_port_swig_type = NULL; - switch (self_component_port_type) { - case BT_PORT_TYPE_INPUT: - self_component_port_swig_type = SWIGTYPE_p_bt_self_component_port_input; - other_port_swig_type = SWIGTYPE_p_bt_port_output; - break; - case BT_PORT_TYPE_OUTPUT: - self_component_port_swig_type = SWIGTYPE_p_bt_self_component_port_output; - other_port_swig_type = SWIGTYPE_p_bt_port_input; - break; - } - BT_ASSERT(self_component_port_swig_type != NULL); - BT_ASSERT(other_port_swig_type != NULL); - - py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port), - self_component_port_swig_type, 0); - if (!py_self_port_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), - other_port_swig_type, 0); - if (!py_other_port_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_method_result = PyObject_CallMethod(py_comp, - "_accept_port_connection_from_native", "(OiO)", py_self_port_ptr, - self_component_port_type, py_other_port_ptr); - - status = bt_py3_exc_to_self_component_status(); - if (!py_method_result && status == BT_SELF_COMPONENT_STATUS_OK) { - /* Pretty sure this should never happen, but just in case */ - BT_LOGE("User's _accept_port_connection() method failed without raising an exception: " - "status=%d", status); - goto error; - } - - if (status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { - /* - * Looks like the user method raised - * PortConnectionRefused: accept this like if it - * returned False. - */ - goto end; - } else if (status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGE("User's _accept_port_connection() raised an unexpected exception: " - "status=%d", status); - goto error; - } - - BT_ASSERT(PyBool_Check(py_method_result)); - - if (py_method_result == Py_True) { - status = BT_SELF_COMPONENT_STATUS_OK; - } else { - status = BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; - } - - goto end; - -error: - status = BT_SELF_COMPONENT_STATUS_ERROR; - - /* - * Clear any exception: we're returning a bad status anyway. If - * this call originated from Python, then the user gets an - * appropriate error. - */ - PyErr_Clear(); - -end: - Py_XDECREF(py_self_port_ptr); - Py_XDECREF(py_other_port_ptr); - Py_XDECREF(py_method_result); - return status; -} - -static bt_self_component_status -bt_py3_component_class_source_accept_output_port_connection(bt_self_component_source *self_component_source, - bt_self_component_port_output *self_component_port_output, - const bt_port_input *other_port_input) -{ - bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); - bt_self_component_port *self_component_port = bt_self_component_port_output_as_self_component_port(self_component_port_output); - const bt_port *other_port = bt_port_input_as_port_const(other_port_input); - return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_OUTPUT, other_port); -} - -static bt_self_component_status -bt_py3_component_class_filter_accept_input_port_connection(bt_self_component_filter *self_component_filter, - bt_self_component_port_input *self_component_port_input, - const bt_port_output *other_port_output) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - bt_self_component_port *self_component_port = bt_self_component_port_input_as_self_component_port(self_component_port_input); - const bt_port *other_port = bt_port_output_as_port_const(other_port_output); - return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_INPUT, other_port); -} - -static bt_self_component_status -bt_py3_component_class_filter_accept_output_port_connection(bt_self_component_filter *self_component_filter, - bt_self_component_port_output *self_component_port_output, - const bt_port_input *other_port_input) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - bt_self_component_port *self_component_port = bt_self_component_port_output_as_self_component_port(self_component_port_output); - const bt_port *other_port = bt_port_input_as_port_const(other_port_input); - return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_OUTPUT, other_port); -} - -static bt_self_component_status -bt_py3_component_class_sink_accept_input_port_connection(bt_self_component_sink *self_component_sink, - bt_self_component_port_input *self_component_port_input, - const bt_port_output *other_port_output) -{ - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - bt_self_component_port *self_component_port = bt_self_component_port_input_as_self_component_port(self_component_port_input); - const bt_port *other_port = bt_port_output_as_port_const(other_port_output); - return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_INPUT, other_port); -} - -static bt_self_component_status -bt_py3_component_class_port_connected( - bt_self_component *self_component, - void *self_component_port, - swig_type_info *self_component_port_swig_type, - bt_port_type self_component_port_type, - const void *other_port, - swig_type_info *other_port_swig_type) -{ - bt_self_component_status status; - PyObject *py_comp = NULL; - PyObject *py_self_port_ptr = NULL; - PyObject *py_other_port_ptr = NULL; - PyObject *py_method_result = NULL; - - py_comp = bt_self_component_get_data(self_component); - BT_ASSERT(py_comp); - - py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port), - self_component_port_swig_type, 0); - if (!py_self_port_ptr) { - BT_LOGF_STR("Failed to create a SWIG pointer object."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), - other_port_swig_type, 0); - if (!py_other_port_ptr) { - BT_LOGF_STR("Failed to create a SWIG pointer object."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; } - - py_method_result = PyObject_CallMethod(py_comp, - "_port_connected_from_native", "(OiO)", py_self_port_ptr, - self_component_port_type, py_other_port_ptr); - - BT_ASSERT(!py_method_result || py_method_result == Py_None); - - status = bt_py3_exc_to_self_component_status(); - -end: - Py_XDECREF(py_self_port_ptr); - Py_XDECREF(py_other_port_ptr); - Py_XDECREF(py_method_result); - - return status; -} - -static bt_self_component_status -bt_py3_component_class_source_output_port_connected( - bt_self_component_source *self_component_source, - bt_self_component_port_output *self_component_port_output, - const bt_port_input *other_port_input) -{ - bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); - - return bt_py3_component_class_port_connected( - self_component, - self_component_port_output, - SWIGTYPE_p_bt_self_component_port_output, - BT_PORT_TYPE_OUTPUT, - other_port_input, - SWIGTYPE_p_bt_port_input); -} - -static bt_self_component_status -bt_py3_component_class_filter_input_port_connected( - bt_self_component_filter *self_component_filter, - bt_self_component_port_input *self_component_port_input, - const bt_port_output *other_port_output) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - - return bt_py3_component_class_port_connected( - self_component, - self_component_port_input, - SWIGTYPE_p_bt_self_component_port_input, - BT_PORT_TYPE_INPUT, - other_port_output, - SWIGTYPE_p_bt_port_output); -} - -static bt_self_component_status -bt_py3_component_class_filter_output_port_connected( - bt_self_component_filter *self_component_filter, - bt_self_component_port_output *self_component_port_output, - const bt_port_input *other_port_input) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - - return bt_py3_component_class_port_connected( - self_component, - self_component_port_output, - SWIGTYPE_p_bt_self_component_port_output, - BT_PORT_TYPE_OUTPUT, - other_port_input, - SWIGTYPE_p_bt_port_input); -} - -static bt_self_component_status -bt_py3_component_class_sink_input_port_connected( - bt_self_component_sink *self_component_sink, - bt_self_component_port_input *self_component_port_input, - const bt_port_output *other_port_output) -{ - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - - return bt_py3_component_class_port_connected( - self_component, - self_component_port_input, - SWIGTYPE_p_bt_self_component_port_input, - BT_PORT_TYPE_INPUT, - other_port_output, - SWIGTYPE_p_bt_port_output); -} - -static bt_self_component_status -bt_py3_component_class_sink_graph_is_configured(bt_self_component_sink *self_component_sink) -{ - PyObject *py_comp = NULL; - PyObject *py_method_result = NULL; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - - py_comp = bt_self_component_get_data(self_component); - py_method_result = PyObject_CallMethod(py_comp, - "_graph_is_configured_from_native", NULL); - - BT_ASSERT(!py_method_result || py_method_result == Py_None); - - status = bt_py3_exc_to_self_component_status(); - - Py_XDECREF(py_method_result); - - return status; -} - -static bt_query_status -bt_py3_component_class_query( - const bt_component_class *component_class, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result) -{ - PyObject *py_cls = NULL; - PyObject *py_params_ptr = NULL; - PyObject *py_query_exec_ptr = NULL; - PyObject *py_query_func = NULL; - PyObject *py_object = NULL; - PyObject *py_results_addr = NULL; - bt_query_status status = BT_QUERY_STATUS_OK; - - py_cls = lookup_cc_ptr_to_py_cls(component_class); - if (!py_cls) { - BT_LOGE("Cannot find Python class associated to native component class: " - "comp-cls-addr=%p", component_class); - goto error; - } - - py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), - SWIGTYPE_p_bt_value, 0); - if (!py_params_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_query_exec_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(query_executor), - SWIGTYPE_p_bt_query_executor, 0); - if (!py_query_exec_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_object = SWIG_FromCharPtr(object); - if (!py_object) { - BT_LOGE_STR("Failed to create a Python string."); - goto error; - } - - py_results_addr = PyObject_CallMethod(py_cls, - "_query_from_native", "(OOO)", py_query_exec_ptr, - py_object, py_params_ptr); - - if (!py_results_addr) { - BT_LOGE("Failed to call Python class's _query_from_native() method: " - "py-cls-addr=%p", py_cls); - status = bt_py3_exc_to_query_status(); - goto end; - } - - /* - * The returned object, on success, is an integer object - * (PyLong) containing the address of a BT value object (new - * reference). - */ - *result = PyLong_AsVoidPtr(py_results_addr); - BT_ASSERT(!PyErr_Occurred()); - BT_ASSERT(*result); - goto end; - -error: - PyErr_Clear(); - status = BT_QUERY_STATUS_ERROR; - -end: - Py_XDECREF(py_params_ptr); - Py_XDECREF(py_query_exec_ptr); - Py_XDECREF(py_query_func); - Py_XDECREF(py_object); - Py_XDECREF(py_results_addr); - return status; -} - -static bt_query_status -bt_py3_component_class_source_query( - bt_self_component_class_source *self_component_class_source, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result) -{ - const bt_component_class_source *component_class_source = bt_self_component_class_source_as_component_class_source(self_component_class_source); - const bt_component_class *component_class = bt_component_class_source_as_component_class_const(component_class_source); - return bt_py3_component_class_query(component_class, query_executor, object, params, result); -} - -static bt_query_status -bt_py3_component_class_filter_query( - bt_self_component_class_filter *self_component_class_filter, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result) -{ - const bt_component_class_filter *component_class_filter = bt_self_component_class_filter_as_component_class_filter(self_component_class_filter); - const bt_component_class *component_class = bt_component_class_filter_as_component_class_const(component_class_filter); - return bt_py3_component_class_query(component_class, query_executor, object, params, result); -} - -static bt_query_status -bt_py3_component_class_sink_query( - bt_self_component_class_sink *self_component_class_sink, - const bt_query_executor *query_executor, - const char *object, const bt_value *params, - const bt_value **result) -{ - const bt_component_class_sink *component_class_sink = bt_self_component_class_sink_as_component_class_sink(self_component_class_sink); - const bt_component_class *component_class = bt_component_class_sink_as_component_class_const(component_class_sink); - return bt_py3_component_class_query(component_class, query_executor, object, params, result); -} - -static bt_self_message_iterator_status -bt_py3_component_class_message_iterator_init( - bt_self_message_iterator *self_message_iterator, - bt_self_component *self_component, - bt_self_component_port_output *self_component_port_output) -{ - bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - PyObject *py_comp_cls = NULL; - PyObject *py_iter_cls = NULL; - PyObject *py_iter_ptr = NULL; - PyObject *py_component_port_output_ptr = NULL; - PyObject *py_init_method_result = NULL; - PyObject *py_iter = NULL; - PyObject *py_comp; - - py_comp = bt_self_component_get_data(self_component); - - /* Find user's Python message iterator class */ - py_comp_cls = PyObject_GetAttrString(py_comp, "__class__"); - if (!py_comp_cls) { - BT_LOGE_STR("Cannot get Python object's `__class__` attribute."); - goto error; - } - - py_iter_cls = PyObject_GetAttrString(py_comp_cls, "_iter_cls"); - if (!py_iter_cls) { - BT_LOGE_STR("Cannot get Python class's `_iter_cls` attribute."); - goto error; - } - - py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_message_iterator), - SWIGTYPE_p_bt_self_message_iterator, 0); - if (!py_iter_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - /* - * Create object with borrowed native message iterator - * reference: - * - * py_iter = py_iter_cls.__new__(py_iter_cls, py_iter_ptr) - */ - py_iter = PyObject_CallMethod(py_iter_cls, "__new__", - "(OO)", py_iter_cls, py_iter_ptr); - if (!py_iter) { - BT_LOGE("Failed to call Python class's __new__() method: " - "py-cls-addr=%p", py_iter_cls); - bt2_py_loge_exception(); - goto error; - } - - /* - * Initialize object: - * - * py_iter.__init__(self_output_port) - * - * through the _init_for_native helper static method. - * - * At this point, py_iter._ptr is set, so this initialization - * function has access to self._component (which gives it the - * user Python component object from which the iterator was - * created). - */ - py_component_port_output_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port_output), - SWIGTYPE_p_bt_self_component_port_output, 0); - if (!py_component_port_output_ptr) { - BT_LOGE_STR("Failed to create a SWIG pointer object."); - goto error; - } - - py_init_method_result = PyObject_CallMethod(py_iter, "_init_from_native", "O", py_component_port_output_ptr); - if (!py_init_method_result) { - BT_LOGE_STR("User's __init__() method failed."); - bt2_py_loge_exception(); - goto error; - } - - /* - * Since the Python code can never instantiate a user-defined - * message iterator class, the native message iterator - * object does NOT belong to a user Python message iterator - * object (borrowed reference). However this Python object is - * owned by this native message iterator object. - * - * In the Python world, the lifetime of the native message - * iterator is managed by a _GenericMessageIterator - * instance: - * - * _GenericMessageIterator instance: - * owns a native bt_message_iterator object (iter) - * owns a _UserMessageIterator instance (py_iter) - * self._ptr is a borrowed reference to the - * native bt_private_connection_private_message_iterator - * object (iter) - */ - bt_self_message_iterator_set_data(self_message_iterator, py_iter); - py_iter = NULL; - goto end; - -error: - status = bt_py3_exc_to_self_message_iterator_status(); - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - /* - * Looks like there wasn't any exception from the Python - * side, but we're still in an error state here. - */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } - - /* - * Clear any exception: we're returning a bad status anyway. If - * this call originated from Python, then the user gets an - * appropriate creation error. - */ - PyErr_Clear(); - -end: - Py_XDECREF(py_comp_cls); - Py_XDECREF(py_iter_cls); - Py_XDECREF(py_iter_ptr); - Py_XDECREF(py_component_port_output_ptr); - Py_XDECREF(py_init_method_result); - Py_XDECREF(py_iter); - return status; -} - -static bt_self_message_iterator_status -bt_py3_component_class_source_message_iterator_init( - bt_self_message_iterator *self_message_iterator, - bt_self_component_source *self_component_source, - bt_self_component_port_output *self_component_port_output) -{ - bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); - return bt_py3_component_class_message_iterator_init(self_message_iterator, self_component, self_component_port_output); -} - -static bt_self_message_iterator_status -bt_py3_component_class_filter_message_iterator_init( - bt_self_message_iterator *self_message_iterator, - bt_self_component_filter *self_component_filter, - bt_self_component_port_output *self_component_port_output) -{ - bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); - return bt_py3_component_class_message_iterator_init(self_message_iterator, self_component, self_component_port_output); -} - -static void -bt_py3_component_class_message_iterator_finalize( - bt_self_message_iterator *message_iterator) -{ - PyObject *py_message_iter = bt_self_message_iterator_get_data(message_iterator); - PyObject *py_method_result = NULL; - - BT_ASSERT(py_message_iter); - - /* Call user's _finalize() method */ - py_method_result = PyObject_CallMethod(py_message_iter, - "_finalize", NULL); - - if (PyErr_Occurred()) { - BT_LOGW("User's _finalize() method raised an exception: ignoring."); - } - - /* - * Ignore any exception raised by the _finalize() method because - * it won't change anything at this point: the component is - * being destroyed anyway. - */ - PyErr_Clear(); - Py_XDECREF(py_method_result); - Py_DECREF(py_message_iter); -} - -/* Valid for both sources and filters. */ - -static bt_self_message_iterator_status -bt_py3_component_class_message_iterator_next( - bt_self_message_iterator *message_iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - PyObject *py_message_iter = bt_self_message_iterator_get_data(message_iterator); - PyObject *py_method_result = NULL; - - BT_ASSERT(py_message_iter); - py_method_result = PyObject_CallMethod(py_message_iter, - "_next_from_native", NULL); - if (!py_method_result) { - status = bt_py3_exc_to_self_message_iterator_status(); - BT_ASSERT(status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK); - goto end; - } - - /* - * The returned object, on success, is an integer object - * (PyLong) containing the address of a native message - * object (which is now ours). - */ - msgs[0] = PyLong_AsVoidPtr(py_method_result); - *count = 1; - - /* Clear potential overflow error; should never happen */ - BT_ASSERT(!PyErr_Occurred()); - goto end; - -end: - Py_XDECREF(py_method_result); - return status; -} - -static bt_self_component_status -bt_py3_component_class_sink_consume(bt_self_component_sink *self_component_sink) -{ - bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); - PyObject *py_comp = bt_self_component_get_data(self_component); - PyObject *py_method_result = NULL; - bt_self_component_status status; - - BT_ASSERT(py_comp); - py_method_result = PyObject_CallMethod(py_comp, - "_consume", NULL); - - status = bt_py3_exc_to_self_component_status(); - if (!py_method_result && status == BT_SELF_COMPONENT_STATUS_OK) { - /* Pretty sure this should never happen, but just in case */ - BT_LOGE("User's _consume() method failed without raising an exception: " - "status=%d", status); - status = BT_SELF_COMPONENT_STATUS_ERROR; - } - - Py_XDECREF(py_method_result); - return status; -} - -static -int bt_py3_component_class_set_help_and_desc( - bt_component_class *component_class, - const char *description, const char *help) -{ - int ret; - - if (description) { - ret = bt_component_class_set_description(component_class, description); - if (ret) { - BT_LOGE("Cannot set component class's description: " - "comp-cls-addr=%p", component_class); - goto end; - } - } - - if (help) { - ret = bt_component_class_set_help(component_class, help); - if (ret) { - BT_LOGE("Cannot set component class's help text: " - "comp-cls-addr=%p", component_class); - goto end; - } - } - - ret = 0; - -end: - return ret; -} - -static -bt_component_class_source *bt_py3_component_class_source_create( - PyObject *py_cls, const char *name, const char *description, - const char *help) -{ - bt_component_class_source *component_class_source; - bt_component_class *component_class; - int ret; - - BT_ASSERT(py_cls); - - component_class_source = bt_component_class_source_create(name, - bt_py3_component_class_message_iterator_next); - if (!component_class_source) { - BT_LOGE_STR("Cannot create source component class."); - goto end; - } - - component_class = bt_component_class_source_as_component_class(component_class_source); - - if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { - goto end; - } - - ret = bt_component_class_source_set_init_method(component_class_source, bt_py3_component_class_source_init); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_finalize_method (component_class_source, bt_py3_component_class_source_finalize); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_accept_output_port_connection_method(component_class_source, - bt_py3_component_class_source_accept_output_port_connection); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_output_port_connected_method(component_class_source, - bt_py3_component_class_source_output_port_connected); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_query_method(component_class_source, bt_py3_component_class_source_query); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_message_iterator_init_method( - component_class_source, bt_py3_component_class_source_message_iterator_init); - BT_ASSERT(ret == 0); - ret = bt_component_class_source_set_message_iterator_finalize_method( - component_class_source, bt_py3_component_class_message_iterator_finalize); - BT_ASSERT(ret == 0); - - register_cc_ptr_to_py_cls(component_class, py_cls); - -end: - return component_class_source; -} - -static -bt_component_class_filter *bt_py3_component_class_filter_create( - PyObject *py_cls, const char *name, const char *description, - const char *help) -{ - bt_component_class *component_class; - bt_component_class_filter *component_class_filter; - int ret; - - BT_ASSERT(py_cls); - - component_class_filter = bt_component_class_filter_create(name, - bt_py3_component_class_message_iterator_next); - if (!component_class_filter) { - BT_LOGE_STR("Cannot create filter component class."); - goto end; - } - - component_class = bt_component_class_filter_as_component_class(component_class_filter); - - if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { - goto end; - } - - ret = bt_component_class_filter_set_init_method(component_class_filter, bt_py3_component_class_filter_init); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_finalize_method (component_class_filter, bt_py3_component_class_filter_finalize); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_accept_input_port_connection_method(component_class_filter, - bt_py3_component_class_filter_accept_input_port_connection); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_accept_output_port_connection_method(component_class_filter, - bt_py3_component_class_filter_accept_output_port_connection); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_input_port_connected_method(component_class_filter, - bt_py3_component_class_filter_input_port_connected); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_output_port_connected_method(component_class_filter, - bt_py3_component_class_filter_output_port_connected); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_query_method(component_class_filter, bt_py3_component_class_filter_query); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_message_iterator_init_method( - component_class_filter, bt_py3_component_class_filter_message_iterator_init); - BT_ASSERT(ret == 0); - ret = bt_component_class_filter_set_message_iterator_finalize_method( - component_class_filter, bt_py3_component_class_message_iterator_finalize); - BT_ASSERT(ret == 0); - - register_cc_ptr_to_py_cls(component_class, py_cls); - -end: - return component_class_filter; -} - -static -bt_component_class_sink *bt_py3_component_class_sink_create( - PyObject *py_cls, const char *name, const char *description, - const char *help) -{ - bt_component_class_sink *component_class_sink; - bt_component_class *component_class; - int ret; - - BT_ASSERT(py_cls); - - component_class_sink = bt_component_class_sink_create(name, bt_py3_component_class_sink_consume); - - if (!component_class_sink) { - BT_LOGE_STR("Cannot create sink component class."); - goto end; - } - - component_class = bt_component_class_sink_as_component_class(component_class_sink); - - if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { - goto end; - } - - ret = bt_component_class_sink_set_init_method(component_class_sink, bt_py3_component_class_sink_init); - BT_ASSERT(ret == 0); - ret = bt_component_class_sink_set_finalize_method(component_class_sink, bt_py3_component_class_sink_finalize); - BT_ASSERT(ret == 0); - ret = bt_component_class_sink_set_accept_input_port_connection_method(component_class_sink, - bt_py3_component_class_sink_accept_input_port_connection); - BT_ASSERT(ret == 0); - ret = bt_component_class_sink_set_input_port_connected_method(component_class_sink, - bt_py3_component_class_sink_input_port_connected); - BT_ASSERT(ret == 0); - ret = bt_component_class_sink_set_graph_is_configured_method(component_class_sink, - bt_py3_component_class_sink_graph_is_configured); - BT_ASSERT(ret == 0); - ret = bt_component_class_sink_set_query_method(component_class_sink, bt_py3_component_class_sink_query); - BT_ASSERT(ret == 0); - - register_cc_ptr_to_py_cls(component_class, py_cls); - -end: - return component_class_sink; -} -%} - -struct bt_component_class_source *bt_py3_component_class_source_create( - PyObject *py_cls, const char *name, const char *description, - const char *help); -struct bt_component_class_filter *bt_py3_component_class_filter_create( - PyObject *py_cls, const char *name, const char *description, - const char *help); -struct bt_component_class_sink *bt_py3_component_class_sink_create( - PyObject *py_cls, const char *name, const char *description, - const char *help); -void bt_py3_cc_init_from_bt2(void); -void bt_py3_cc_exit_handler(void); diff --git a/bindings/python/bt2/bt2/native_bt_connection.i b/bindings/python/bt2/bt2/native_bt_connection.i deleted file mode 100644 index dbdb04a5..00000000 --- a/bindings/python/bt2/bt2/native_bt_connection.i +++ /dev/null @@ -1,35 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From connection-const.h */ - -extern const bt_port_input *bt_connection_borrow_downstream_port_const( - const bt_connection *connection); - -extern const bt_port_output *bt_connection_borrow_upstream_port_const( - const bt_connection *connection); - -extern void bt_connection_get_ref(const bt_connection *connection); - -extern void bt_connection_put_ref(const bt_connection *connection); diff --git a/bindings/python/bt2/bt2/native_bt_event.i b/bindings/python/bt2/bt2/native_bt_event.i deleted file mode 100644 index 82ddbfae..00000000 --- a/bindings/python/bt2/bt2/native_bt_event.i +++ /dev/null @@ -1,64 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From event-const.h */ - -typedef enum bt_event_status { - BT_EVENT_STATUS_OK = 0, - BT_EVENT_STATUS_NOMEM = -12, -} bt_event_status; - -extern const bt_event_class *bt_event_borrow_class_const( - const bt_event *event); - -extern const bt_packet *bt_event_borrow_packet_const( - const bt_event *event); - -extern const bt_stream *bt_event_borrow_stream_const( - const bt_event *event); - -extern const bt_field *bt_event_borrow_common_context_field_const( - const bt_event *event); - -extern const bt_field *bt_event_borrow_specific_context_field_const( - const bt_event *event); - -extern const bt_field *bt_event_borrow_payload_field_const( - const bt_event *event); - -/* From event.h */ - -extern bt_event_class *bt_event_borrow_class(bt_event *event); - -extern bt_packet *bt_event_borrow_packet(bt_event *event); - -extern bt_stream *bt_event_borrow_stream(bt_event *event); - -extern bt_field * -bt_event_borrow_common_context_field(bt_event *event); - -extern bt_field * -bt_event_borrow_specific_context_field(bt_event *event); - -extern bt_field *bt_event_borrow_payload_field(bt_event *event); diff --git a/bindings/python/bt2/bt2/native_bt_event_class.i b/bindings/python/bt2/bt2/native_bt_event_class.i deleted file mode 100644 index cac0c411..00000000 --- a/bindings/python/bt2/bt2/native_bt_event_class.i +++ /dev/null @@ -1,121 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* Output argument typemap for initialized event class log level output - * parameter (always appends). - */ -%typemap(in, numinputs=0) - (bt_event_class_log_level *OUT) - (bt_event_class_log_level temp = -1) { - $1 = &temp; -} - -%typemap(argout) bt_event_class_log_level *OUT { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, SWIG_From_int(*$1)); -} - -/* From event-class-const.h */ - -typedef enum bt_event_class_status { - BT_EVENT_CLASS_STATUS_OK = 0, - BT_EVENT_CLASS_STATUS_NOMEM = -12, -} bt_event_class_status; - -typedef enum bt_event_class_log_level { - BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY, - BT_EVENT_CLASS_LOG_LEVEL_ALERT, - BT_EVENT_CLASS_LOG_LEVEL_CRITICAL, - BT_EVENT_CLASS_LOG_LEVEL_ERROR, - BT_EVENT_CLASS_LOG_LEVEL_WARNING, - BT_EVENT_CLASS_LOG_LEVEL_NOTICE, - BT_EVENT_CLASS_LOG_LEVEL_INFO, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE, - BT_EVENT_CLASS_LOG_LEVEL_DEBUG, -} bt_event_class_log_level; - -extern const bt_stream_class *bt_event_class_borrow_stream_class_const( - const bt_event_class *event_class); - -extern const char *bt_event_class_get_name(const bt_event_class *event_class); - -extern uint64_t bt_event_class_get_id(const bt_event_class *event_class); - -extern bt_property_availability bt_event_class_get_log_level( - const bt_event_class *event_class, - bt_event_class_log_level *OUT); - -extern const char *bt_event_class_get_emf_uri( - const bt_event_class *event_class); - -extern const bt_field_class * -bt_event_class_borrow_specific_context_field_class_const( - const bt_event_class *event_class); - -extern const bt_field_class *bt_event_class_borrow_payload_field_class_const( - const bt_event_class *event_class); - -extern void bt_event_class_get_ref(const bt_event_class *event_class); - -extern void bt_event_class_put_ref(const bt_event_class *event_class); - -/* From event-class.h */ - -extern bt_event_class *bt_event_class_create( - bt_stream_class *stream_class); - -extern bt_event_class *bt_event_class_create_with_id( - bt_stream_class *stream_class, uint64_t id); - -extern bt_stream_class *bt_event_class_borrow_stream_class( - bt_event_class *event_class); - -extern bt_event_class_status bt_event_class_set_name( - bt_event_class *event_class, const char *name); - -extern void bt_event_class_set_log_level(bt_event_class *event_class, - bt_event_class_log_level log_level); - -extern bt_event_class_status bt_event_class_set_emf_uri( - bt_event_class *event_class, const char *emf_uri); - -extern bt_event_class_status -bt_event_class_set_specific_context_field_class(bt_event_class *event_class, - bt_field_class *field_class); - -extern bt_field_class * -bt_event_class_borrow_specific_context_field_class(bt_event_class *event_class); - -extern bt_event_class_status bt_event_class_set_payload_field_class( - bt_event_class *event_class, - bt_field_class *field_class); - -extern bt_field_class *bt_event_class_borrow_payload_field_class( - bt_event_class *event_class); diff --git a/bindings/python/bt2/bt2/native_bt_field.i b/bindings/python/bt2/bt2/native_bt_field.i deleted file mode 100644 index f4511f45..00000000 --- a/bindings/python/bt2/bt2/native_bt_field.i +++ /dev/null @@ -1,120 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* For label type mappings. */ -%include "native_bt_field_class.i" - -/* From field-const.h */ - -typedef enum bt_field_status { - BT_FIELD_STATUS_OK = 0, - BT_FIELD_STATUS_NOMEM = -12, -} bt_field_status; - -extern const bt_field_class *bt_field_borrow_class_const( - const bt_field *field); - -extern bt_field_class_type bt_field_get_class_type( - const bt_field *field); - -extern int64_t bt_field_signed_integer_get_value(const bt_field *field); - -extern uint64_t bt_field_unsigned_integer_get_value( - const bt_field *field); - -extern double bt_field_real_get_value(const bt_field *field); - -extern bt_field_status bt_field_unsigned_enumeration_get_mapping_labels( - const bt_field *field, - bt_field_class_enumeration_mapping_label_array *LABELARRAY, - uint64_t *LABELCOUNT); - -extern bt_field_status bt_field_signed_enumeration_get_mapping_labels( - const bt_field *field, - bt_field_class_enumeration_mapping_label_array *LABELARRAY, - uint64_t *LABELCOUNT); - -extern const char *bt_field_string_get_value(const bt_field *field); - -extern uint64_t bt_field_string_get_length(const bt_field *field); - -extern const bt_field * -bt_field_structure_borrow_member_field_by_index_const( - const bt_field *field, uint64_t index); - -extern const bt_field * -bt_field_structure_borrow_member_field_by_name_const( - const bt_field *field, const char *name); - -extern uint64_t bt_field_array_get_length(const bt_field *field); - -extern const bt_field * -bt_field_array_borrow_element_field_by_index_const( - const bt_field *field, uint64_t index); - -extern uint64_t bt_field_variant_get_selected_option_field_index( - const bt_field *field); - -extern const bt_field * -bt_field_variant_borrow_selected_option_field_const( - const bt_field *field); - -/* From field.h */ - -extern void bt_field_signed_integer_set_value(bt_field *field, - int64_t value); - -extern void bt_field_unsigned_integer_set_value(bt_field *field, - uint64_t value); - -extern void bt_field_real_set_value(bt_field *field, double value); - -extern bt_field_status bt_field_string_set_value(bt_field *field, - const char *value); - -extern bt_field_status bt_field_string_append(bt_field *field, - const char *value); - -extern bt_field_status bt_field_string_append_with_length(bt_field *field, - const char *value, uint64_t length); - -extern bt_field_status bt_field_string_clear(bt_field *field); - -extern bt_field *bt_field_structure_borrow_member_field_by_index( - bt_field *field, uint64_t index); - -extern bt_field *bt_field_structure_borrow_member_field_by_name( - bt_field *field, const char *name); - -extern bt_field *bt_field_array_borrow_element_field_by_index( - bt_field *field, uint64_t index); - -extern bt_field_status bt_field_dynamic_array_set_length(bt_field *field, - uint64_t length); - -extern bt_field_status bt_field_variant_select_option_field( - bt_field *field, uint64_t index); - -extern bt_field *bt_field_variant_borrow_selected_option_field( - bt_field *field); diff --git a/bindings/python/bt2/bt2/native_bt_field_class.i b/bindings/python/bt2/bt2/native_bt_field_class.i deleted file mode 100644 index b6173c06..00000000 --- a/bindings/python/bt2/bt2/native_bt_field_class.i +++ /dev/null @@ -1,320 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -%typemap(in, numinputs=0) - (bt_field_class_enumeration_mapping_label_array *LABELARRAY, uint64_t *LABELCOUNT) - (bt_field_class_enumeration_mapping_label_array temp_array, uint64_t temp_label_count = 0) { - $1 = &temp_array; - $2 = &temp_label_count; -} - -%typemap(argout) - (bt_field_class_enumeration_mapping_label_array *LABELARRAY, uint64_t *LABELCOUNT) { - if (*$1) { - PyObject *py_label_list = PyList_New(*$2); - for (int i = 0; i < *$2; i++) { - PyList_SET_ITEM(py_label_list, i, PyUnicode_FromString((*$1)[i])); - } - - $result = SWIG_Python_AppendOutput($result, py_label_list); - } else { - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for value output (always appends) */ -%typemap(in, numinputs=0) - (const bt_field_class_signed_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) - (bt_field_class_signed_enumeration_mapping_ranges *temp_value = NULL) { - $1 = &temp_value; -} - -%typemap(argout) - (const bt_field_class_signed_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_field_class_signed_enumeration_mapping_ranges, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for value output (always appends) */ -%typemap(in, numinputs=0) - (const bt_field_class_unsigned_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) - (bt_field_class_unsigned_enumeration_mapping_ranges *temp_value = NULL) { - $1 = &temp_value; -} - -%typemap(argout) - (const bt_field_class_unsigned_enumeration_mapping_ranges **ENUM_RANGE_MAPPING ) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_field_class_unsigned_enumeration_mapping_ranges, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* From field-class-const.h */ - -typedef enum bt_field_class_status { - BT_FIELD_CLASS_STATUS_OK = 0, - BT_FIELD_CLASS_STATUS_NOMEM = -12, -} bt_field_class_status; - -typedef enum bt_field_class_type { - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER, - BT_FIELD_CLASS_TYPE_SIGNED_INTEGER, - BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, - BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, - BT_FIELD_CLASS_TYPE_REAL, - BT_FIELD_CLASS_TYPE_STRING, - BT_FIELD_CLASS_TYPE_STRUCTURE, - BT_FIELD_CLASS_TYPE_STATIC_ARRAY, - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, - BT_FIELD_CLASS_TYPE_VARIANT, -} bt_field_class_type; - -typedef enum bt_field_class_integer_preferred_display_base { - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL, -} bt_field_class_integer_preferred_display_base; - -extern bt_field_class_type bt_field_class_get_type( - const bt_field_class *field_class); - -extern uint64_t bt_field_class_integer_get_field_value_range( - const bt_field_class *field_class); - -extern bt_field_class_integer_preferred_display_base -bt_field_class_integer_get_preferred_display_base( - const bt_field_class *field_class); - -extern bt_bool bt_field_class_real_is_single_precision( - const bt_field_class *field_class); - -extern uint64_t bt_field_class_enumeration_get_mapping_count( - const bt_field_class *field_class); - -extern const bt_field_class_unsigned_enumeration_mapping * -bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( - const bt_field_class *field_class, uint64_t index); - -extern const bt_field_class_signed_enumeration_mapping * -bt_field_class_signed_enumeration_borrow_mapping_by_index_const( - const bt_field_class *field_class, uint64_t index); - -const bt_field_class_enumeration_mapping * -bt_field_class_unsigned_enumeration_mapping_as_mapping_const( - const bt_field_class_unsigned_enumeration_mapping *mapping); - -const bt_field_class_enumeration_mapping * -bt_field_class_signed_enumeration_mapping_as_mapping_const( - const bt_field_class_signed_enumeration_mapping *mapping); - -extern const char *bt_field_class_enumeration_mapping_get_label( - const bt_field_class_enumeration_mapping *mapping); - -extern uint64_t bt_field_class_enumeration_mapping_get_range_count( - const bt_field_class_enumeration_mapping *mapping); - -extern void -bt_field_class_unsigned_enumeration_mapping_get_range_by_index( - const bt_field_class_unsigned_enumeration_mapping *mapping, - uint64_t index, uint64_t *OUT, uint64_t *OUT); - -extern void -bt_field_class_signed_enumeration_mapping_get_range_by_index( - const bt_field_class_signed_enumeration_mapping *mapping, - uint64_t index, int64_t *OUT, int64_t *OUT); - -extern bt_field_class_status -bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( - const bt_field_class *field_class, uint64_t value, - bt_field_class_enumeration_mapping_label_array *LABELARRAY, - uint64_t *LABELCOUNT); - -extern bt_field_class_status -bt_field_class_signed_enumeration_get_mapping_labels_by_value( - const bt_field_class *field_class, int64_t value, - bt_field_class_enumeration_mapping_label_array *LABELARRAY, - uint64_t *LABELCOUNT); - -extern uint64_t bt_field_class_structure_get_member_count( - const bt_field_class *field_class); - -extern const bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_index_const( - const bt_field_class *field_class, uint64_t index); - -extern const bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_name_const( - const bt_field_class *field_class, const char *name); - -extern const char *bt_field_class_structure_member_get_name( - const bt_field_class_structure_member *member); - -extern const bt_field_class * -bt_field_class_structure_member_borrow_field_class_const( - const bt_field_class_structure_member *member); - -extern const bt_field_class * -bt_field_class_array_borrow_element_field_class_const( - const bt_field_class *field_class); - -extern uint64_t bt_field_class_static_array_get_length( - const bt_field_class *field_class); - -extern const bt_field_path * -bt_field_class_dynamic_array_borrow_length_field_path_const( - const bt_field_class *field_class); - -extern const bt_field_path * -bt_field_class_variant_borrow_selector_field_path_const( - const bt_field_class *field_class); - -extern uint64_t bt_field_class_variant_get_option_count( - const bt_field_class *field_class); - -extern const bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_index_const( - const bt_field_class *field_class, uint64_t index); - -extern const bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_name_const( - const bt_field_class *field_class, const char *name); - -extern const char *bt_field_class_variant_option_get_name( - const bt_field_class_variant_option *option); - -extern const bt_field_class * -bt_field_class_variant_option_borrow_field_class_const( - const bt_field_class_variant_option *option); - -extern void bt_field_class_get_ref(const bt_field_class *field_class); - -extern void bt_field_class_put_ref(const bt_field_class *field_class); - -/* From field-class.h */ - -extern bt_field_class *bt_field_class_unsigned_integer_create( - bt_trace_class *trace_class); - -extern bt_field_class *bt_field_class_signed_integer_create( - bt_trace_class *trace_class); - -extern void bt_field_class_integer_set_field_value_range( - bt_field_class *field_class, uint64_t size); - -extern void bt_field_class_integer_set_preferred_display_base( - bt_field_class *field_class, - bt_field_class_integer_preferred_display_base base); - -extern bt_field_class *bt_field_class_real_create(bt_trace_class *trace_class); - -extern void bt_field_class_real_set_is_single_precision( - bt_field_class *field_class, - bt_bool is_single_precision); - -extern bt_field_class *bt_field_class_unsigned_enumeration_create( - bt_trace_class *trace_class); - -extern bt_field_class *bt_field_class_signed_enumeration_create( - bt_trace_class *trace_class); - -extern bt_field_class_status bt_field_class_unsigned_enumeration_map_range( - bt_field_class *field_class, const char *label, - uint64_t range_lower, uint64_t range_upper); - -extern bt_field_class_status bt_field_class_signed_enumeration_map_range( - bt_field_class *field_class, const char *label, - int64_t range_lower, int64_t range_upper); - -extern bt_field_class *bt_field_class_string_create( - bt_trace_class *trace_class); - -extern bt_field_class *bt_field_class_structure_create( - bt_trace_class *trace_class); - -extern bt_field_class_status bt_field_class_structure_append_member( - bt_field_class *struct_field_class, - const char *name, bt_field_class *field_class); - -extern bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_index( - bt_field_class *field_class, uint64_t index); - -extern bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_name( - bt_field_class *field_class, const char *name); - -extern bt_field_class *bt_field_class_static_array_create( - bt_trace_class *trace_class, - bt_field_class *elem_field_class, uint64_t length); - -extern bt_field_class *bt_field_class_dynamic_array_create( - bt_trace_class *trace_class, - bt_field_class *elem_field_class); - -extern bt_field_class *bt_field_class_array_borrow_element_field_class( - bt_field_class *field_class); - -extern bt_field_class_status -bt_field_class_dynamic_array_set_length_field_class( - bt_field_class *field_class, - bt_field_class *length_field_class); - -extern bt_field_class *bt_field_class_variant_create( - bt_trace_class *trace_class); - -extern bt_field_class_status -bt_field_class_variant_set_selector_field_class(bt_field_class *field_class, - bt_field_class *selector_field_class); - -extern bt_field_class_status bt_field_class_variant_append_option( - bt_field_class *var_field_class, - const char *name, bt_field_class *field_class); - -extern bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_index( - bt_field_class *field_class, uint64_t index); - -extern bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_name( - bt_field_class *field_class, char *name); - -extern bt_field_class *bt_field_class_variant_option_borrow_field_class( - bt_field_class_variant_option *option); diff --git a/bindings/python/bt2/bt2/native_bt_field_path.i b/bindings/python/bt2/bt2/native_bt_field_path.i deleted file mode 100644 index 5369f442..00000000 --- a/bindings/python/bt2/bt2/native_bt_field_path.i +++ /dev/null @@ -1,56 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From field-path-const.h */ - -typedef enum bt_field_path_item_type { - BT_FIELD_PATH_ITEM_TYPE_INDEX, - BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT, -} bt_field_path_item_type; - -typedef enum bt_scope { - BT_SCOPE_PACKET_CONTEXT, - BT_SCOPE_EVENT_COMMON_CONTEXT, - BT_SCOPE_EVENT_SPECIFIC_CONTEXT, - BT_SCOPE_EVENT_PAYLOAD, -} bt_scope; - -extern bt_scope bt_field_path_get_root_scope( - const bt_field_path *field_path); - -extern uint64_t bt_field_path_get_item_count( - const bt_field_path *field_path); - -extern const bt_field_path_item *bt_field_path_borrow_item_by_index_const( - const bt_field_path *field_path, uint64_t index); - -extern bt_field_path_item_type bt_field_path_item_get_type( - const bt_field_path_item *field_path_item); - -extern uint64_t bt_field_path_item_index_get_index( - const bt_field_path_item *field_path_item); - -extern void bt_field_path_get_ref(const bt_field_path *field_path); - -extern void bt_field_path_put_ref(const bt_field_path *field_path); diff --git a/bindings/python/bt2/bt2/native_bt_graph.i b/bindings/python/bt2/bt2/native_bt_graph.i deleted file mode 100644 index bb7a5d13..00000000 --- a/bindings/python/bt2/bt2/native_bt_graph.i +++ /dev/null @@ -1,723 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* Output argument typemap for connection output (always appends) */ -%typemap(in, numinputs=0) - (const bt_connection **BTOUTCONN) - (bt_connection *temp_conn = NULL) { - $1 = &temp_conn; -} - -%typemap(argout) - (const bt_connection **BTOUTCONN) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_connection, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* Output argument typemap for component output (always appends) */ -%typemap(in, numinputs=0) - (const bt_component_source **OUT) - (bt_component_source *temp_comp = NULL) { - $1 = &temp_comp; -} - -%typemap(in, numinputs=0) - (const bt_component_filter **OUT) - (bt_component_filter *temp_comp = NULL) { - $1 = &temp_comp; -} - -%typemap(in, numinputs=0) - (const bt_component_sink **OUT) - (bt_component_sink *temp_comp = NULL) { - $1 = &temp_comp; -} - -%typemap(argout) (const bt_component_source **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_component_source, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -%typemap(argout) (const bt_component_filter **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_component_filter, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -%typemap(argout) (const bt_component_sink **OUT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_component_sink, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* From graph-const.h */ - -typedef enum bt_graph_status { - BT_GRAPH_STATUS_OK = 0, - BT_GRAPH_STATUS_END = 1, - BT_GRAPH_STATUS_AGAIN = 11, - BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION = 111, - BT_GRAPH_STATUS_CANCELED = 125, - BT_GRAPH_STATUS_ERROR = -1, - BT_GRAPH_STATUS_NOMEM = -12, -} bt_graph_status; - -extern bt_bool bt_graph_is_canceled(const bt_graph *graph); - -extern void bt_graph_get_ref(const bt_graph *graph); - -extern void bt_graph_put_ref(const bt_graph *graph); - -/* From graph.h */ - -typedef enum bt_graph_listener_status { - BT_GRAPH_LISTENER_STATUS_OK = 0, - BT_GRAPH_LISTENER_STATUS_ERROR = -1, - BT_GRAPH_LISTENER_STATUS_NOMEM = -12, -} bt_graph_listener_status; - - -typedef bt_graph_listener_status -(*bt_graph_filter_component_input_port_added_listener_func)( - const bt_component_filter *component, - const bt_port_input *port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_sink_component_input_port_added_listener_func)( - const bt_component_sink *component, - const bt_port_input *port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_source_component_output_port_added_listener_func)( - const bt_component_source *component, - const bt_port_output *port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_filter_component_output_port_added_listener_func)( - const bt_component_filter *component, - const bt_port_output *port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_source_filter_component_ports_connected_listener_func)( - const bt_component_source *source_component, - const bt_component_filter *filter_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_source_sink_component_ports_connected_listener_func)( - const bt_component_source *source_component, - const bt_component_sink *sink_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *data); - -typedef bt_graph_listener_status -(*bt_graph_filter_filter_component_ports_connected_listener_func)( - const bt_component_filter *filter_component_upstream, - const bt_component_filter *filter_component_downstream, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, - void *data); - -typedef bt_graph_listener_status -(*bt_graph_filter_sink_component_ports_connected_listener_func)( - const bt_component_filter *filter_component, - const bt_component_sink *sink_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *data); - -typedef void (* bt_graph_listener_removed_func)(void *data); - -extern bt_graph *bt_graph_create(void); - -extern bt_graph_status bt_graph_add_source_component(bt_graph *graph, - const bt_component_class_source *component_class, - const char *name, const bt_value *params, - const bt_component_source **OUT); - -extern bt_graph_status bt_graph_add_source_component_with_init_method_data( - bt_graph *graph, - const bt_component_class_source *component_class, - const char *name, const bt_value *params, - void *init_method_data, - const bt_component_source **OUT); - -extern bt_graph_status bt_graph_add_filter_component(bt_graph *graph, - const bt_component_class_filter *component_class, - const char *name, const bt_value *params, - const bt_component_filter **OUT); - -extern bt_graph_status bt_graph_add_filter_component_with_init_method_data( - bt_graph *graph, - const bt_component_class_filter *component_class, - const char *name, const bt_value *params, - void *init_method_data, - const bt_component_filter **OUT); - -extern bt_graph_status bt_graph_add_sink_component( - bt_graph *graph, const bt_component_class_sink *component_class, - const char *name, const bt_value *params, - const bt_component_sink **OUT); - -extern bt_graph_status bt_graph_add_sink_component_with_init_method_data( - bt_graph *graph, const bt_component_class_sink *component_class, - const char *name, const bt_value *params, - void *init_method_data, - const bt_component_sink **OUT); - -extern bt_graph_status bt_graph_connect_ports(bt_graph *graph, - const bt_port_output *upstream, - const bt_port_input *downstream, - const bt_connection **BTOUTCONN); - -extern bt_graph_status bt_graph_run(bt_graph *graph); - -extern bt_graph_status bt_graph_consume(bt_graph *graph); - -extern bt_graph_status bt_graph_add_filter_component_input_port_added_listener( - bt_graph *graph, - bt_graph_filter_component_input_port_added_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status bt_graph_add_sink_component_input_port_added_listener( - bt_graph *graph, - bt_graph_sink_component_input_port_added_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status bt_graph_add_source_component_output_port_added_listener( - bt_graph *graph, - bt_graph_source_component_output_port_added_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status bt_graph_add_filter_component_output_port_added_listener( - bt_graph *graph, - bt_graph_filter_component_output_port_added_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status -bt_graph_add_source_filter_component_ports_connected_listener( - bt_graph *graph, - bt_graph_source_filter_component_ports_connected_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status -bt_graph_add_filter_filter_component_ports_connected_listener( - bt_graph *graph, - bt_graph_filter_filter_component_ports_connected_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status -bt_graph_add_source_sink_component_ports_connected_listener( - bt_graph *graph, - bt_graph_source_sink_component_ports_connected_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status -bt_graph_add_filter_sink_component_ports_connected_listener( - bt_graph *graph, - bt_graph_filter_sink_component_ports_connected_listener_func listener, - bt_graph_listener_removed_func listener_removed, void *data, - int *listener_id); - -extern bt_graph_status bt_graph_cancel(bt_graph *graph); - -/* Helper functions for Python */ - -%{ - -static void graph_listener_removed(void *py_callable) -{ - BT_ASSERT(py_callable); - Py_DECREF(py_callable); -} - -static bt_graph_listener_status -port_added_listener( - const void *component, - swig_type_info *component_swig_type, - bt_component_class_type component_class_type, - const void *port, - swig_type_info *port_swig_type, - bt_port_type port_type, - void *py_callable) -{ - PyObject *py_component_ptr = NULL; - PyObject *py_port_ptr = NULL; - PyObject *py_res = NULL; - bt_graph_listener_status status; - - py_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component), component_swig_type, 0); - if (!py_component_ptr) { - BT_LOGF_STR("Failed to create component SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(port), port_swig_type, 0); - if (!py_port_ptr) { - BT_LOGF_STR("Failed to create port SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_res = PyObject_CallFunction(py_callable, "(OiOi)", - py_component_ptr, component_class_type, py_port_ptr, port_type); - if (!py_res) { - bt2_py_loge_exception(); - PyErr_Clear(); - status = BT_GRAPH_LISTENER_STATUS_ERROR; - goto end; - } - - BT_ASSERT(py_res == Py_None); - status = BT_GRAPH_LISTENER_STATUS_OK; - -end: - Py_XDECREF(py_res); - Py_XDECREF(py_port_ptr); - Py_XDECREF(py_component_ptr); - - return status; -} - -static bt_graph_listener_status -source_component_output_port_added_listener(const bt_component_source *component_source, - const bt_port_output *port_output, void *py_callable) -{ - return port_added_listener( - component_source, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, - port_output, SWIGTYPE_p_bt_port_output, BT_PORT_TYPE_OUTPUT, py_callable); -} - -static bt_graph_listener_status -filter_component_input_port_added_listener(const bt_component_filter *component_filter, - const bt_port_input *port_input, void *py_callable) -{ - return port_added_listener( - component_filter, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - port_input, SWIGTYPE_p_bt_port_input, BT_PORT_TYPE_INPUT, py_callable); -} - -static bt_graph_listener_status -filter_component_output_port_added_listener(const bt_component_filter *component_filter, - const bt_port_output *port_output, void *py_callable) -{ - return port_added_listener( - component_filter, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - port_output, SWIGTYPE_p_bt_port_output, BT_PORT_TYPE_OUTPUT, py_callable); -} - -static bt_graph_listener_status -sink_component_input_port_added_listener(const bt_component_sink *component_sink, - const bt_port_input *port_input, void *py_callable) -{ - return port_added_listener( - component_sink, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, - port_input, SWIGTYPE_p_bt_port_input, BT_PORT_TYPE_INPUT, py_callable); -} - -static PyObject * -bt_py3_graph_add_port_added_listener(struct bt_graph *graph, - PyObject *py_callable) -{ - PyObject *py_listener_ids = NULL; - PyObject *py_listener_id = NULL; - int listener_id; - bt_graph_status status; - - BT_ASSERT(graph); - BT_ASSERT(py_callable); - - /* - * Behind the scene, we will be registering 4 different listeners and - * return all of their ids. - */ - py_listener_ids = PyTuple_New(4); - if (!py_listener_ids) { - goto error; - } - - /* source output port */ - status = bt_graph_add_source_component_output_port_added_listener( - graph, source_component_output_port_added_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 0, py_listener_id); - py_listener_id = NULL; - - /* filter input port */ - status = bt_graph_add_filter_component_input_port_added_listener( - graph, filter_component_input_port_added_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 1, py_listener_id); - py_listener_id = NULL; - - /* filter output port */ - status = bt_graph_add_filter_component_output_port_added_listener( - graph, filter_component_output_port_added_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 2, py_listener_id); - py_listener_id = NULL; - - /* sink input port */ - status = bt_graph_add_sink_component_input_port_added_listener( - graph, sink_component_input_port_added_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - - PyTuple_SET_ITEM(py_listener_ids, 3, py_listener_id); - py_listener_id = NULL; - - Py_INCREF(py_callable); - Py_INCREF(py_callable); - Py_INCREF(py_callable); - Py_INCREF(py_callable); - - goto end; - -error: - Py_XDECREF(py_listener_ids); - py_listener_ids = Py_None; - Py_INCREF(py_listener_ids); - -end: - - Py_XDECREF(py_listener_id); - return py_listener_ids; -} - -static bt_graph_listener_status -ports_connected_listener( - const void *upstream_component, - swig_type_info *upstream_component_swig_type, - bt_component_class_type upstream_component_class_type, - const bt_port_output *upstream_port, - const void *downstream_component, - swig_type_info *downstream_component_swig_type, - bt_component_class_type downstream_component_class_type, - const bt_port_input *downstream_port, - void *py_callable) -{ - PyObject *py_upstream_component_ptr = NULL; - PyObject *py_upstream_port_ptr = NULL; - PyObject *py_downstream_component_ptr = NULL; - PyObject *py_downstream_port_ptr = NULL; - PyObject *py_res = NULL; - bt_graph_listener_status status; - - py_upstream_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(upstream_component), - upstream_component_swig_type, 0); - if (!py_upstream_component_ptr) { - BT_LOGF_STR("Failed to create upstream component SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_upstream_port_ptr = SWIG_NewPointerObj( - SWIG_as_voidptr(upstream_port), SWIGTYPE_p_bt_port_output, 0); - if (!py_upstream_port_ptr) { - BT_LOGF_STR("Failed to create upstream port SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_downstream_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(downstream_component), - downstream_component_swig_type, 0); - if (!py_downstream_component_ptr) { - BT_LOGF_STR("Failed to create downstream component SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_downstream_port_ptr = SWIG_NewPointerObj( - SWIG_as_voidptr(downstream_port), SWIGTYPE_p_bt_port_input, 0); - if (!py_downstream_port_ptr) { - BT_LOGF_STR("Failed to create downstream port SWIG pointer object."); - status = BT_GRAPH_LISTENER_STATUS_NOMEM; - goto end; - } - - py_res = PyObject_CallFunction(py_callable, "(OiOOiO)", - py_upstream_component_ptr, upstream_component_class_type, - py_upstream_port_ptr, - py_downstream_component_ptr, downstream_component_class_type, - py_downstream_port_ptr); - if (!py_res) { - bt2_py_loge_exception(); - PyErr_Clear(); - status = BT_GRAPH_LISTENER_STATUS_ERROR; - goto end; - } - - BT_ASSERT(py_res == Py_None); - status = BT_GRAPH_LISTENER_STATUS_OK; - -end: - Py_XDECREF(py_upstream_component_ptr); - Py_XDECREF(py_upstream_port_ptr); - Py_XDECREF(py_downstream_component_ptr); - Py_XDECREF(py_downstream_port_ptr); - Py_XDECREF(py_res); - - return status; -} - -static bt_graph_listener_status -source_filter_component_ports_connected_listener( - const bt_component_source *source_component, - const bt_component_filter *filter_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *py_callable) -{ - return ports_connected_listener( - source_component, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, - upstream_port, - filter_component, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - downstream_port, - py_callable); -} - -static bt_graph_listener_status -source_sink_component_ports_connected_listener( - const bt_component_source *source_component, - const bt_component_sink *sink_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *py_callable) -{ - return ports_connected_listener( - source_component, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, - upstream_port, - sink_component, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, - downstream_port, - py_callable); -} - -static bt_graph_listener_status -filter_filter_component_ports_connected_listener( - const bt_component_filter *filter_component_left, - const bt_component_filter *filter_component_right, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *py_callable) -{ - return ports_connected_listener( - filter_component_left, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - upstream_port, - filter_component_right, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - downstream_port, - py_callable); -} - -static bt_graph_listener_status -filter_sink_component_ports_connected_listener( - const bt_component_filter *filter_component, - const bt_component_sink *sink_component, - const bt_port_output *upstream_port, - const bt_port_input *downstream_port, void *py_callable) -{ - return ports_connected_listener( - filter_component, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, - upstream_port, - sink_component, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, - downstream_port, - py_callable); -} - -static PyObject* -bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, - PyObject *py_callable) -{ - PyObject *py_listener_ids = NULL; - PyObject *py_listener_id = NULL; - int listener_id; - bt_graph_status status; - - BT_ASSERT(graph); - BT_ASSERT(py_callable); - - /* Behind the scene, we will be registering 4 different listeners and - * return all of their ids. */ - py_listener_ids = PyTuple_New(4); - if (!py_listener_ids) { - goto error; - } - - /* source -> filter connection */ - status = bt_graph_add_source_filter_component_ports_connected_listener( - graph, source_filter_component_ports_connected_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 0, py_listener_id); - py_listener_id = NULL; - - /* source -> sink connection */ - status = bt_graph_add_source_sink_component_ports_connected_listener( - graph, source_sink_component_ports_connected_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 1, py_listener_id); - py_listener_id = NULL; - - /* filter -> filter connection */ - status = bt_graph_add_filter_filter_component_ports_connected_listener( - graph, filter_filter_component_ports_connected_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 2, py_listener_id); - py_listener_id = NULL; - - /* filter -> sink connection */ - status = bt_graph_add_filter_sink_component_ports_connected_listener( - graph, filter_sink_component_ports_connected_listener, - graph_listener_removed, py_callable, &listener_id); - if (status != BT_GRAPH_STATUS_OK) { - goto error; - } - - py_listener_id = PyLong_FromLong(listener_id); - if (!py_listener_id) { - goto error; - } - - PyTuple_SET_ITEM(py_listener_ids, 3, py_listener_id); - py_listener_id = NULL; - - Py_INCREF(py_callable); - Py_INCREF(py_callable); - Py_INCREF(py_callable); - Py_INCREF(py_callable); - - goto end; - -error: - Py_XDECREF(py_listener_ids); - py_listener_ids = Py_None; - Py_INCREF(py_listener_ids); - -end: - - Py_XDECREF(py_listener_id); - return py_listener_ids; -} - -%} - -PyObject *bt_py3_graph_add_port_added_listener(struct bt_graph *graph, - PyObject *py_callable); -PyObject *bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, - PyObject *py_callable); diff --git a/bindings/python/bt2/bt2/native_bt_logging.i b/bindings/python/bt2/bt2/native_bt_logging.i deleted file mode 100644 index df364bdf..00000000 --- a/bindings/python/bt2/bt2/native_bt_logging.i +++ /dev/null @@ -1,43 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -%{ -#include -%} - -/* Log levels */ -enum bt_logging_level { - BT_LOGGING_LEVEL_VERBOSE = 1, - BT_LOGGING_LEVEL_DEBUG = 2, - BT_LOGGING_LEVEL_INFO = 3, - BT_LOGGING_LEVEL_WARN = 4, - BT_LOGGING_LEVEL_ERROR = 5, - BT_LOGGING_LEVEL_FATAL = 6, - BT_LOGGING_LEVEL_NONE = 0xff, -}; - -/* Logging functions */ -enum bt_logging_level bt_logging_get_minimal_level(void); -enum bt_logging_level bt_logging_get_global_level(void); -void bt_logging_set_global_level(enum bt_logging_level log_level); diff --git a/bindings/python/bt2/bt2/native_bt_message.i b/bindings/python/bt2/bt2/native_bt_message.i deleted file mode 100644 index 0c81b938..00000000 --- a/bindings/python/bt2/bt2/native_bt_message.i +++ /dev/null @@ -1,337 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -/* Output argument typemap for clock value output (always appends) */ -%typemap(in, numinputs=0) - (const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT) - (bt_clock_snapshot *temp_clock_snapshot = NULL) { - $1 = &temp_clock_snapshot; -} - -%typemap(argout) - (const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT) { - if (*$1) { - /* SWIG_Python_AppendOutput() steals the created object */ - $result = SWIG_Python_AppendOutput($result, - SWIG_NewPointerObj(SWIG_as_voidptr(*$1), - SWIGTYPE_p_bt_clock_snapshot, 0)); - } else { - /* SWIG_Python_AppendOutput() steals Py_None */ - Py_INCREF(Py_None); - $result = SWIG_Python_AppendOutput($result, Py_None); - } -} - -/* From message-const.h */ - -typedef enum bt_message_type { - BT_MESSAGE_TYPE_EVENT = 0, - BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY = 1, - BT_MESSAGE_TYPE_STREAM_BEGINNING = 2, - BT_MESSAGE_TYPE_STREAM_END = 3, - BT_MESSAGE_TYPE_PACKET_BEGINNING = 4, - BT_MESSAGE_TYPE_PACKET_END = 5, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING = 6, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_END = 7, - BT_MESSAGE_TYPE_DISCARDED_EVENTS = 8, - BT_MESSAGE_TYPE_DISCARDED_PACKETS = 9, -} bt_message_type; - -extern bt_message_type bt_message_get_type(const bt_message *message); - -extern void bt_message_get_ref(const bt_message *message); - -extern void bt_message_put_ref(const bt_message *message); - -/* From message-event-const.h */ - -extern const bt_event *bt_message_event_borrow_event_const( - const bt_message *message); - -extern const bt_clock_snapshot * -bt_message_event_borrow_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_class * -bt_message_event_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -/* From message-event.h */ - -extern -bt_message *bt_message_event_create( - bt_self_message_iterator *message_iterator, - const bt_event_class *event_class, - const bt_packet *packet); - -extern -bt_message *bt_message_event_create_with_default_clock_snapshot( - bt_self_message_iterator *message_iterator, - const bt_event_class *event_class, - const bt_packet *packet, uint64_t raw_clock_value); - -extern bt_event *bt_message_event_borrow_event( - bt_message *message); - -/* From message-message-iterator-inactivity-const.h */ - -extern const bt_clock_snapshot * -bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - const bt_message *msg); - -/* From message-message-iterator-inactivity.h */ - -extern -bt_message *bt_message_message_iterator_inactivity_create( - bt_self_message_iterator *message_iterator, - const bt_clock_class *default_clock_class, uint64_t raw_value); - -/* From message-stream-beginning-const.h */ - -extern const bt_stream *bt_message_stream_beginning_borrow_stream_const( - const bt_message *message); - -/* From message-stream-beginning.h */ - -extern -bt_message *bt_message_stream_beginning_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern bt_stream *bt_message_stream_beginning_borrow_stream( - bt_message *message); - -/* From message-stream-end-const.h */ - -extern const bt_stream *bt_message_stream_end_borrow_stream_const( - const bt_message *message); - -/* From message-stream-end.h */ - -extern -bt_message *bt_message_stream_end_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern bt_stream *bt_message_stream_end_borrow_stream( - bt_message *message); - -/* From message-packet-beginning-const.h */ - -extern const bt_packet *bt_message_packet_beginning_borrow_packet_const( - const bt_message *message); - -extern const bt_clock_snapshot * -bt_message_packet_beginning_borrow_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_class * -bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -/* From message-packet-beginning.h */ - -extern -bt_message *bt_message_packet_beginning_create( - bt_self_message_iterator *message_iterator, - const bt_packet *packet); - -extern -bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot( - bt_self_message_iterator *message_iterator, - const bt_packet *packet, uint64_t raw_value); - -extern bt_packet *bt_message_packet_beginning_borrow_packet( - bt_message *message); - -/* From message-packet-end-const.h */ - -extern const bt_packet *bt_message_packet_end_borrow_packet_const( - const bt_message *message); - -extern const bt_clock_snapshot * -bt_message_packet_end_borrow_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_class * -bt_message_packet_end_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -/* From message-packet-end.h */ - -extern -bt_message *bt_message_packet_end_create( - bt_self_message_iterator *message_iterator, - const bt_packet *packet); - -extern -bt_message *bt_message_packet_end_create_with_default_clock_snapshot( - bt_self_message_iterator *message_iterator, - const bt_packet *packet, uint64_t raw_value); - -extern bt_packet *bt_message_packet_end_borrow_packet( - bt_message *message); - -/* From message-stream-activity-const.h */ - -typedef enum bt_message_stream_activity_clock_snapshot_state { - BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN, - BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN, - BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE, -} bt_message_stream_activity_clock_snapshot_state; - -/* From message-stream-activity-beginning-const.h */ - -extern bt_message_stream_activity_clock_snapshot_state -bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - const bt_message *msg, const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT); - -extern const bt_clock_class * -bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -extern const bt_stream * -bt_message_stream_activity_beginning_borrow_stream_const( - const bt_message *message); - -/* From message-stream-activity-beginning.h */ - -extern bt_message *bt_message_stream_activity_beginning_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern bt_stream *bt_message_stream_activity_beginning_borrow_stream( - bt_message *message); - -extern void bt_message_stream_activity_beginning_set_default_clock_snapshot_state( - bt_message *msg, - bt_message_stream_activity_clock_snapshot_state state); - -extern void bt_message_stream_activity_beginning_set_default_clock_snapshot( - bt_message *msg, uint64_t raw_value); - -/* From message-stream-activity-end-const.h */ - -extern bt_message_stream_activity_clock_snapshot_state -bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - const bt_message *msg, const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT); - -extern const bt_clock_class * -bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -extern const bt_stream * -bt_message_stream_activity_end_borrow_stream_const( - const bt_message *message); - -/* From message-stream-activity-end.h */ - -extern bt_message *bt_message_stream_activity_end_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern void bt_message_stream_activity_end_set_default_clock_snapshot_state( - bt_message *msg, - bt_message_stream_activity_clock_snapshot_state state); - -extern void bt_message_stream_activity_end_set_default_clock_snapshot( - bt_message *msg, uint64_t raw_value); - -extern bt_stream *bt_message_stream_activity_end_borrow_stream( - bt_message *message); - -/* From message-discarded-events-const.h */ - -extern const bt_clock_snapshot * -bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_snapshot * -bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_class * -bt_message_discarded_events_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -extern const bt_stream * -bt_message_discarded_events_borrow_stream_const(const bt_message *message); - -extern bt_property_availability bt_message_discarded_events_get_count( - const bt_message *message, uint64_t *OUT); - -/* From message-discarded-events.h */ - -extern bt_message *bt_message_discarded_events_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern bt_message *bt_message_discarded_events_create_with_default_clock_snapshots( - bt_self_message_iterator *message_iterator, - const bt_stream *stream, uint64_t beginning_raw_value, - uint64_t end_raw_value); - -extern bt_stream *bt_message_discarded_events_borrow_stream( - bt_message *message); - -extern void bt_message_discarded_events_set_count(bt_message *message, - uint64_t count); - -/* From message-discarded-packets-const.h */ - -extern const bt_clock_snapshot * -bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_snapshot * -bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - const bt_message *msg); - -extern const bt_clock_class * -bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( - const bt_message *msg); - -extern const bt_stream * -bt_message_discarded_packets_borrow_stream_const(const bt_message *message); - -extern bt_property_availability bt_message_discarded_packets_get_count( - const bt_message *message, uint64_t *OUT); - -/* From message-discarded-packets.h */ - -extern bt_message *bt_message_discarded_packets_create( - bt_self_message_iterator *message_iterator, - const bt_stream *stream); - -extern bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots( - bt_self_message_iterator *message_iterator, - const bt_stream *stream, uint64_t beginning_raw_value, - uint64_t end_raw_value); - -extern bt_stream *bt_message_discarded_packets_borrow_stream( - bt_message *message); - -extern void bt_message_discarded_packets_set_count(bt_message *message, - uint64_t count); diff --git a/bindings/python/bt2/bt2/native_bt_notifier.i b/bindings/python/bt2/bt2/native_bt_notifier.i deleted file mode 100644 index b5f53900..00000000 --- a/bindings/python/bt2/bt2/native_bt_notifier.i +++ /dev/null @@ -1,232 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From message-iterator-const.h */ - -typedef enum bt_message_iterator_status { - BT_MESSAGE_ITERATOR_STATUS_OK = 0, - BT_MESSAGE_ITERATOR_STATUS_END = 1, - BT_MESSAGE_ITERATOR_STATUS_AGAIN = 11, - BT_MESSAGE_ITERATOR_STATUS_ERROR = -1, - BT_MESSAGE_ITERATOR_STATUS_NOMEM = -12, -} bt_message_iterator_status; - -/* From self-message-iterator.h */ - -typedef enum bt_self_message_iterator_status { - BT_SELF_MESSAGE_ITERATOR_STATUS_OK = BT_MESSAGE_ITERATOR_STATUS_OK, - BT_SELF_MESSAGE_ITERATOR_STATUS_END = BT_MESSAGE_ITERATOR_STATUS_END, - BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN = BT_MESSAGE_ITERATOR_STATUS_AGAIN, - BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR = BT_MESSAGE_ITERATOR_STATUS_ERROR, - BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM = BT_MESSAGE_ITERATOR_STATUS_NOMEM, -} bt_self_message_iterator_status; - -extern bt_self_component * -bt_self_message_iterator_borrow_component( - bt_self_message_iterator *message_iterator); - -extern bt_self_port_output * -bt_self_message_iterator_borrow_port( - bt_self_message_iterator *message_iterator); - -extern void bt_self_message_iterator_set_data( - bt_self_message_iterator *message_iterator, - void *user_data); - -extern void *bt_self_message_iterator_get_data( - const bt_self_message_iterator *message_iterator); - -/* From self-component-port-input-message-iterator.h */ - -bt_message_iterator * -bt_self_component_port_input_message_iterator_as_message_iterator( - bt_self_component_port_input_message_iterator *iterator); - -extern bt_self_component_port_input_message_iterator * -bt_self_component_port_input_message_iterator_create( - bt_self_component_port_input *input_port); - -extern bt_component * -bt_self_component_port_input_message_iterator_borrow_component( - bt_self_component_port_input_message_iterator *iterator); - -extern bt_message_iterator_status -bt_self_component_port_input_message_iterator_next( - bt_self_component_port_input_message_iterator *iterator, - bt_message_array_const *msgs, uint64_t *count); - -extern bt_bool -bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( - bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin); - -extern bt_bool bt_self_component_port_input_message_iterator_can_seek_beginning( - bt_self_component_port_input_message_iterator *iterator); - -extern bt_message_iterator_status -bt_self_component_port_input_message_iterator_seek_ns_from_origin( - bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin); - -extern bt_message_iterator_status -bt_self_component_port_input_message_iterator_seek_beginning( - bt_self_component_port_input_message_iterator *iterator); - -extern void bt_self_component_port_input_message_iterator_get_ref( - const bt_self_component_port_input_message_iterator *self_component_port_input_message_iterator); - -extern void bt_self_component_port_input_message_iterator_put_ref( - const bt_self_component_port_input_message_iterator *self_component_port_input_message_iterator); - -/* From port-output-message-iterator.h */ - -bt_message_iterator * -bt_port_output_message_iterator_as_message_iterator( - bt_port_output_message_iterator *iterator); - -extern bt_port_output_message_iterator * -bt_port_output_message_iterator_create( - bt_graph *graph, - const bt_port_output *output_port); - -extern bt_message_iterator_status -bt_port_output_message_iterator_next( - bt_port_output_message_iterator *iterator, - bt_message_array_const *msgs, uint64_t *count); - -extern bt_bool bt_port_output_message_iterator_can_seek_ns_from_origin( - bt_port_output_message_iterator *iterator, - int64_t ns_from_origin); - -extern bt_bool bt_port_output_message_iterator_can_seek_beginning( - bt_port_output_message_iterator *iterator); - -extern bt_message_iterator_status -bt_port_output_message_iterator_seek_ns_from_origin( - bt_port_output_message_iterator *iterator, - int64_t ns_from_origin); - -extern bt_message_iterator_status -bt_port_output_message_iterator_seek_beginning( - bt_port_output_message_iterator *iterator); - -extern void bt_port_output_message_iterator_get_ref( - const bt_port_output_message_iterator *port_output_message_iterator); - -extern void bt_port_output_message_iterator_put_ref( - const bt_port_output_message_iterator *port_output_message_iterator); - -/* Helper functions for Python */ -%{ -static PyObject *bt_py3_get_user_component_from_user_msg_iter( - bt_self_message_iterator *self_message_iterator) -{ - bt_self_component *self_component = bt_self_message_iterator_borrow_component(self_message_iterator); - PyObject *py_comp; - - BT_ASSERT(self_component); - py_comp = bt_self_component_get_data(self_component); - BT_ASSERT(py_comp); - - /* Return new reference */ - Py_INCREF(py_comp); - return py_comp; -} - -static inline -PyObject *create_pylist_from_messages(bt_message_array_const messages, - uint64_t message_count) -{ - uint64_t i; - PyObject *py_msg_list = PyList_New(message_count); - BT_ASSERT(py_msg_list); - for (i = 0; i < message_count; i++) { - PyList_SET_ITEM(py_msg_list, i, - SWIG_NewPointerObj(SWIG_as_voidptr(messages[i]), - SWIGTYPE_p_bt_message, 0)); - } - - return py_msg_list; -} - -static -PyObject *bt_py3_get_msg_range_common(bt_message_iterator_status status, - bt_message_array_const messages, uint64_t message_count) -{ - PyObject *py_status; - PyObject *py_return_tuple; - PyObject *py_msg_list = Py_None; - - py_status = SWIG_From_long_SS_long(status); - if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - py_msg_list = create_pylist_from_messages(messages, message_count); - -end: - py_return_tuple = PyTuple_New(2); - BT_ASSERT(py_return_tuple); - PyTuple_SET_ITEM(py_return_tuple, 0, py_status); - PyTuple_SET_ITEM(py_return_tuple, 1, py_msg_list); - - return py_return_tuple; -} - -static PyObject -*bt_py3_self_component_port_input_get_msg_range( - bt_self_component_port_input_message_iterator *iter) -{ - bt_message_array_const messages; - uint64_t message_count = 0; - bt_message_iterator_status status; - - status = bt_self_component_port_input_message_iterator_next(iter, &messages, - &message_count); - - return bt_py3_get_msg_range_common(status, messages, message_count); -} - -static PyObject -*bt_py3_port_output_get_msg_range( - bt_port_output_message_iterator *iter) -{ - bt_message_array_const messages; - uint64_t message_count = 0; - bt_message_iterator_status status; - - status = - bt_port_output_message_iterator_next(iter, &messages, - &message_count); - - return bt_py3_get_msg_range_common(status, messages, message_count); -} -%} - -PyObject *bt_py3_get_user_component_from_user_msg_iter( - bt_self_message_iterator *self_message_iterator); -PyObject *bt_py3_self_component_port_input_get_msg_range( - bt_self_component_port_input_message_iterator *iter); -PyObject *bt_py3_port_output_get_msg_range( - bt_port_output_message_iterator *iter); diff --git a/bindings/python/bt2/bt2/native_bt_packet.i b/bindings/python/bt2/bt2/native_bt_packet.i deleted file mode 100644 index b4c90735..00000000 --- a/bindings/python/bt2/bt2/native_bt_packet.i +++ /dev/null @@ -1,54 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From packet-const.h */ - -typedef enum bt_packet_status { - BT_PACKET_STATUS_OK = 0, - BT_PACKET_STATUS_NOMEM = -12, -} bt_packet_status; - -extern const bt_stream *bt_packet_borrow_stream_const( - const bt_packet *packet); - -extern -const bt_field *bt_packet_borrow_context_field_const( - const bt_packet *packet); - -extern void bt_packet_get_ref(const bt_packet *packet); - -extern void bt_packet_put_ref(const bt_packet *packet); - -/* From packet.h */ - -extern bt_packet *bt_packet_create(const bt_stream *stream); - -extern bt_stream *bt_packet_borrow_stream(bt_packet *packet); - -extern -bt_field *bt_packet_borrow_context_field(bt_packet *packet); - -extern -bt_packet_status bt_packet_move_context_field(bt_packet *packet, - bt_packet_context_field *context); diff --git a/bindings/python/bt2/bt2/native_bt_plugin.i b/bindings/python/bt2/bt2/native_bt_plugin.i deleted file mode 100644 index 435d40f2..00000000 --- a/bindings/python/bt2/bt2/native_bt_plugin.i +++ /dev/null @@ -1,131 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From plugin-const.h */ - -extern const bt_plugin *bt_plugin_find(const char *plugin_name); - -extern const bt_plugin_set *bt_plugin_find_all_from_file( - const char *path); - -extern const bt_plugin_set *bt_plugin_find_all_from_dir( - const char *path, bt_bool recurse); - -extern const bt_plugin_set *bt_plugin_find_all_from_static(void); - -extern const char *bt_plugin_get_name(const bt_plugin *plugin); - -extern const char *bt_plugin_get_author(const bt_plugin *plugin); - -extern const char *bt_plugin_get_license(const bt_plugin *plugin); - -extern const char *bt_plugin_get_description(const bt_plugin *plugin); - -extern const char *bt_plugin_get_path(const bt_plugin *plugin); - -extern bt_property_availability bt_plugin_get_version( - const bt_plugin *plugin, unsigned int *OUT, - unsigned int *OUT, unsigned int *OUT, const char **OUT); - -extern uint64_t bt_plugin_get_source_component_class_count( - const bt_plugin *plugin); - -extern uint64_t bt_plugin_get_filter_component_class_count( - const bt_plugin *plugin); - -extern uint64_t bt_plugin_get_sink_component_class_count( - const bt_plugin *plugin); - -extern const bt_component_class_source * -bt_plugin_borrow_source_component_class_by_index_const( - const bt_plugin *plugin, uint64_t index); - -extern const bt_component_class_filter * -bt_plugin_borrow_filter_component_class_by_index_const( - const bt_plugin *plugin, uint64_t index); - -extern const bt_component_class_sink * -bt_plugin_borrow_sink_component_class_by_index_const( - const bt_plugin *plugin, uint64_t index); - -extern const bt_component_class_source * -bt_plugin_borrow_source_component_class_by_name_const( - const bt_plugin *plugin, const char *name); - -extern const bt_component_class_filter * -bt_plugin_borrow_filter_component_class_by_name_const( - const bt_plugin *plugin, const char *name); - -extern const bt_component_class_sink * -bt_plugin_borrow_sink_component_class_by_name_const( - const bt_plugin *plugin, const char *name); - -extern void bt_plugin_get_ref(const bt_plugin *plugin); - -extern void bt_plugin_put_ref(const bt_plugin *plugin); - -/* From plugin-set-const.h */ - -extern uint64_t bt_plugin_set_get_plugin_count( - const bt_plugin_set *plugin_set); - -extern const bt_plugin *bt_plugin_set_borrow_plugin_by_index_const( - const bt_plugin_set *plugin_set, uint64_t index); - -extern void bt_plugin_set_get_ref(const bt_plugin_set *plugin_set); - -extern void bt_plugin_set_put_ref(const bt_plugin_set *plugin_set); - -/* Helpers */ - -bt_property_availability bt_plugin_get_version_wrapper( - const bt_plugin *plugin, unsigned int *OUT, - unsigned int *OUT, unsigned int *OUT, const char **OUT); - -%{ - -/* - * This wrapper ensures that when the API function fails, the `*extra` output - * parameter is set to NULL. This is necessary because the epilogue of the - * "char **OUT" typemap will use that value to make a Python str object. We - * can't rely on the initial value of `*extra`, it could point to unreadable - * memory. - */ - -bt_property_availability bt_plugin_get_version_wrapper( - const bt_plugin *plugin, unsigned int *major, - unsigned int *minor, unsigned int *patch, const char **extra) -{ - bt_property_availability ret; - - ret = bt_plugin_get_version(plugin, major, minor, patch, extra); - - if (ret == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { - *extra = NULL; - } - - return ret; -} - -%} diff --git a/bindings/python/bt2/bt2/native_bt_port.i b/bindings/python/bt2/bt2/native_bt_port.i deleted file mode 100644 index 873c6136..00000000 --- a/bindings/python/bt2/bt2/native_bt_port.i +++ /dev/null @@ -1,116 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * Typemap for the user data attached to (and owned by) a self component port. - * The pointer saved as the port's user data is directly the PyObject *. - * - * As per the CPython calling convention, we need to return a new reference to - * the returned object, which will be transferred to the caller. The following - * typedef allows us to apply the typemap. - */ -%{ -typedef void *PY_SELF_PORT_USER_DATA; -%} - -%typemap(out) PY_SELF_PORT_USER_DATA { - Py_INCREF($1); - $result = $1; -} - -/* From port-const.h */ - -typedef enum bt_port_type { - BT_PORT_TYPE_INPUT = 0, - BT_PORT_TYPE_OUTPUT = 1, -} bt_port_type; - -extern const char *bt_port_get_name(const bt_port *port); - -extern bt_port_type bt_port_get_type(const bt_port *port); - -extern const bt_connection *bt_port_borrow_connection_const( - const bt_port *port); - -extern const bt_component *bt_port_borrow_component_const( - const bt_port *port); - -extern bt_bool bt_port_is_connected(const bt_port *port); - -bt_bool bt_port_is_input(const bt_port *port); - -bt_bool bt_port_is_output(const bt_port *port); - -extern void bt_port_get_ref(const bt_port *port); - -extern void bt_port_put_ref(const bt_port *port); - -/* From port-output-const.h */ - -const bt_port *bt_port_output_as_port_const(const bt_port_output *port_output); - -extern void bt_port_output_get_ref(const bt_port_output *port_output); - -extern void bt_port_output_put_ref(const bt_port_output *port_output); - -/* From port-input-const.h */ - -const bt_port *bt_port_input_as_port_const(const bt_port_input *port_input); - -extern void bt_port_input_get_ref(const bt_port_input *port_input); - -extern void bt_port_input_put_ref(const bt_port_input *port_input); - -/* From self-component-port.h */ - -typedef enum bt_self_component_port_status { - BT_SELF_PORT_STATUS_OK = 0, -} bt_self_component_port_status; - -const bt_port *bt_self_component_port_as_port( - bt_self_component_port *self_port); - -extern bt_self_component *bt_self_component_port_borrow_component( - bt_self_component_port *self_port); - -extern PY_SELF_PORT_USER_DATA bt_self_component_port_get_data( - const bt_self_component_port *self_port); - -/* From self-component-port-output.h */ - -bt_self_component_port * -bt_self_component_port_output_as_self_component_port( - bt_self_component_port_output *self_component_port); - -const bt_port_output *bt_self_component_port_output_as_port_output( - bt_self_component_port_output *self_component_port); - -/* From self-component-port-input.h */ - -bt_self_component_port * -bt_self_component_port_input_as_self_component_port( - bt_self_component_port_input *self_component_port); - -const bt_port_input *bt_self_component_port_input_as_port_input( - const bt_self_component_port_input *self_component_port); diff --git a/bindings/python/bt2/bt2/native_bt_query_exec.i b/bindings/python/bt2/bt2/native_bt_query_exec.i deleted file mode 100644 index af78efaf..00000000 --- a/bindings/python/bt2/bt2/native_bt_query_exec.i +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From query-executor-const.h */ - -typedef enum bt_query_executor_status { - BT_QUERY_EXECUTOR_STATUS_OK = 0, - BT_QUERY_EXECUTOR_STATUS_AGAIN = 11, - BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED = 95, - BT_QUERY_EXECUTOR_STATUS_CANCELED = 125, - BT_QUERY_EXECUTOR_STATUS_ERROR = -1, - BT_QUERY_EXECUTOR_STATUS_NOMEM = -12, - BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT = -23, - BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS = -24, -} bt_query_executor_status; - -extern -bt_bool bt_query_executor_is_canceled( - const bt_query_executor *query_executor); - -extern void bt_query_executor_get_ref( - const bt_query_executor *query_executor); - -extern void bt_query_executor_put_ref( - const bt_query_executor *query_executor); - -/* From query-executor.h */ - -extern -bt_query_executor *bt_query_executor_create(void); - -extern -bt_query_executor_status bt_query_executor_query( - bt_query_executor *query_executor, - const bt_component_class *component_class, - const char *object, const bt_value *params, - const bt_value **OUT); - -extern -bt_query_executor_status bt_query_executor_cancel( - bt_query_executor *query_executor); diff --git a/bindings/python/bt2/bt2/native_bt_stream.i b/bindings/python/bt2/bt2/native_bt_stream.i deleted file mode 100644 index 394d9872..00000000 --- a/bindings/python/bt2/bt2/native_bt_stream.i +++ /dev/null @@ -1,60 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From stream-const.h */ - -typedef enum bt_stream_status { - BT_STREAM_STATUS_OK = 0, - BT_STREAM_STATUS_NOMEM = -12, -} bt_stream_status; - -extern const bt_stream_class *bt_stream_borrow_class_const( - const bt_stream *stream); - -extern const bt_trace *bt_stream_borrow_trace_const( - const bt_stream *stream); - -extern const char *bt_stream_get_name(const bt_stream *stream); - -extern uint64_t bt_stream_get_id(const bt_stream *stream); - -extern void bt_stream_get_ref(const bt_stream *stream); - -extern void bt_stream_put_ref(const bt_stream *stream); - -/* From stream.h */ - -extern bt_stream *bt_stream_create(bt_stream_class *stream_class, - bt_trace *trace); - -extern bt_stream *bt_stream_create_with_id( - bt_stream_class *stream_class, - bt_trace *trace, uint64_t id); - -extern bt_trace *bt_stream_borrow_trace(bt_stream *stream); - -extern bt_stream_class *bt_stream_borrow_class(bt_stream *stream); - -extern bt_stream_status bt_stream_set_name(bt_stream *stream, - const char *name); diff --git a/bindings/python/bt2/bt2/native_bt_stream_class.i b/bindings/python/bt2/bt2/native_bt_stream_class.i deleted file mode 100644 index e5927043..00000000 --- a/bindings/python/bt2/bt2/native_bt_stream_class.i +++ /dev/null @@ -1,159 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From stream-class-const.h */ - -typedef enum bt_stream_class_status { - BT_STREAM_CLASS_STATUS_OK = 0, - BT_STREAM_CLASS_STATUS_NOMEM = -12, -} bt_stream_class_status; - -extern const bt_trace_class *bt_stream_class_borrow_trace_class_const( - const bt_stream_class *stream_class); - -extern const char *bt_stream_class_get_name( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_assigns_automatic_event_class_id( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_assigns_automatic_stream_id( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_packets_have_end_default_clock_snapshot( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_supports_discarded_events( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_supports_discarded_packets( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots( - const bt_stream_class *stream_class); - -extern bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots( - const bt_stream_class *stream_class); - -extern uint64_t bt_stream_class_get_id( - const bt_stream_class *stream_class); - -extern const bt_field_class * -bt_stream_class_borrow_packet_context_field_class_const( - const bt_stream_class *stream_class); - -extern const bt_field_class * -bt_stream_class_borrow_event_common_context_field_class_const( - const bt_stream_class *stream_class); - -extern uint64_t bt_stream_class_get_event_class_count( - const bt_stream_class *stream_class); - -extern const bt_event_class * -bt_stream_class_borrow_event_class_by_index_const( - const bt_stream_class *stream_class, uint64_t index); - -extern const bt_event_class * -bt_stream_class_borrow_event_class_by_id_const( - const bt_stream_class *stream_class, uint64_t id); - -extern const bt_clock_class * -bt_stream_class_borrow_default_clock_class_const( - const bt_stream_class *stream_class); - -extern void bt_stream_class_get_ref(const bt_stream_class *stream_class); - -extern void bt_stream_class_put_ref(const bt_stream_class *stream_class); - -/* From stream-class-const.h */ - -extern bt_stream_class *bt_stream_class_create( - bt_trace_class *trace_class); - -extern bt_stream_class *bt_stream_class_create_with_id( - bt_trace_class *trace_class, uint64_t id); - -extern bt_trace_class *bt_stream_class_borrow_trace_class( - bt_stream_class *stream_class); - -extern bt_stream_class_status bt_stream_class_set_name( - bt_stream_class *stream_class, const char *name); - -extern void bt_stream_class_set_assigns_automatic_event_class_id( - bt_stream_class *stream_class, bt_bool value); - -extern void bt_stream_class_set_assigns_automatic_stream_id( - bt_stream_class *stream_class, bt_bool value); - -extern void bt_stream_class_set_packets_have_beginning_default_clock_snapshot( - bt_stream_class *stream_class, bt_bool value); - -extern void bt_stream_class_set_packets_have_end_default_clock_snapshot( - bt_stream_class *stream_class, bt_bool value); - -extern void bt_stream_class_set_supports_discarded_events( - bt_stream_class *stream_class, - bt_bool supports_discarded_events, - bt_bool with_default_clock_snapshots); - -extern void bt_stream_class_set_supports_discarded_packets( - bt_stream_class *stream_class, - bt_bool supports_discarded_packets, - bt_bool with_default_clock_snapshots); - -extern bt_stream_class_status -bt_stream_class_set_packet_context_field_class( - bt_stream_class *stream_class, - bt_field_class *field_class); - -extern bt_field_class * -bt_stream_class_borrow_packet_context_field_class( - bt_stream_class *stream_class); - -extern bt_stream_class_status -bt_stream_class_set_event_common_context_field_class( - bt_stream_class *stream_class, - bt_field_class *field_class); - -extern bt_field_class * -bt_stream_class_borrow_event_common_context_field_class( - bt_stream_class *stream_class); - -extern bt_event_class * -bt_stream_class_borrow_event_class_by_index( - bt_stream_class *stream_class, uint64_t index); - -extern bt_event_class * -bt_stream_class_borrow_event_class_by_id( - bt_stream_class *stream_class, uint64_t id); - -extern bt_clock_class *bt_stream_class_borrow_default_clock_class( - bt_stream_class *stream_class); - -extern bt_stream_class_status bt_stream_class_set_default_clock_class( - bt_stream_class *stream_class, - bt_clock_class *clock_class); diff --git a/bindings/python/bt2/bt2/native_bt_trace.i b/bindings/python/bt2/bt2/native_bt_trace.i deleted file mode 100644 index 2d8f538b..00000000 --- a/bindings/python/bt2/bt2/native_bt_trace.i +++ /dev/null @@ -1,122 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From trace-const.h */ - -typedef enum bt_trace_status { - BT_TRACE_STATUS_OK = 0, - BT_TRACE_STATUS_NOMEM = -12, -} bt_trace_status; - -typedef void (* bt_trace_destruction_listener_func)( - const bt_trace *trace, void *data); - -extern const bt_trace_class *bt_trace_borrow_class_const( - const bt_trace *trace); - -extern const char *bt_trace_get_name(const bt_trace *trace); - -extern uint64_t bt_trace_get_stream_count(const bt_trace *trace); - -extern const bt_stream *bt_trace_borrow_stream_by_index_const( - const bt_trace *trace, uint64_t index); - -extern const bt_stream *bt_trace_borrow_stream_by_id_const( - const bt_trace *trace, uint64_t id); - -extern bt_trace_status bt_trace_add_destruction_listener( - const bt_trace *trace, - bt_trace_destruction_listener_func listener, - void *data, uint64_t *listener_id); - -extern bt_trace_status bt_trace_remove_destruction_listener( - const bt_trace *trace, uint64_t listener_id); - -extern void bt_trace_get_ref(const bt_trace *trace); - -extern void bt_trace_put_ref(const bt_trace *trace); - -/* From trace.h */ - -extern bt_trace_class *bt_trace_borrow_class(bt_trace *trace); - -extern bt_trace *bt_trace_create(bt_trace_class *trace_class); - -extern bt_trace_status bt_trace_set_name(bt_trace *trace, - const char *name); - -extern bt_stream *bt_trace_borrow_stream_by_index(bt_trace *trace, - uint64_t index); - -extern bt_stream *bt_trace_borrow_stream_by_id(bt_trace *trace, - uint64_t id); - -%{ -static void -trace_destroyed_listener(const bt_trace *trace, void *py_callable) -{ - PyObject *py_trace_ptr = NULL; - PyObject *py_res = NULL; - - py_trace_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(trace), - SWIGTYPE_p_bt_trace, 0); - if (!py_trace_ptr) { - BT_LOGF_STR("Failed to create a SWIG pointer object."); - abort(); - } - - py_res = PyObject_CallFunction(py_callable, "(O)", py_trace_ptr); - if (py_res != NULL) { - BT_ASSERT(py_res == Py_None); - } else { - bt2_py_loge_exception(); - } - - Py_DECREF(py_trace_ptr); - Py_XDECREF(py_res); -} - -uint64_t bt_py3_trace_add_destruction_listener(bt_trace *trace, PyObject *py_callable) -{ - uint64_t id = UINT64_C(-1); - bt_trace_status status; - - BT_ASSERT(trace); - BT_ASSERT(py_callable); - - status = bt_trace_add_destruction_listener( - trace, trace_destroyed_listener, py_callable, &id); - if (status != BT_TRACE_STATUS_OK) { - BT_LOGF_STR("Failed to add trace destruction listener."); - abort(); - } - - Py_INCREF(py_callable); - - return id; -} -%} - -uint64_t bt_py3_trace_add_destruction_listener(bt_trace *trace, - PyObject *py_callable); diff --git a/bindings/python/bt2/bt2/native_bt_trace_class.i b/bindings/python/bt2/bt2/native_bt_trace_class.i deleted file mode 100644 index 71061ffa..00000000 --- a/bindings/python/bt2/bt2/native_bt_trace_class.i +++ /dev/null @@ -1,153 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From trace-class-const.h */ - -typedef enum bt_trace_class_status { - BT_TRACE_CLASS_STATUS_OK = 0, - BT_TRACE_CLASS_STATUS_NOMEM = -12, -} bt_trace_class_status; - -typedef void (* bt_trace_class_destruction_listener_func)( - const bt_trace_class *trace_class, void *data); - -extern bt_bool bt_trace_class_assigns_automatic_stream_class_id( - const bt_trace_class *trace_class); - -extern const char *bt_trace_class_get_name( - const bt_trace_class *trace_class); - -extern bt_uuid bt_trace_class_get_uuid( - const bt_trace_class *trace_class); - -extern uint64_t bt_trace_class_get_environment_entry_count( - const bt_trace_class *trace_class); - -extern void bt_trace_class_borrow_environment_entry_by_index_const( - const bt_trace_class *trace_class, uint64_t index, - const char **OUT, const bt_value **OUT); - -extern const bt_value * -bt_trace_class_borrow_environment_entry_value_by_name_const( - const bt_trace_class *trace_class, const char *name); - -extern uint64_t bt_trace_class_get_stream_class_count( - const bt_trace_class *trace_class); - -extern const bt_stream_class * -bt_trace_class_borrow_stream_class_by_index_const( - const bt_trace_class *trace_class, uint64_t index); - -extern const bt_stream_class *bt_trace_class_borrow_stream_class_by_id_const( - const bt_trace_class *trace_class, uint64_t id); - -extern bt_trace_class_status bt_trace_class_add_destruction_listener( - const bt_trace_class *trace_class, - bt_trace_class_destruction_listener_func listener, - void *data, uint64_t *listener_id); - -extern bt_trace_class_status bt_trace_class_remove_destruction_listener( - const bt_trace_class *trace_class, uint64_t listener_id); - -extern void bt_trace_class_get_ref(const bt_trace_class *trace_class); - -extern void bt_trace_class_put_ref(const bt_trace_class *trace_class); - -/* From trace-class.h */ - -extern bt_trace_class *bt_trace_class_create(bt_self_component *self_comp); - -extern void bt_trace_class_set_assigns_automatic_stream_class_id( - bt_trace_class *trace_class, bt_bool value); - -extern bt_trace_class_status bt_trace_class_set_name( - bt_trace_class *trace_class, const char *name); - -extern void bt_trace_class_set_uuid(bt_trace_class *trace_class, - bt_uuid uuid); - -extern bt_trace_class_status bt_trace_class_set_environment_entry_integer( - bt_trace_class *trace_class, - const char *name, int64_t value); - -extern bt_trace_class_status bt_trace_class_set_environment_entry_string( - bt_trace_class *trace_class, - const char *name, const char *value); - -extern bt_stream_class *bt_trace_class_borrow_stream_class_by_index( - bt_trace_class *trace_class, uint64_t index); - -extern bt_stream_class *bt_trace_class_borrow_stream_class_by_id( - bt_trace_class *trace_class, uint64_t id); - -/* Helper functions for Python */ -%{ -static void -trace_class_destroyed_listener(const bt_trace_class *trace_class, void *py_callable) -{ - PyObject *py_trace_class_ptr = NULL; - PyObject *py_res = NULL; - - py_trace_class_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(trace_class), - SWIGTYPE_p_bt_trace_class, 0); - if (!py_trace_class_ptr) { - BT_LOGF_STR("Failed to create a SWIG pointer object."); - abort(); - } - - py_res = PyObject_CallFunction(py_callable, "(O)", py_trace_class_ptr); - if (py_res != NULL) { - BT_ASSERT(py_res == Py_None); - } else { - bt2_py_loge_exception(); - } - - Py_DECREF(py_trace_class_ptr); - Py_XDECREF(py_res); -} - -uint64_t bt_py3_trace_class_add_destruction_listener(bt_trace_class *trace_class, - PyObject *py_callable) -{ - uint64_t id = UINT64_C(-1); - bt_trace_class_status status; - - BT_ASSERT(trace_class); - BT_ASSERT(py_callable); - - status = bt_trace_class_add_destruction_listener( - trace_class, trace_class_destroyed_listener, py_callable, &id); - if (status != BT_TRACE_CLASS_STATUS_OK) { - BT_LOGF_STR("Failed to add trace class destruction listener."); - abort(); - } - - Py_INCREF(py_callable); - - return id; -} -%} - -uint64_t bt_py3_trace_class_add_destruction_listener(bt_trace_class *trace_class, - PyObject *py_callable); diff --git a/bindings/python/bt2/bt2/native_bt_value.i b/bindings/python/bt2/bt2/native_bt_value.i deleted file mode 100644 index 1ecd21e3..00000000 --- a/bindings/python/bt2/bt2/native_bt_value.i +++ /dev/null @@ -1,264 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* From value-const.h */ - -typedef enum bt_value_status { - /// Operation canceled. - BT_VALUE_STATUS_CANCELED = 125, - - /// Cannot allocate memory. - BT_VALUE_STATUS_NOMEM = -12, - - /// Okay, no error. - BT_VALUE_STATUS_OK = 0, -} bt_value_status; - -typedef enum bt_value_type { - /// Null value object. - BT_VALUE_TYPE_NULL = 0, - - /// Boolean value object (holds #BT_TRUE or #BT_FALSE). - BT_VALUE_TYPE_BOOL = 1, - - /// Unsigned integer value object (holds an unsigned 64-bit integer raw value). - BT_VALUE_TYPE_UNSIGNED_INTEGER = 2, - - /// Signed integer value object (holds a signed 64-bit integer raw value). - BT_VALUE_TYPE_SIGNED_INTEGER = 3, - - /// Floating point number value object (holds a \c double raw value). - BT_VALUE_TYPE_REAL = 4, - - /// String value object. - BT_VALUE_TYPE_STRING = 5, - - /// Array value object. - BT_VALUE_TYPE_ARRAY = 6, - - /// Map value object. - BT_VALUE_TYPE_MAP = 7, -} bt_value_type; - -extern bt_value_type bt_value_get_type(const bt_value *object); - -extern bt_value_status bt_value_copy(const bt_value *object, - bt_value **copy); - -extern bt_bool bt_value_compare(const bt_value *object_a, - const bt_value *object_b); - -extern bt_bool bt_value_bool_get(const bt_value *bool_obj); - -extern uint64_t bt_value_unsigned_integer_get(const bt_value *integer_obj); - -extern int64_t bt_value_signed_integer_get(const bt_value *integer_obj); - -extern double bt_value_real_get(const bt_value *real_obj); - -extern const char *bt_value_string_get(const bt_value *string_obj); - -extern uint64_t bt_value_array_get_size(const bt_value *array_obj); - -extern const bt_value *bt_value_array_borrow_element_by_index_const( - const bt_value *array_obj, uint64_t index); - -extern uint64_t bt_value_map_get_size(const bt_value *map_obj); - -extern const bt_value *bt_value_map_borrow_entry_value_const( - const bt_value *map_obj, const char *key); - -typedef bt_bool (* bt_value_map_foreach_entry_const_func)(const char *key, - const bt_value *object, void *data); - -extern bt_value_status bt_value_map_foreach_entry_const( - const bt_value *map_obj, - bt_value_map_foreach_entry_const_func func, void *data); - -extern bt_bool bt_value_map_has_entry(const bt_value *map_obj, - const char *key); - -extern bt_value_status bt_value_map_extend( - const bt_value *base_map_obj, - const bt_value *extension_map_obj, - bt_value **extended_map_obj); - -extern void bt_value_get_ref(const bt_value *value); - -extern void bt_value_put_ref(const bt_value *value); - -/* From value.h */ - -extern bt_value *const bt_value_null; - -extern bt_value *bt_value_bool_create(void); - -extern bt_value *bt_value_bool_create_init(bt_bool val); - -extern void bt_value_bool_set(bt_value *bool_obj, bt_bool val); - -extern bt_value *bt_value_unsigned_integer_create(void); - -extern bt_value *bt_value_unsigned_integer_create_init(uint64_t val); - -extern void bt_value_unsigned_integer_set(bt_value *integer_obj, uint64_t val); - -extern bt_value *bt_value_signed_integer_create(void); - -extern bt_value *bt_value_signed_integer_create_init(int64_t val); - -extern void bt_value_signed_integer_set(bt_value *integer_obj, int64_t val); - -extern bt_value *bt_value_real_create(void); - -extern bt_value *bt_value_real_create_init(double val); - -extern void bt_value_real_set(bt_value *real_obj, double val); - -extern bt_value *bt_value_string_create(void); - -extern bt_value *bt_value_string_create_init(const char *val); - -extern bt_value_status bt_value_string_set(bt_value *string_obj, - const char *val); - -extern bt_value *bt_value_array_create(void); - -extern bt_value *bt_value_array_borrow_element_by_index( - bt_value *array_obj, uint64_t index); - -extern bt_value_status bt_value_array_append_element( - bt_value *array_obj, - bt_value *element_obj); - -extern bt_value_status bt_value_array_append_bool_element( - bt_value *array_obj, bt_bool val); - -extern bt_value_status bt_value_array_append_unsigned_integer_element( - bt_value *array_obj, uint64_t val); - -extern bt_value_status bt_value_array_append_signed_integer_element( - bt_value *array_obj, int64_t val); - -extern bt_value_status bt_value_array_append_real_element( - bt_value *array_obj, double val); - -extern bt_value_status bt_value_array_append_string_element( - bt_value *array_obj, const char *val); - -extern bt_value_status bt_value_array_append_empty_array_element( - bt_value *array_obj); - -extern bt_value_status bt_value_array_append_empty_map_element( - bt_value *array_obj); - -extern bt_value_status bt_value_array_set_element_by_index( - bt_value *array_obj, uint64_t index, - bt_value *element_obj); - -extern bt_value *bt_value_map_create(void); - -extern bt_value *bt_value_map_borrow_entry_value( - bt_value *map_obj, const char *key); - -typedef bt_bool (* bt_value_map_foreach_entry_func)(const char *key, - bt_value *object, void *data); - -extern bt_value_status bt_value_map_foreach_entry( - bt_value *map_obj, - bt_value_map_foreach_entry_func func, void *data); - -extern bt_value_status bt_value_map_insert_entry( - bt_value *map_obj, const char *key, - bt_value *element_obj); - -extern bt_value_status bt_value_map_insert_bool_entry( - bt_value *map_obj, const char *key, bt_bool val); - -extern bt_value_status bt_value_map_insert_unsigned_integer_entry( - bt_value *map_obj, const char *key, uint64_t val); - -extern bt_value_status bt_value_map_insert_signed_integer_entry( - bt_value *map_obj, const char *key, int64_t val); - -extern bt_value_status bt_value_map_insert_real_entry( - bt_value *map_obj, const char *key, double val); - -extern bt_value_status bt_value_map_insert_string_entry( - bt_value *map_obj, const char *key, - const char *val); - -extern bt_value_status bt_value_map_insert_empty_array_entry( - bt_value *map_obj, const char *key); - -extern bt_value_status bt_value_map_insert_empty_map_entry( - bt_value *map_obj, const char *key); - -%{ -struct bt_value_map_get_keys_data { - struct bt_value *keys; -}; - -static int bt_value_map_get_keys_cb(const char *key, const struct bt_value *object, void *data) -{ - enum bt_value_status status; - struct bt_value_map_get_keys_data *priv_data = data; - - status = bt_value_array_append_string_element(priv_data->keys, key); - if (status != BT_VALUE_STATUS_OK) { - return BT_FALSE; - } - - return BT_TRUE; -} - -static struct bt_value *bt_value_map_get_keys(const struct bt_value *map_obj) -{ - enum bt_value_status status; - struct bt_value_map_get_keys_data data; - - data.keys = bt_value_array_create(); - if (!data.keys) { - return NULL; - } - - status = bt_value_map_foreach_entry_const(map_obj, bt_value_map_get_keys_cb, - &data); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - - goto end; - -error: - if (data.keys) { - BT_VALUE_PUT_REF_AND_RESET(data.keys); - } - -end: - return data.keys; -} -%} - -struct bt_value *bt_value_map_get_keys(const struct bt_value *map_obj); diff --git a/bindings/python/bt2/bt2/native_bt_version.i b/bindings/python/bt2/bt2/native_bt_version.i deleted file mode 100644 index 3d99e7d2..00000000 --- a/bindings/python/bt2/bt2/native_bt_version.i +++ /dev/null @@ -1,29 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* Version functions */ -int bt_version_get_major(void); -int bt_version_get_minor(void); -int bt_version_get_patch(void); -const char *bt_version_get_extra(void); diff --git a/bindings/python/bt2/bt2/object.py b/bindings/python/bt2/bt2/object.py deleted file mode 100644 index 218965de..00000000 --- a/bindings/python/bt2/bt2/object.py +++ /dev/null @@ -1,138 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - -class _BaseObject: - # Ensure that the object always has _ptr set, even if it throws during - # construction. - - def __new__(cls, *args, **kwargs): - obj = super().__new__(cls) - obj._ptr = None - return obj - - def __init__(self, ptr): - self._ptr = ptr - - @property - def addr(self): - return int(self._ptr) - - def __repr__(self): - return '<{}.{} object @ {}>'.format(self.__class__.__module__, - self.__class__.__name__, - hex(self.addr)) - - def __copy__(self): - raise NotImplementedError - - def __deepcopy__(self, memo): - raise NotImplementedError - - -# A Python object that is itself not refcounted, but is wholly owned by an -# object that is itself refcounted (a _SharedObject). A Babeltrace unique -# object gets destroyed once its owner gets destroyed (its refcount drops to -# 0). -# -# In the Python bindings, to avoid having to deal with issues with the lifetime -# of unique objects, we make it so acquiring a reference on a unique object -# acquires a reference on its owner. - -class _UniqueObject(_BaseObject): - - # Create a _UniqueObject. - # - # - ptr: SWIG Object, pointer to the unique object. - # - owner_ptr: SWIG Object, pointer to the owner of the unique - # object. A new reference is acquired. - # - owner_get_ref: Callback to get a reference on the owner - # - owner_put_ref: Callback to put a reference on the owner. - - @classmethod - def _create_from_ptr_and_get_ref(cls, ptr, owner_ptr, - owner_get_ref, owner_put_ref): - obj = cls.__new__(cls) - - obj._ptr = ptr - obj._owner_ptr = owner_ptr - obj._owner_get_ref = owner_get_ref - obj._owner_put_ref = owner_put_ref - - obj._owner_get_ref(obj._owner_ptr) - - return obj - - def __del__(self): - self._owner_put_ref(self._owner_ptr) - - -# Python object that owns a reference to a Babeltrace object. -class _SharedObject(_BaseObject): - - # Get a new reference on ptr. - # - # This must be implemented by subclasses to work correctly with a pointer - # of the native type they wrap. - - @staticmethod - def _get_ref(ptr): - raise NotImplementedError - - # Put a reference on ptr. - # - # This must be implemented by subclasses to work correctly with a pointer - # of the native type they wrap. - - @staticmethod - def _put_ref(ptr): - raise NotImplementedError - - # Create a _SharedObject from an existing reference. - # - # This assumes that the caller owns a reference to the Babeltrace object - # and transfers this ownership to the newly created Python object. - - @classmethod - def _create_from_ptr(cls, ptr_owned): - obj = cls.__new__(cls) - obj._ptr = ptr_owned - return obj - - # Like _create_from_ptr, but acquire a new reference rather than - # stealing the caller's reference. - - @classmethod - def _create_from_ptr_and_get_ref(cls, ptr): - obj = cls._create_from_ptr(ptr) - cls._get_ref(obj._ptr) - return obj - - def _release(self): - """Return the wrapped pointer, transfer its ownership to the - caller.""" - ptr = self._ptr - self._ptr = None - return ptr - - def __del__(self): - self._put_ref(self._ptr) diff --git a/bindings/python/bt2/bt2/packet.py b/bindings/python/bt2/bt2/packet.py deleted file mode 100644 index 93e9ac8f..00000000 --- a/bindings/python/bt2/bt2/packet.py +++ /dev/null @@ -1,47 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2016-2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object -import bt2.field -import bt2 - - -class _Packet(object._SharedObject): - _get_ref = staticmethod(native_bt.packet_get_ref) - _put_ref = staticmethod(native_bt.packet_put_ref) - - @property - def stream(self): - stream_ptr = native_bt.packet_borrow_stream(self._ptr) - assert stream_ptr is not None - return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) - - @property - def context_field(self): - field_ptr = native_bt.packet_borrow_context_field(self._ptr) - - if field_ptr is None: - return - - return bt2.field._create_field_from_ptr(field_ptr, self._ptr, - self._get_ref, - self._put_ref) diff --git a/bindings/python/bt2/bt2/plugin.py b/bindings/python/bt2/bt2/plugin.py deleted file mode 100644 index 2d0d8b05..00000000 --- a/bindings/python/bt2/bt2/plugin.py +++ /dev/null @@ -1,218 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import collections.abc -import bt2.component -import os.path -import bt2 - - -def find_plugins(path, recurse=True): - utils._check_str(path) - utils._check_bool(recurse) - plugin_set_ptr = None - - if os.path.isfile(path): - plugin_set_ptr = native_bt.plugin_find_all_from_file(path) - elif os.path.isdir(path): - plugin_set_ptr = native_bt.plugin_find_all_from_dir(path, int(recurse)) - - if plugin_set_ptr is None: - return - - return _PluginSet._create_from_ptr(plugin_set_ptr) - - -def find_plugin(name): - utils._check_str(name) - ptr = native_bt.plugin_find(name) - - if ptr is None: - return - - return _Plugin._create_from_ptr(ptr) - - -class _PluginSet(object._SharedObject, collections.abc.Sequence): - _put_ref = staticmethod(native_bt.plugin_set_put_ref) - _get_ref = staticmethod(native_bt.plugin_set_get_ref) - - def __len__(self): - count = native_bt.plugin_set_get_plugin_count(self._ptr) - assert(count >= 0) - return count - - def __getitem__(self, index): - utils._check_uint64(index) - - if index >= len(self): - raise IndexError - - plugin_ptr = native_bt.plugin_set_borrow_plugin_by_index_const(self._ptr, index) - assert plugin_ptr is not None - return _Plugin._create_from_ptr_and_get_ref(plugin_ptr) - - -class _PluginVersion: - def __init__(self, major, minor, patch, extra): - self._major = major - self._minor = minor - self._patch = patch - self._extra = extra - - @property - def major(self): - return self._major - - @property - def minor(self): - return self._minor - - @property - def patch(self): - return self._patch - - @property - def extra(self): - return self._extra - - def __str__(self): - extra = '' - - if self._extra is not None: - extra = self._extra - - return '{}.{}.{}{}'.format(self._major, self._minor, self._patch, extra) - - -class _PluginComponentClassesIterator(collections.abc.Iterator): - def __init__(self, plugin_comp_cls): - self._plugin_comp_cls = plugin_comp_cls - self._at = 0 - - def __next__(self): - plugin_ptr = self._plugin_comp_cls._plugin._ptr - total = self._plugin_comp_cls._component_class_count(plugin_ptr) - - if self._at == total: - raise StopIteration - - comp_cls_ptr = self._plugin_comp_cls._borrow_component_class_by_index(plugin_ptr, self._at) - assert comp_cls_ptr is not None - self._at += 1 - - comp_cls_type = self._plugin_comp_cls._comp_cls_type - comp_cls_pycls = bt2.component._COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS[comp_cls_type] - comp_cls_ptr = comp_cls_pycls._as_component_class_ptr(comp_cls_ptr) - name = native_bt.component_class_get_name(comp_cls_ptr) - assert name is not None - return name - - -class _PluginComponentClasses(collections.abc.Mapping): - def __init__(self, plugin): - self._plugin = plugin - - def __getitem__(self, key): - utils._check_str(key) - cc_ptr = self._borrow_component_class_by_name(self._plugin._ptr, key) - - if cc_ptr is None: - raise KeyError(key) - - return bt2.component._create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) - - def __len__(self): - return self._component_class_count(self._plugin._ptr) - - def __iter__(self): - return _PluginComponentClassesIterator(self) - - -class _PluginSourceComponentClasses(_PluginComponentClasses): - _component_class_count = staticmethod(native_bt.plugin_get_source_component_class_count) - _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_source_component_class_by_name_const) - _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_source_component_class_by_index_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE - - -class _PluginFilterComponentClasses(_PluginComponentClasses): - _component_class_count = staticmethod(native_bt.plugin_get_filter_component_class_count) - _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_filter_component_class_by_name_const) - _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_filter_component_class_by_index_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_FILTER - - -class _PluginSinkComponentClasses(_PluginComponentClasses): - _component_class_count = staticmethod(native_bt.plugin_get_sink_component_class_count) - _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_sink_component_class_by_name_const) - _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_sink_component_class_by_index_const) - _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SINK - - -class _Plugin(object._SharedObject): - _put_ref = staticmethod(native_bt.plugin_put_ref) - _get_ref = staticmethod(native_bt.plugin_get_ref) - - @property - def name(self): - name = native_bt.plugin_get_name(self._ptr) - assert(name is not None) - return name - - @property - def author(self): - return native_bt.plugin_get_author(self._ptr) - - @property - def license(self): - return native_bt.plugin_get_license(self._ptr) - - @property - def description(self): - return native_bt.plugin_get_description(self._ptr) - - @property - def path(self): - return native_bt.plugin_get_path(self._ptr) - - @property - def version(self): - status, major, minor, patch, extra = native_bt.plugin_get_version_wrapper(self._ptr) - - if status == native_bt.PROPERTY_AVAILABILITY_NOT_AVAILABLE: - return - - return _PluginVersion(major, minor, patch, extra) - - @property - def source_component_classes(self): - return _PluginSourceComponentClasses(self) - - @property - def filter_component_classes(self): - return _PluginFilterComponentClasses(self) - - @property - def sink_component_classes(self): - return _PluginSinkComponentClasses(self) diff --git a/bindings/python/bt2/bt2/port.py b/bindings/python/bt2/bt2/port.py deleted file mode 100644 index f7c23669..00000000 --- a/bindings/python/bt2/bt2/port.py +++ /dev/null @@ -1,136 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object -import bt2.component -import bt2.connection -import bt2.message_iterator -import bt2.message -import bt2 - - -def _create_from_ptr_and_get_ref(ptr, port_type): - cls = _PORT_TYPE_TO_PYCLS.get(port_type, None) - - if cls is None: - raise bt2.Error('unknown port type: {}'.format(port_type)) - - return cls._create_from_ptr_and_get_ref(ptr) - - -def _create_self_from_ptr_and_get_ref(ptr, port_type): - cls = _PORT_TYPE_TO_USER_PYCLS.get(port_type, None) - - if cls is None: - raise bt2.Error('unknown port type: {}'.format(port_type)) - - return cls._create_from_ptr_and_get_ref(ptr) - - -class _Port(object._SharedObject): - @classmethod - def _get_ref(cls, ptr): - ptr = cls._as_port_ptr(ptr) - return native_bt.port_get_ref(ptr) - - @classmethod - def _put_ref(cls, ptr): - ptr = cls._as_port_ptr(ptr) - return native_bt.port_put_ref(ptr) - - @property - def name(self): - ptr = self._as_port_ptr(self._ptr) - name = native_bt.port_get_name(ptr) - assert name is not None - return name - - @property - def connection(self): - ptr = self._as_port_ptr(self._ptr) - conn_ptr = native_bt.port_borrow_connection_const(ptr) - - if conn_ptr is None: - return - - return bt2.connection._Connection._create_from_ptr_and_get_ref(conn_ptr) - - @property - def is_connected(self): - return self.connection is not None - - -class _InputPort(_Port): - _as_port_ptr = staticmethod(native_bt.port_input_as_port_const) - - -class _OutputPort(_Port): - _as_port_ptr = staticmethod(native_bt.port_output_as_port_const) - - -class _UserComponentPort(_Port): - @classmethod - def _as_port_ptr(cls, ptr): - ptr = cls._as_self_port_ptr(ptr) - return native_bt.self_component_port_as_port(ptr) - - @property - def connection(self): - ptr = self._as_port_ptr(self._ptr) - conn_ptr = native_bt.port_borrow_connection_const(ptr) - - if conn_ptr is None: - return - - return bt2.connection._Connection._create_from_ptr_and_get_ref(conn_ptr) - - @property - def user_data(self): - ptr = self._as_self_port_ptr(self._ptr) - return native_bt.self_component_port_get_data(ptr) - - -class _UserComponentInputPort(_UserComponentPort, _InputPort): - _as_self_port_ptr = staticmethod(native_bt.self_component_port_input_as_self_component_port) - - def create_message_iterator(self): - msg_iter_ptr = native_bt.self_component_port_input_message_iterator_create(self._ptr) - if msg_iter_ptr is None: - raise bt2.CreationError('cannot create message iterator object') - - return bt2.message_iterator._UserComponentInputPortMessageIterator(msg_iter_ptr) - - -class _UserComponentOutputPort(_UserComponentPort, _OutputPort): - _as_self_port_ptr = staticmethod(native_bt.self_component_port_output_as_self_component_port) - - -_PORT_TYPE_TO_PYCLS = { - native_bt.PORT_TYPE_INPUT: _InputPort, - native_bt.PORT_TYPE_OUTPUT: _OutputPort, -} - - -_PORT_TYPE_TO_USER_PYCLS = { - native_bt.PORT_TYPE_INPUT: _UserComponentInputPort, - native_bt.PORT_TYPE_OUTPUT: _UserComponentOutputPort, -} diff --git a/bindings/python/bt2/bt2/py_plugin.py b/bindings/python/bt2/bt2/py_plugin.py deleted file mode 100644 index b8450308..00000000 --- a/bindings/python/bt2/bt2/py_plugin.py +++ /dev/null @@ -1,135 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import utils -import bt2.component - - -def plugin_component_class(component_class): - if not issubclass(component_class, bt2.component._UserComponent): - raise TypeError('component class is not a subclass of a user component class') - - component_class._bt_plugin_component_class = None - return component_class - - -def register_plugin(module_name, name, description=None, author=None, - license=None, version=None): - import sys - - if module_name not in sys.modules: - raise RuntimeError("cannot find module '{}' in loaded modules".format(module_name)) - - utils._check_str(name) - - if description is not None: - utils._check_str(description) - - if author is not None: - utils._check_str(author) - - if license is not None: - utils._check_str(license) - - if version is not None: - if not _validate_version(version): - raise ValueError('wrong version: expecting a tuple: (major, minor, patch) or (major, minor, patch, extra)') - - sys.modules[module_name]._bt_plugin_info = _PluginInfo(name, description, - author, license, - version) - - -def _validate_version(version): - if version is None: - return True - - if not isinstance(version, tuple): - return False - - if len(version) < 3 or len(version) > 4: - return False - - if not isinstance(version[0], int): - return False - - if not isinstance(version[1], int): - return False - - if not isinstance(version[2], int): - return False - - if len(version) == 4: - if not isinstance(version[3], str): - return False - - return True - - -class _PluginInfo: - def __init__(self, name, description, author, license, version): - self.name = name - self.description = description - self.author = author - self.license = license - self.version = version - self.comp_class_addrs = None - - -# called by the BT plugin system -def _try_load_plugin_module(path): - import importlib.machinery - import inspect - import hashlib - - if path is None: - raise TypeError('missing path') - - # In order to load the module uniquely from its path, even from - # different files which have the same basename, we hash the path - # and prefix with `bt_plugin_`. This is its key in sys.modules. - h = hashlib.sha256() - h.update(path.encode()) - module_name = 'bt_plugin_{}'.format(h.hexdigest()) - - # try loading the module: any raised exception is catched by the caller - mod = importlib.machinery.SourceFileLoader(module_name, path).load_module() - - # we have the module: look for its plugin info first - if not hasattr(mod, '_bt_plugin_info'): - raise RuntimeError("missing '_bt_plugin_info' module attribute") - - plugin_info = mod._bt_plugin_info - - # search for user component classes - def is_user_comp_class(obj): - if not inspect.isclass(obj): - return False - - if not hasattr(obj, '_bt_plugin_component_class'): - return False - - return True - - comp_class_entries = inspect.getmembers(mod, is_user_comp_class) - plugin_info.comp_class_addrs = [entry[1].addr for entry in comp_class_entries] - return plugin_info diff --git a/bindings/python/bt2/bt2/query_executor.py b/bindings/python/bt2/bt2/query_executor.py deleted file mode 100644 index a87713ff..00000000 --- a/bindings/python/bt2/bt2/query_executor.py +++ /dev/null @@ -1,93 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.component -import bt2 - - -class QueryExecutor(object._SharedObject): - _get_ref = staticmethod(native_bt.query_executor_get_ref) - _put_ref = staticmethod(native_bt.query_executor_put_ref) - - def _handle_status(self, status, gen_error_msg): - if status == native_bt.QUERY_EXECUTOR_STATUS_AGAIN: - raise bt2.TryAgain - elif status == native_bt.QUERY_EXECUTOR_STATUS_CANCELED: - raise bt2.QueryExecutorCanceled - elif status == native_bt.QUERY_EXECUTOR_STATUS_INVALID_OBJECT: - raise bt2.InvalidQueryObject - elif status == native_bt.QUERY_EXECUTOR_STATUS_INVALID_PARAMS: - raise bt2.InvalidQueryParams - elif status < 0: - raise bt2.Error(gen_error_msg) - - def __init__(self): - ptr = native_bt.query_executor_create() - - if ptr is None: - raise bt2.CreationError('cannot create query executor object') - - super().__init__(ptr) - - def cancel(self): - status = native_bt.query_executor_cancel(self._ptr) - self._handle_status(status, 'cannot cancel query executor object') - - @property - def is_canceled(self): - is_canceled = native_bt.query_executor_is_canceled(self._ptr) - assert(is_canceled >= 0) - return is_canceled > 0 - - def query(self, component_class, object, params=None): - if self.is_canceled: - raise bt2.QueryExecutorCanceled - - if not isinstance(component_class, bt2.component._GenericComponentClass): - err = False - - try: - if not issubclass(component_class, bt2.component._UserComponent): - err = True - except TypeError: - err = True - - if err: - o = component_class - raise TypeError("'{}' is not a component class object".format(o)) - - utils._check_str(object) - - if params is None: - params_ptr = native_bt.value_null - else: - params = bt2.create_value(params) - params_ptr = params._ptr - - cc_ptr = component_class._component_class_ptr() - - status, result_ptr = native_bt.query_executor_query(self._ptr, cc_ptr, - object, params_ptr) - self._handle_status(status, 'cannot query component class') - assert(result_ptr) - return bt2.value._create_from_ptr(result_ptr) diff --git a/bindings/python/bt2/bt2/stream.py b/bindings/python/bt2/bt2/stream.py deleted file mode 100644 index 5accfcd0..00000000 --- a/bindings/python/bt2/bt2/stream.py +++ /dev/null @@ -1,60 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2016-2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, utils -import bt2.packet -import bt2.event -import bt2 - - -class _Stream(bt2.object._SharedObject): - _get_ref = staticmethod(native_bt.stream_get_ref) - _put_ref = staticmethod(native_bt.stream_put_ref) - - @property - def cls(self): - stream_class_ptr = native_bt.stream_borrow_class(self._ptr) - assert stream_class_ptr is not None - return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(stream_class_ptr) - - @property - def name(self): - return native_bt.stream_get_name(self._ptr) - - def _name(self, name): - utils._check_str(name) - native_bt.stream_set_name(self._ptr, name) - - _name = property(fset=_name) - - @property - def id(self): - id = native_bt.stream_get_id(self._ptr) - return id if id >= 0 else None - - def create_packet(self): - packet_ptr = native_bt.packet_create(self._ptr) - - if packet_ptr is None: - raise bt2.CreationError('cannot create packet object') - - return bt2.packet._Packet._create_from_ptr(packet_ptr) diff --git a/bindings/python/bt2/bt2/stream_class.py b/bindings/python/bt2/bt2/stream_class.py deleted file mode 100644 index 54b5b38b..00000000 --- a/bindings/python/bt2/bt2/stream_class.py +++ /dev/null @@ -1,253 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.field_class -import bt2.event_class -import collections.abc -import bt2.stream -import bt2 - - -class _StreamClass(object._SharedObject, collections.abc.Mapping): - _get_ref = staticmethod(native_bt.stream_class_get_ref) - _put_ref = staticmethod(native_bt.stream_class_put_ref) - - def __getitem__(self, key): - utils._check_int64(key) - ec_ptr = native_bt.stream_class_borrow_event_class_by_id(self._ptr, key) - - if ec_ptr is None: - raise KeyError(key) - - return bt2.event_class._EventClass._create_from_ptr_and_get_ref(ec_ptr) - - def __len__(self): - count = native_bt.stream_class_get_event_class_count(self._ptr) - assert count >= 0 - return count - - def __iter__(self): - for idx in range(len(self)): - ec_ptr = native_bt.stream_class_borrow_event_class_by_index_const(self._ptr, idx) - assert ec_ptr is not None - - id = native_bt.event_class_get_id(ec_ptr) - assert id >= 0 - - yield id - - def create_event_class(self, id=None, name=None, log_level=None, emf_uri=None, - specific_context_field_class=None, - payload_field_class=None): - if self.assigns_automatic_event_class_id: - if id is not None: - raise ValueError('id provided, but stream class assigns automatic event class ids') - - ec_ptr = native_bt.event_class_create(self._ptr) - else: - if id is None: - raise ValueError('id not provided, but stream class does not assign automatic event class ids') - - utils._check_uint64(id) - ec_ptr = native_bt.event_class_create_with_id(self._ptr, id) - - event_class = bt2.event_class._EventClass._create_from_ptr(ec_ptr) - - if name is not None: - event_class._name = name - - if log_level is not None: - event_class._log_level = log_level - - if emf_uri is not None: - event_class._emf_uri = emf_uri - - if specific_context_field_class is not None: - event_class._specific_context_field_class = specific_context_field_class - - if payload_field_class is not None: - event_class._payload_field_class = payload_field_class - - return event_class - - @property - def trace_class(self): - tc_ptr = native_bt.stream_class_borrow_trace_class_const(self._ptr) - - if tc_ptr is not None: - return bt2._TraceClass._create_from_ptr_and_get_ref(tc_ptr) - - @property - def name(self): - return native_bt.stream_class_get_name(self._ptr) - - def _name(self, name): - utils._check_str(name) - ret = native_bt.stream_class_set_name(self._ptr, name) - utils._handle_ret(ret, "cannot set stream class object's name") - - _name = property(fset=_name) - - @property - def assigns_automatic_event_class_id(self): - return native_bt.stream_class_assigns_automatic_event_class_id(self._ptr) - - def _assigns_automatic_event_class_id(self, auto_id): - utils._check_bool(auto_id) - return native_bt.stream_class_set_assigns_automatic_event_class_id(self._ptr, auto_id) - - _assigns_automatic_event_class_id = property(fset=_assigns_automatic_event_class_id) - - @property - def assigns_automatic_stream_id(self): - return native_bt.stream_class_assigns_automatic_stream_id(self._ptr) - - def _assigns_automatic_stream_id(self, auto_id): - utils._check_bool(auto_id) - return native_bt.stream_class_set_assigns_automatic_stream_id(self._ptr, auto_id) - - _assigns_automatic_stream_id = property(fset=_assigns_automatic_stream_id) - - @property - def packets_have_beginning_default_clock_snapshot(self): - return native_bt.stream_class_packets_have_beginning_default_clock_snapshot(self._ptr) - - def _packets_have_beginning_default_clock_snapshot(self, value): - utils._check_bool(value) - native_bt.stream_class_set_packets_have_beginning_default_clock_snapshot(self._ptr, value) - - _packets_have_beginning_default_clock_snapshot = property(fset=_packets_have_beginning_default_clock_snapshot) - - @property - def packets_have_end_default_clock_snapshot(self): - return native_bt.stream_class_packets_have_end_default_clock_snapshot(self._ptr) - - def _packets_have_end_default_clock_snapshot(self, value): - utils._check_bool(value) - native_bt.stream_class_set_packets_have_end_default_clock_snapshot(self._ptr, value) - - _packets_have_end_default_clock_snapshot = property(fset=_packets_have_end_default_clock_snapshot) - - @property - def supports_discarded_events(self): - return native_bt.stream_class_supports_discarded_events(self._ptr) - - def _set_supports_discarded_events(self, supports, with_cs=False): - utils._check_bool(supports) - utils._check_bool(with_cs) - - if not supports and with_cs: - raise ValueError('cannot not support discarded events, but have default clock snapshots') - - native_bt.stream_class_set_supports_discarded_events(self._ptr, supports, with_cs) - - @property - def discarded_events_have_default_clock_snapshots(self): - return native_bt.stream_class_discarded_events_have_default_clock_snapshots(self._ptr) - - @property - def supports_discarded_packets(self): - return native_bt.stream_class_supports_discarded_packets(self._ptr) - - def _set_supports_discarded_packets(self, supports, with_cs): - utils._check_bool(supports) - utils._check_bool(with_cs) - - if not supports and with_cs: - raise ValueError('cannot not support discarded packets, but have default clock snapshots') - - native_bt.stream_class_set_supports_discarded_packets(self._ptr, supports, with_cs) - - @property - def discarded_packets_have_default_clock_snapshots(self): - return native_bt.stream_class_discarded_packets_have_default_clock_snapshots(self._ptr) - - @property - def id(self): - id = native_bt.stream_class_get_id(self._ptr) - - if id < 0: - return - - return id - - @id.setter - def id(self, id): - utils._check_int64(id) - ret = native_bt.stream_class_set_id(self._ptr, id) - utils._handle_ret(ret, "cannot set stream class object's ID") - - @property - def packet_context_field_class(self): - fc_ptr = native_bt.stream_class_borrow_packet_context_field_class_const(self._ptr) - - if fc_ptr is None: - return - - return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) - - def _packet_context_field_class(self, packet_context_field_class): - if packet_context_field_class is not None: - utils._check_type(packet_context_field_class, - bt2.field_class._StructureFieldClass) - ret = native_bt.stream_class_set_packet_context_field_class(self._ptr, - packet_context_field_class._ptr) - utils._handle_ret(ret, "cannot set stream class' packet context field class") - - _packet_context_field_class = property(fset=_packet_context_field_class) - - @property - def event_common_context_field_class(self): - fc_ptr = native_bt.stream_class_borrow_event_common_context_field_class_const(self._ptr) - - if fc_ptr is None: - return - - return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) - - def _event_common_context_field_class(self, event_common_context_field_class): - if event_common_context_field_class is not None: - utils._check_type(event_common_context_field_class, - bt2.field_class._StructureFieldClass) - - set_context_fn = native_bt.stream_class_set_event_common_context_field_class - ret = set_context_fn(self._ptr, event_common_context_field_class._ptr) - - utils._handle_ret(ret, "cannot set stream class' event context field type") - - _event_common_context_field_class = property(fset=_event_common_context_field_class) - - @property - def default_clock_class(self): - cc_ptr = native_bt.stream_class_borrow_default_clock_class(self._ptr) - if cc_ptr is None: - return - - return bt2.clock_class._ClockClass._create_from_ptr_and_get_ref(cc_ptr) - - def _default_clock_class(self, clock_class): - utils._check_type(clock_class, bt2.clock_class._ClockClass) - native_bt.stream_class_set_default_clock_class( - self._ptr, clock_class._ptr) - - _default_clock_class = property(fset=_default_clock_class) diff --git a/bindings/python/bt2/bt2/trace.py b/bindings/python/bt2/bt2/trace.py deleted file mode 100644 index 5830ec3f..00000000 --- a/bindings/python/bt2/bt2/trace.py +++ /dev/null @@ -1,122 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import bt2.field_class -import collections.abc -import bt2.value -import bt2.stream -import bt2.trace_class -import bt2 -import functools - - -def _trace_destruction_listener_from_native(user_listener, trace_ptr): - trace = bt2.trace._Trace._create_from_ptr_and_get_ref(trace_ptr) - user_listener(trace) - - -class _Trace(object._SharedObject, collections.abc.Mapping): - _get_ref = staticmethod(native_bt.trace_get_ref) - _put_ref = staticmethod(native_bt.trace_put_ref) - - def __len__(self): - count = native_bt.trace_get_stream_count(self._ptr) - assert count >= 0 - return count - - def __getitem__(self, id): - utils._check_uint64(id) - - stream_ptr = native_bt.trace_borrow_stream_by_id_const(self._ptr, id) - - if stream_ptr is None: - raise KeyError(id) - - return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) - - def __iter__(self): - for idx in range(len(self)): - stream_ptr = native_bt.trace_borrow_stream_by_index_const(self._ptr, idx) - assert stream_ptr is not None - - id = native_bt.stream_get_id(stream_ptr) - assert id >= 0 - - yield id - - @property - def cls(self): - trace_class_ptr = native_bt.trace_borrow_class(self._ptr) - assert trace_class_ptr is not None - return bt2.trace_class._TraceClass._create_from_ptr_and_get_ref(trace_class_ptr) - - @property - def name(self): - return native_bt.trace_get_name(self._ptr) - - def _name(self, name): - utils._check_str(name) - ret = native_bt.trace_set_name(self._ptr, name) - utils._handle_ret(ret, "cannot set trace class object's name") - - _name = property(fset=_name) - - def create_stream(self, stream_class, id=None, name=None): - utils._check_type(stream_class, bt2.stream_class._StreamClass) - - if stream_class.assigns_automatic_stream_id: - if id is not None: - raise ValueError("id provided, but stream class assigns automatic stream ids") - - stream_ptr = native_bt.stream_create(stream_class._ptr, self._ptr) - else: - if id is None: - raise ValueError("id not provided, but stream class does not assign automatic stream ids") - - utils._check_uint64(id) - stream_ptr = native_bt.stream_create_with_id(stream_class._ptr, self._ptr, id) - - if stream_ptr is None: - raise bt2.CreationError('cannot create stream object') - - stream = bt2.stream._Stream._create_from_ptr(stream_ptr) - - if name is not None: - stream._name = name - - return stream - - def add_destruction_listener(self, listener): - '''Add a listener to be called when the trace is destroyed.''' - if not callable(listener): - raise TypeError("'listener' parameter is not callable") - - fn = native_bt.py3_trace_add_destruction_listener - listener_from_native = functools.partial(_trace_destruction_listener_from_native, - listener) - - listener_id = fn(self._ptr, listener_from_native) - if listener_id is None: - utils._raise_bt2_error('cannot add destruction listener to trace object') - - return bt2._ListenerHandle(listener_id, self) diff --git a/bindings/python/bt2/bt2/trace_class.py b/bindings/python/bt2/bt2/trace_class.py deleted file mode 100644 index 5713db8c..00000000 --- a/bindings/python/bt2/bt2/trace_class.py +++ /dev/null @@ -1,335 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# Copyright (c) 2018 Francis Deslauriers -# Copyright (c) 2019 Simon Marchi -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -__all__ = ['_TraceClass'] - -import bt2 -from bt2 import native_bt, utils, object -import bt2.stream_class -import uuid as uuidp -import collections.abc -import functools - - -class _TraceClassEnv(collections.abc.MutableMapping): - def __init__(self, trace_class): - self._trace_class = trace_class - - def __getitem__(self, key): - utils._check_str(key) - - borrow_entry_fn = native_bt.trace_class_borrow_environment_entry_value_by_name_const - value_ptr = borrow_entry_fn(self._trace_class._ptr, key) - - if value_ptr is None: - raise KeyError(key) - - return bt2.value._create_from_ptr_and_get_ref(value_ptr) - - def __setitem__(self, key, value): - if isinstance(value, str): - set_env_entry_fn = native_bt.trace_class_set_environment_entry_string - elif isinstance(value, int): - set_env_entry_fn = native_bt.trace_class_set_environment_entry_integer - else: - raise TypeError('expected str or int, got {}'.format(type(value))) - - ret = set_env_entry_fn(self._trace_class._ptr, key, value) - - utils._handle_ret(ret, "cannot set trace class object's environment entry") - - def __delitem__(self, key): - raise NotImplementedError - - def __len__(self): - count = native_bt.trace_class_get_environment_entry_count(self._trace_class._ptr) - assert count >= 0 - return count - - def __iter__(self): - trace_class_ptr = self._trace_class_env._trace_class._ptr - - for idx in range(len(self)): - borrow_entry_fn = native_bt.trace_class_borrow_environment_entry_by_index_const - entry_name, _ = borrow_entry_fn(trace_class_ptr, idx) - assert entry_name is not None - yield entry_name - - -class _StreamClassIterator(collections.abc.Iterator): - def __init__(self, trace_class): - self._trace_class = trace_class - self._at = 0 - - def __next__(self): - if self._at == len(self._trace_class): - raise StopIteration - - borrow_stream_class_fn = native_bt.trace_class_borrow_stream_class_by_index_const - sc_ptr = borrow_stream_class_fn(self._trace_class._ptr, self._at) - assert sc_ptr - id = native_bt.stream_class_get_id(sc_ptr) - assert id >= 0 - self._at += 1 - return id - - -def _trace_class_destruction_listener_from_native(user_listener, trace_class_ptr): - trace_class = bt2.trace_class._TraceClass._create_from_ptr_and_get_ref(trace_class_ptr) - user_listener(trace_class) - - -class _TraceClass(object._SharedObject, collections.abc.Mapping): - _get_ref = staticmethod(native_bt.trace_class_get_ref) - _put_ref = staticmethod(native_bt.trace_class_put_ref) - - @property - def uuid(self): - uuid_bytes = native_bt.trace_class_get_uuid(self._ptr) - if uuid_bytes is None: - return - - return uuidp.UUID(bytes=uuid_bytes) - - def _uuid(self, uuid): - utils._check_type(uuid, uuidp.UUID) - native_bt.trace_class_set_uuid(self._ptr, uuid.bytes) - - _uuid = property(fset=_uuid) - - # Instantiate a trace of this class. - - def __call__(self, name=None): - trace_ptr = native_bt.trace_create(self._ptr) - - if trace_ptr is None: - raise bt2.CreationError('cannot create trace class object') - - trace = bt2.trace._Trace._create_from_ptr(trace_ptr) - - if name is not None: - trace._name = name - - return trace - - # Number of stream classes in this trace class. - - def __len__(self): - count = native_bt.trace_class_get_stream_class_count(self._ptr) - assert count >= 0 - return count - - # Get a stream class by stream id. - - def __getitem__(self, key): - utils._check_uint64(key) - - sc_ptr = native_bt.trace_class_borrow_stream_class_by_id_const(self._ptr, key) - if sc_ptr is None: - raise KeyError(key) - - return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(sc_ptr) - - def __iter__(self): - for idx in range(len(self)): - sc_ptr = native_bt.trace_class_borrow_stream_class_by_index_const(self._ptr, idx) - assert sc_ptr is not None - - id = native_bt.stream_class_get_id(sc_ptr) - assert id >= 0 - - yield id - - @property - def env(self): - return _TraceClassEnv(self) - - def create_stream_class(self, id=None, - name=None, - packet_context_field_class=None, - event_common_context_field_class=None, - default_clock_class=None, - assigns_automatic_event_class_id=True, - assigns_automatic_stream_id=True, - packets_have_beginning_default_clock_snapshot=False, - packets_have_end_default_clock_snapshot=False, - supports_discarded_events=False, - discarded_events_have_default_clock_snapshots=False, - supports_discarded_packets=False, - discarded_packets_have_default_clock_snapshots=False): - - if self.assigns_automatic_stream_class_id: - if id is not None: - raise ValueError('id provided, but trace class assigns automatic stream class ids') - - sc_ptr = native_bt.stream_class_create(self._ptr) - else: - if id is None: - raise ValueError('id not provided, but trace class does not assign automatic stream class ids') - - utils._check_uint64(id) - sc_ptr = native_bt.stream_class_create_with_id(self._ptr, id) - - sc = bt2.stream_class._StreamClass._create_from_ptr(sc_ptr) - - if name is not None: - sc._name = name - - if packet_context_field_class is not None: - sc._packet_context_field_class = packet_context_field_class - - if event_common_context_field_class is not None: - sc._event_common_context_field_class = event_common_context_field_class - - if default_clock_class is not None: - sc._default_clock_class = default_clock_class - - sc._assigns_automatic_event_class_id = assigns_automatic_event_class_id - sc._assigns_automatic_stream_id = assigns_automatic_stream_id - sc._packets_have_beginning_default_clock_snapshot = packets_have_beginning_default_clock_snapshot - sc._packets_have_end_default_clock_snapshot = packets_have_end_default_clock_snapshot - sc._set_supports_discarded_events(supports_discarded_events, - discarded_events_have_default_clock_snapshots) - sc._set_supports_discarded_packets(supports_discarded_packets, - discarded_packets_have_default_clock_snapshots) - return sc - - @property - def assigns_automatic_stream_class_id(self): - return native_bt.trace_class_assigns_automatic_stream_class_id(self._ptr) - - def _assigns_automatic_stream_class_id(self, auto_id): - utils._check_bool(auto_id) - return native_bt.trace_class_set_assigns_automatic_stream_class_id(self._ptr, auto_id) - - _assigns_automatic_stream_class_id = property(fset=_assigns_automatic_stream_class_id) - - # Field class creation methods. - - def _check_create_status(self, ptr, type_name): - if ptr is None: - raise bt2.CreationError( - 'cannot create {} field class'.format(type_name)) - - def _create_integer_field_class(self, create_func, py_cls, type_name, field_value_range, preferred_display_base): - field_class_ptr = create_func(self._ptr) - self._check_create_status(field_class_ptr, type_name) - - field_class = py_cls._create_from_ptr(field_class_ptr) - - if field_value_range is not None: - field_class._field_value_range = field_value_range - - if preferred_display_base is not None: - field_class._preferred_display_base = preferred_display_base - - return field_class - - def create_signed_integer_field_class(self, field_value_range=None, preferred_display_base=None): - return self._create_integer_field_class(native_bt.field_class_signed_integer_create, - bt2.field_class._SignedIntegerFieldClass, - 'signed integer', field_value_range, preferred_display_base) - - def create_unsigned_integer_field_class(self, field_value_range=None, preferred_display_base=None): - return self._create_integer_field_class(native_bt.field_class_unsigned_integer_create, - bt2.field_class._UnsignedIntegerFieldClass, - 'unsigned integer', field_value_range, preferred_display_base) - - def create_signed_enumeration_field_class(self, field_value_range=None, preferred_display_base=None): - return self._create_integer_field_class(native_bt.field_class_signed_enumeration_create, - bt2.field_class._SignedEnumerationFieldClass, - 'signed enumeration', field_value_range, preferred_display_base) - - def create_unsigned_enumeration_field_class(self, field_value_range=None, preferred_display_base=None): - return self._create_integer_field_class(native_bt.field_class_unsigned_enumeration_create, - bt2.field_class._UnsignedEnumerationFieldClass, - 'unsigned enumeration', field_value_range, preferred_display_base) - - def create_real_field_class(self, is_single_precision=False): - field_class_ptr = native_bt.field_class_real_create(self._ptr) - self._check_create_status(field_class_ptr, 'real') - - field_class = bt2.field_class._RealFieldClass._create_from_ptr(field_class_ptr) - - field_class._is_single_precision = is_single_precision - - return field_class - - def create_structure_field_class(self): - field_class_ptr = native_bt.field_class_structure_create(self._ptr) - self._check_create_status(field_class_ptr, 'structure') - - return bt2.field_class._StructureFieldClass._create_from_ptr(field_class_ptr) - - def create_string_field_class(self): - field_class_ptr = native_bt.field_class_string_create(self._ptr) - self._check_create_status(field_class_ptr, 'string') - - return bt2.field_class._StringFieldClass._create_from_ptr(field_class_ptr) - - def create_static_array_field_class(self, elem_fc, length): - utils._check_type(elem_fc, bt2.field_class._FieldClass) - utils._check_uint64(length) - ptr = native_bt.field_class_static_array_create(self._ptr, elem_fc._ptr, length) - self._check_create_status(ptr, 'static array') - - return bt2.field_class._StaticArrayFieldClass._create_from_ptr_and_get_ref(ptr) - - def create_dynamic_array_field_class(self, elem_fc, length_fc=None): - utils._check_type(elem_fc, bt2.field_class._FieldClass) - ptr = native_bt.field_class_dynamic_array_create(self._ptr, elem_fc._ptr) - self._check_create_status(ptr, 'dynamic array') - obj = bt2.field_class._DynamicArrayFieldClass._create_from_ptr(ptr) - - if length_fc is not None: - obj._length_field_class = length_fc - - return obj - - def create_variant_field_class(self, selector_fc=None): - ptr = native_bt.field_class_variant_create(self._ptr) - self._check_create_status(ptr, 'variant') - obj = bt2.field_class._VariantFieldClass._create_from_ptr(ptr) - - if selector_fc is not None: - obj._selector_field_class = selector_fc - - return obj - - # Add a listener to be called when the trace class is destroyed. - - def add_destruction_listener(self, listener): - - if not callable(listener): - raise TypeError("'listener' parameter is not callable") - - fn = native_bt.py3_trace_class_add_destruction_listener - listener_from_native = functools.partial(_trace_class_destruction_listener_from_native, - listener) - - listener_id = fn(self._ptr, listener_from_native) - if listener_id is None: - utils._raise_bt2_error('cannot add destruction listener to trace class object') - - return bt2._ListenerHandle(listener_id, self) diff --git a/bindings/python/bt2/bt2/trace_collection_message_iterator.py b/bindings/python/bt2/bt2/trace_collection_message_iterator.py deleted file mode 100644 index 10b555f1..00000000 --- a/bindings/python/bt2/bt2/trace_collection_message_iterator.py +++ /dev/null @@ -1,322 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import utils -import bt2 -import itertools -import bt2.message_iterator -import datetime -from collections import namedtuple -import numbers - - -# a pair of component and ComponentSpec -_ComponentAndSpec = namedtuple('_ComponentAndSpec', ['comp', 'spec']) - - -class ComponentSpec: - def __init__(self, plugin_name, class_name, params=None): - utils._check_str(plugin_name) - utils._check_str(class_name) - self._plugin_name = plugin_name - self._class_name = class_name - - if type(params) is str: - self._params = bt2.create_value({'paths': [params]}) - else: - self._params = bt2.create_value(params) - - @property - def plugin_name(self): - return self._plugin_name - - @property - def class_name(self): - return self._class_name - - @property - def params(self): - return self._params - - -# datetime.datetime or integral to nanoseconds -def _get_ns(obj): - if obj is None: - return - - if isinstance(obj, numbers.Real): - # consider that it's already in seconds - s = obj - elif isinstance(obj, datetime.datetime): - # s -> ns - s = obj.timestamp() - else: - raise TypeError('"{}" is not an integral number or a datetime.datetime object'.format(obj)) - - return int(s * 1e9) - - -class _CompClsType: - SOURCE = 0 - FILTER = 1 - - -class TraceCollectionMessageIterator(bt2.message_iterator._MessageIterator): - def __init__(self, source_component_specs, filter_component_specs=None, - stream_intersection_mode=False, begin=None, end=None): - utils._check_bool(stream_intersection_mode) - self._stream_intersection_mode = stream_intersection_mode - self._begin_ns = _get_ns(begin) - self._end_ns = _get_ns(end) - - if type(source_component_specs) is ComponentSpec: - source_component_specs = [source_component_specs] - - if type(filter_component_specs) is ComponentSpec: - filter_component_specs = [filter_component_specs] - elif filter_component_specs is None: - filter_component_specs = [] - - self._src_comp_specs = source_component_specs - self._flt_comp_specs = filter_component_specs - self._next_suffix = 1 - self._connect_ports = False - - # lists of _ComponentAndSpec - self._src_comps_and_specs = [] - self._flt_comps_and_specs = [] - - self._validate_component_specs(source_component_specs) - self._validate_component_specs(filter_component_specs) - self._build_graph() - - def _validate_component_specs(self, comp_specs): - for comp_spec in comp_specs: - if type(comp_spec) is not ComponentSpec: - raise TypeError('"{}" object is not a ComponentSpec'.format(type(comp_spec))) - - def __next__(self): - return next(self._msg_iter) - - def _create_stream_intersection_trimmer(self, component, port): - # find the original parameters specified by the user to create - # this port's component to get the `path` parameter - for src_comp_and_spec in self._src_comps_and_specs: - if component == src_comp_and_spec.comp: - break - - try: - paths = src_comp_and_spec.spec.params['paths'] - except Exception as e: - raise bt2.Error('all source components must be created with a "paths" parameter in stream intersection mode') from e - - params = {'paths': paths} - - # query the port's component for the `trace-info` object which - # contains the stream intersection range for each exposed - # trace - query_exec = bt2.QueryExecutor() - trace_info_res = query_exec.query(src_comp_and_spec.comp.cls, - 'trace-info', params) - begin = None - end = None - - # find the trace info for this port's trace - try: - for trace_info in trace_info_res: - for stream in trace_info['streams']: - if stream['port-name'] == port.name: - range_ns = trace_info['intersection-range-ns'] - begin = range_ns['begin'] - end = range_ns['end'] - break - except Exception: - pass - - if begin is None or end is None: - raise bt2.Error('cannot find stream intersection range for port "{}"'.format(port.name)) - - name = 'trimmer-{}-{}'.format(src_comp_and_spec.comp.name, port.name) - return self._create_trimmer(begin, end, name) - - def _create_muxer(self): - plugin = bt2.find_plugin('utils') - - if plugin is None: - raise bt2.Error('cannot find "utils" plugin (needed for the muxer)') - - if 'muxer' not in plugin.filter_component_classes: - raise bt2.Error('cannot find "muxer" filter component class in "utils" plugin') - - comp_cls = plugin.filter_component_classes['muxer'] - return self._graph.add_component(comp_cls, 'muxer') - - def _create_trimmer(self, begin_ns, end_ns, name): - plugin = bt2.find_plugin('utils') - - if plugin is None: - raise bt2.Error('cannot find "utils" plugin (needed for the trimmer)') - - if 'trimmer' not in plugin.filter_component_classes: - raise bt2.Error('cannot find "trimmer" filter component class in "utils" plugin') - - params = {} - - def ns_to_string(ns): - s_part = ns // 1000000000 - ns_part = ns % 1000000000 - return '{}.{:09d}'.format(s_part, ns_part) - - if begin_ns is not None: - params['begin'] = ns_to_string(begin_ns) - - if end_ns is not None: - params['end'] = ns_to_string(end_ns) - - comp_cls = plugin.filter_component_classes['trimmer'] - return self._graph.add_component(comp_cls, name, params) - - def _get_unique_comp_name(self, comp_spec): - name = '{}-{}'.format(comp_spec.plugin_name, - comp_spec.class_name) - comps_and_specs = itertools.chain(self._src_comps_and_specs, - self._flt_comps_and_specs) - - if name in [comp_and_spec.comp.name for comp_and_spec in comps_and_specs]: - name += '-{}'.format(self._next_suffix) - self._next_suffix += 1 - - return name - - def _create_comp(self, comp_spec, comp_cls_type): - plugin = bt2.find_plugin(comp_spec.plugin_name) - - if plugin is None: - raise bt2.Error('no such plugin: {}'.format(comp_spec.plugin_name)) - - if comp_cls_type == _CompClsType.SOURCE: - comp_classes = plugin.source_component_classes - else: - comp_classes = plugin.filter_component_classes - - if comp_spec.class_name not in comp_classes: - cc_type = 'source' if comp_cls_type == _CompClsType.SOURCE else 'filter' - raise bt2.Error('no such {} component class in "{}" plugin: {}'.format(cc_type, - comp_spec.plugin_name, - comp_spec.class_name)) - - comp_cls = comp_classes[comp_spec.class_name] - name = self._get_unique_comp_name(comp_spec) - comp = self._graph.add_component(comp_cls, name, comp_spec.params) - return comp - - def _get_free_muxer_input_port(self): - for port in self._muxer_comp.input_ports.values(): - if not port.is_connected: - return port - - def _connect_src_comp_port(self, component, port): - # if this trace collection iterator is in stream intersection - # mode, we need this connection: - # - # port -> trimmer -> muxer - # - # otherwise, simply: - # - # port -> muxer - if self._stream_intersection_mode: - trimmer_comp = self._create_stream_intersection_trimmer(component, port) - self._graph.connect_ports(port, trimmer_comp.input_ports['in']) - port_to_muxer = trimmer_comp.output_ports['out'] - else: - port_to_muxer = port - - self._graph.connect_ports(port_to_muxer, self._get_free_muxer_input_port()) - - def _graph_port_added(self, component, port): - if not self._connect_ports: - return - - if type(port) is bt2.port._InputPort: - return - - if component not in [comp.comp for comp in self._src_comps_and_specs]: - # do not care about non-source components (muxer, trimmer, etc.) - return - - self._connect_src_comp_port(component, port) - - def _build_graph(self): - self._graph = bt2.Graph() - self._graph.add_port_added_listener(self._graph_port_added) - self._muxer_comp = self._create_muxer() - - if self._begin_ns is not None or self._end_ns is not None: - trimmer_comp = self._create_trimmer(self._begin_ns, - self._end_ns, 'trimmer') - self._graph.connect_ports(self._muxer_comp.output_ports['out'], - trimmer_comp.input_ports['in']) - msg_iter_port = trimmer_comp.output_ports['out'] - else: - msg_iter_port = self._muxer_comp.output_ports['out'] - - # create extra filter components (chained) - for comp_spec in self._flt_comp_specs: - comp = self._create_comp(comp_spec, _CompClsType.FILTER) - self._flt_comps_and_specs.append(_ComponentAndSpec(comp, comp_spec)) - - # connect the extra filter chain - for comp_and_spec in self._flt_comps_and_specs: - in_port = list(comp_and_spec.comp.input_ports.values())[0] - out_port = list(comp_and_spec.comp.output_ports.values())[0] - self._graph.connect_ports(msg_iter_port, in_port) - msg_iter_port = out_port - - # Here we create the components, self._graph_port_added() is - # called when they add ports, but the callback returns early - # because self._connect_ports is False. This is because the - # self._graph_port_added() could not find the associated source - # component specification in self._src_comps_and_specs because - # it does not exist yet (it needs the created component to - # exist). - for comp_spec in self._src_comp_specs: - comp = self._create_comp(comp_spec, _CompClsType.SOURCE) - self._src_comps_and_specs.append(_ComponentAndSpec(comp, comp_spec)) - - # Now we connect the ports which exist at this point. We allow - # self._graph_port_added() to automatically connect _new_ ports. - self._connect_ports = True - - for comp_and_spec in self._src_comps_and_specs: - # Keep a separate list because comp_and_spec.output_ports - # could change during the connection of one of its ports. - # Any new port is handled by self._graph_port_added(). - out_ports = [port for port in comp_and_spec.comp.output_ports.values()] - - for out_port in out_ports: - if out_port.is_connected: - continue - - self._connect_src_comp_port(comp_and_spec.comp, out_port) - - # create this trace collection iterator's message iterator - self._msg_iter = self._graph.create_output_port_message_iterator(msg_iter_port) diff --git a/bindings/python/bt2/bt2/utils.py b/bindings/python/bt2/bt2/utils.py deleted file mode 100644 index bd8ebf8e..00000000 --- a/bindings/python/bt2/bt2/utils.py +++ /dev/null @@ -1,109 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import bt2 - - -def _check_bool(o): - if not isinstance(o, bool): - raise TypeError("'{}' is not a 'bool' object".format(o.__class__.__name__)) - - -def _check_int(o): - if not isinstance(o, int): - raise TypeError("'{}' is not an 'int' object".format(o.__class__.__name__)) - - -def _check_float(o): - if not isinstance(o, float): - raise TypeError("'{}' is not a 'float' object".format(o.__class__.__name__)) - - -def _check_str(o): - if not isinstance(o, str): - raise TypeError("'{}' is not a 'str' object".format(o.__class__.__name__)) - - -def _check_type(o, expected_type): - if not isinstance(o, expected_type): - raise TypeError("'{}' is not a '{}' object".format(o.__class__.__name__, - expected_type)) - - -def _is_int64(v): - _check_int(v) - return v >= -(2**63) and v <= (2**63 - 1) - - -def _is_uint64(v): - _check_int(v) - return v >= 0 and v <= (2**64 - 1) - - -def _check_int64(v, msg=None): - if not _is_int64(v): - if msg is None: - msg = 'expecting a signed 64-bit integral value' - - msg += ' (got {})'.format(v) - raise ValueError(msg) - - -def _check_uint64(v, msg=None): - if not _is_uint64(v): - if msg is None: - msg = 'expecting an unsigned 64-bit integral value' - - msg += ' (got {})'.format(v) - raise ValueError(msg) - - -def _is_m1ull(v): - return v == 18446744073709551615 - - -def _is_pow2(v): - return v != 0 and ((v & (v - 1)) == 0) - - -def _check_alignment(a): - _check_uint64(a) - - if not _is_pow2(a): - raise ValueError('{} is not a power of two'.format(a)) - - -def _raise_bt2_error(msg): - if msg is None: - raise bt2.Error - else: - raise bt2.Error(msg) - - -def _handle_ret(ret, msg=None): - if int(ret) < 0: - _raise_bt2_error(msg) - - -def _handle_ptr(ptr, msg=None): - if ptr is None: - _raise_bt2_error(msg) diff --git a/bindings/python/bt2/bt2/value.py b/bindings/python/bt2/bt2/value.py deleted file mode 100644 index d8b8332e..00000000 --- a/bindings/python/bt2/bt2/value.py +++ /dev/null @@ -1,712 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Philippe Proulx -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -from bt2 import native_bt, object, utils -import collections.abc -import functools -import numbers -import math -import abc -import bt2 - - -def _handle_status(status, obj_name): - if status >= 0: - return - else: - raise RuntimeError('unexpected error') - - -def _create_from_ptr(ptr): - if ptr is None or ptr == native_bt.value_null: - return - - typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr(ptr) - - -def _create_from_ptr_and_get_ref(ptr): - if ptr is None or ptr == native_bt.value_null: - return - - typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) - - -def create_value(value): - if value is None: - # null value object - return - - if isinstance(value, _Value): - return value - - if isinstance(value, bool): - return BoolValue(value) - - if isinstance(value, int): - return SignedIntegerValue(value) - - if isinstance(value, float): - return RealValue(value) - - if isinstance(value, str): - return StringValue(value) - - try: - return MapValue(value) - except: - pass - - try: - return ArrayValue(value) - except: - pass - - raise TypeError("cannot create value object from '{}' object".format(value.__class__.__name__)) - - -class _Value(object._SharedObject, metaclass=abc.ABCMeta): - _get_ref = staticmethod(native_bt.value_get_ref) - _put_ref = staticmethod(native_bt.value_put_ref) - - def __eq__(self, other): - if other is None: - # self is never the null value object - return False - - # try type-specific comparison first - spec_eq = self._spec_eq(other) - - if spec_eq is not None: - return spec_eq - - if not isinstance(other, _Value): - # not comparing apples to apples - return False - - # fall back to native comparison function - return native_bt.value_compare(self._ptr, other._ptr) - - def __ne__(self, other): - return not (self == other) - - @abc.abstractmethod - def _spec_eq(self, other): - pass - - def _handle_status(self, status): - _handle_status(status, self._NAME) - - def _check_create_status(self, ptr): - if ptr is None: - raise bt2.CreationError( - 'cannot create {} value object'.format(self._NAME.lower())) - - -@functools.total_ordering -class _NumericValue(_Value): - @staticmethod - def _extract_value(other): - if isinstance(other, _NumericValue): - return other._value - - if other is True or other is False: - return other - - if isinstance(other, numbers.Integral): - return int(other) - - if isinstance(other, numbers.Real): - return float(other) - - if isinstance(other, numbers.Complex): - return complex(other) - - raise TypeError("'{}' object is not a number object".format(other.__class__.__name__)) - - def __int__(self): - return int(self._value) - - def __float__(self): - return float(self._value) - - def __repr__(self): - return repr(self._value) - - def __lt__(self, other): - if not isinstance(other, numbers.Number): - raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__, - other.__class__.__name__)) - - return self._value < float(other) - - def __le__(self, other): - if not isinstance(other, numbers.Number): - raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__, - other.__class__.__name__)) - - return self._value <= float(other) - - def _spec_eq(self, other): - pass - - def __eq__(self, other): - if not isinstance(other, numbers.Number): - return False - - return self._value == complex(other) - - def __rmod__(self, other): - return self._extract_value(other) % self._value - - def __mod__(self, other): - return self._value % self._extract_value(other) - - def __rfloordiv__(self, other): - return self._extract_value(other) // self._value - - def __floordiv__(self, other): - return self._value // self._extract_value(other) - - def __round__(self, ndigits=None): - if ndigits is None: - return round(self._value) - else: - return round(self._value, ndigits) - - def __ceil__(self): - return math.ceil(self._value) - - def __floor__(self): - return math.floor(self._value) - - def __trunc__(self): - return int(self._value) - - def __abs__(self): - return abs(self._value) - - def __add__(self, other): - return self._value + self._extract_value(other) - - def __radd__(self, other): - return self.__add__(other) - - def __neg__(self): - return -self._value - - def __pos__(self): - return +self._value - - def __mul__(self, other): - return self._value * self._extract_value(other) - - def __rmul__(self, other): - return self.__mul__(other) - - def __truediv__(self, other): - return self._value / self._extract_value(other) - - def __rtruediv__(self, other): - return self._extract_value(other) / self._value - - def __pow__(self, exponent): - return self._value ** self._extract_value(exponent) - - def __rpow__(self, base): - return self._extract_value(base) ** self._value - - def __iadd__(self, other): - self.value = self + other - return self - - def __isub__(self, other): - self.value = self - other - return self - - def __imul__(self, other): - self.value = self * other - return self - - def __itruediv__(self, other): - self.value = self / other - return self - - def __ifloordiv__(self, other): - self.value = self // other - return self - - def __imod__(self, other): - self.value = self % other - return self - - def __ipow__(self, other): - self.value = self ** other - return self - - -class _IntegralValue(_NumericValue, numbers.Integral): - def __lshift__(self, other): - return self._value << self._extract_value(other) - - def __rlshift__(self, other): - return self._extract_value(other) << self._value - - def __rshift__(self, other): - return self._value >> self._extract_value(other) - - def __rrshift__(self, other): - return self._extract_value(other) >> self._value - - def __and__(self, other): - return self._value & self._extract_value(other) - - def __rand__(self, other): - return self._extract_value(other) & self._value - - def __xor__(self, other): - return self._value ^ self._extract_value(other) - - def __rxor__(self, other): - return self._extract_value(other) ^ self._value - - def __or__(self, other): - return self._value | self._extract_value(other) - - def __ror__(self, other): - return self._extract_value(other) | self._value - - def __invert__(self): - return ~self._value - - def __ilshift__(self, other): - self.value = self << other - return self - - def __irshift__(self, other): - self.value = self >> other - return self - - def __iand__(self, other): - self.value = self & other - return self - - def __ixor__(self, other): - self.value = self ^ other - return self - - def __ior__(self, other): - self.value = self | other - return self - - -class _RealValue(_NumericValue, numbers.Real): - pass - - -class BoolValue(_Value): - _NAME = 'Boolean' - - def __init__(self, value=None): - if value is None: - ptr = native_bt.value_bool_create() - else: - ptr = native_bt.value_bool_create_init(self._value_to_bool(value)) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _spec_eq(self, other): - if isinstance(other, numbers.Number): - return self._value == bool(other) - - def __bool__(self): - return self._value - - def __repr__(self): - return repr(self._value) - - def _value_to_bool(self, value): - if isinstance(value, BoolValue): - value = value._value - - if not isinstance(value, bool): - raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value.__class__)) - - return int(value) - - @property - def _value(self): - value = native_bt.value_bool_get(self._ptr) - return value != 0 - - def _set_value(self, value): - native_bt.value_bool_set(self._ptr, self._value_to_bool(value)) - - value = property(fset=_set_value) - - -class _IntegerValue(_IntegralValue): - def __init__(self, value=None): - if value is None: - ptr = self._create_default_value() - else: - ptr = self._create_value(self._value_to_int(value)) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _value_to_int(self, value): - if not isinstance(value, numbers.Real): - raise TypeError('expecting a number object') - - value = int(value) - self._check_int_range(value) - return value - - @property - def _value(self): - return self._get_value(self._ptr) - - def _prop_set_value(self, value): - self._set_value(self._ptr, self._value_to_int(value)) - - value = property(fset=_prop_set_value) - - -class UnsignedIntegerValue(_IntegerValue): - _check_int_range = staticmethod(utils._check_uint64) - _create_default_value = staticmethod(native_bt.value_unsigned_integer_create) - _create_value = staticmethod(native_bt.value_unsigned_integer_create_init) - _set_value = staticmethod(native_bt.value_unsigned_integer_set) - _get_value = staticmethod(native_bt.value_unsigned_integer_get) - - -class SignedIntegerValue(_IntegerValue): - _check_int_range = staticmethod(utils._check_int64) - _create_default_value = staticmethod(native_bt.value_signed_integer_create) - _create_value = staticmethod(native_bt.value_signed_integer_create_init) - _set_value = staticmethod(native_bt.value_signed_integer_set) - _get_value = staticmethod(native_bt.value_signed_integer_get) - - -class RealValue(_RealValue): - _NAME = 'Real number' - - def __init__(self, value=None): - if value is None: - ptr = native_bt.value_real_create() - else: - value = self._value_to_float(value) - ptr = native_bt.value_real_create_init(value) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _value_to_float(self, value): - if not isinstance(value, numbers.Real): - raise TypeError("expecting a real number object") - - return float(value) - - @property - def _value(self): - return native_bt.value_real_get(self._ptr) - - def _set_value(self, value): - native_bt.value_real_set(self._ptr, self._value_to_float(value)) - - value = property(fset=_set_value) - - -@functools.total_ordering -class StringValue(collections.abc.Sequence, _Value): - _NAME = 'String' - - def __init__(self, value=None): - if value is None: - ptr = native_bt.value_string_create() - else: - ptr = native_bt.value_string_create_init(self._value_to_str(value)) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _value_to_str(self, value): - if isinstance(value, self.__class__): - value = value._value - - utils._check_str(value) - return value - - @property - def _value(self): - return native_bt.value_string_get(self._ptr) - - def _set_value(self, value): - status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) - self._handle_status(status) - - value = property(fset=_set_value) - - def _spec_eq(self, other): - try: - return self._value == self._value_to_str(other) - except: - return - - def __le__(self, other): - return self._value <= self._value_to_str(other) - - def __lt__(self, other): - return self._value < self._value_to_str(other) - - def __bool__(self): - return bool(self._value) - - def __repr__(self): - return repr(self._value) - - def __str__(self): - return self._value - - def __getitem__(self, index): - return self._value[index] - - def __len__(self): - return len(self._value) - - def __iadd__(self, value): - curvalue = self._value - curvalue += self._value_to_str(value) - self.value = curvalue - return self - - -class _Container: - def __bool__(self): - return len(self) != 0 - - def __delitem__(self, index): - raise NotImplementedError - - -class ArrayValue(_Container, collections.abc.MutableSequence, _Value): - _NAME = 'Array' - - def __init__(self, value=None): - ptr = native_bt.value_array_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable protocol. - if value is not None: - for elem in value: - self.append(elem) - - def _spec_eq(self, other): - try: - if len(self) != len(other): - # early mismatch - return False - - for self_elem, other_elem in zip(self, other): - if self_elem != other_elem: - return False - - return True - except: - return - - def __len__(self): - size = native_bt.value_array_get_size(self._ptr) - assert(size >= 0) - return size - - def _check_index(self, index): - # TODO: support slices also - if not isinstance(index, numbers.Integral): - raise TypeError("'{}' object is not an integral number object: invalid index".format(index.__class__.__name__)) - - index = int(index) - - if index < 0 or index >= len(self): - raise IndexError('array value object index is out of range') - - def __getitem__(self, index): - self._check_index(index) - ptr = native_bt.value_array_borrow_element_by_index(self._ptr, index) - assert(ptr) - return _create_from_ptr_and_get_ref(ptr) - - def __setitem__(self, index, value): - self._check_index(index) - value = create_value(value) - - if value is None: - ptr = native_bt.value_null - else: - ptr = value._ptr - - status = native_bt.value_array_set_element_by_index( - self._ptr, index, ptr) - self._handle_status(status) - - def append(self, value): - value = create_value(value) - - if value is None: - ptr = native_bt.value_null - else: - ptr = value._ptr - - status = native_bt.value_array_append_element(self._ptr, ptr) - self._handle_status(status) - - def __iadd__(self, iterable): - # Python will raise a TypeError if there's anything wrong with - # the iterable protocol. - for elem in iterable: - self.append(elem) - - return self - - def __repr__(self): - return '[{}]'.format(', '.join([repr(v) for v in self])) - - def insert(self, value): - raise NotImplementedError - - -class _MapValueKeyIterator(collections.abc.Iterator): - def __init__(self, map_obj): - self._map_obj = map_obj - self._at = 0 - keys_ptr = native_bt.value_map_get_keys(map_obj._ptr) - - if keys_ptr is None: - raise RuntimeError('unexpected error: cannot get map value object keys') - - self._keys = _create_from_ptr(keys_ptr) - - def __next__(self): - if self._at == len(self._map_obj): - raise StopIteration - - key = self._keys[self._at] - self._at += 1 - return str(key) - - -class MapValue(_Container, collections.abc.MutableMapping, _Value): - _NAME = 'Map' - - def __init__(self, value=None): - ptr = native_bt.value_map_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable/mapping protocol. - if value is not None: - for key, elem in value.items(): - self[key] = elem - - def __eq__(self, other): - return _Value.__eq__(self, other) - - def __ne__(self, other): - return _Value.__ne__(self, other) - - def _spec_eq(self, other): - try: - if len(self) != len(other): - # early mismatch - return False - - for self_key in self: - if self_key not in other: - return False - - self_value = self[self_key] - other_value = other[self_key] - - if self_value != other_value: - return False - - return True - except: - return - - def __len__(self): - size = native_bt.value_map_get_size(self._ptr) - assert(size >= 0) - return size - - def __contains__(self, key): - self._check_key_type(key) - return native_bt.value_map_has_entry(self._ptr, key) - - def _check_key_type(self, key): - utils._check_str(key) - - def _check_key(self, key): - if key not in self: - raise KeyError(key) - - def __getitem__(self, key): - self._check_key(key) - ptr = native_bt.value_map_borrow_entry_value(self._ptr, key) - assert(ptr) - return _create_from_ptr_and_get_ref(ptr) - - def __iter__(self): - return _MapValueKeyIterator(self) - - def __setitem__(self, key, value): - self._check_key_type(key) - value = create_value(value) - - if value is None: - ptr = native_bt.value_null - else: - ptr = value._ptr - - status = native_bt.value_map_insert_entry(self._ptr, key, ptr) - self._handle_status(status) - - def __repr__(self): - items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] - return '{{{}}}'.format(', '.join(items)) - - -_TYPE_TO_OBJ = { - native_bt.VALUE_TYPE_BOOL: BoolValue, - native_bt.VALUE_TYPE_UNSIGNED_INTEGER: UnsignedIntegerValue, - native_bt.VALUE_TYPE_SIGNED_INTEGER: SignedIntegerValue, - native_bt.VALUE_TYPE_REAL: RealValue, - native_bt.VALUE_TYPE_STRING: StringValue, - native_bt.VALUE_TYPE_ARRAY: ArrayValue, - native_bt.VALUE_TYPE_MAP: MapValue, -} diff --git a/bindings/python/bt2/setup.py.in b/bindings/python/bt2/setup.py.in deleted file mode 100644 index aeeac8f7..00000000 --- a/bindings/python/bt2/setup.py.in +++ /dev/null @@ -1,86 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (C) 2017 - Francis Deslauriers -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import sys - -from distutils.core import setup, Extension - -PY_PATH_WARN_MSG = """ --------------------------------------WARNING------------------------------------ -The install directory used:\n ({})\nis not included in your PYTHONPATH. - -To add this directory to your Python search path permanently you can add the -following command to your .bashrc/.zshrc: - export PYTHONPATH="${{PYTHONPATH}}:{}" --------------------------------------------------------------------------------- -""" - -def main(): - babeltrace_ext = Extension('bt2._native_bt', - sources=['bt2/native_bt.i', 'bt2/logging.c'], - libraries=['babeltrace2', 'glib-2.0'], - extra_objects=['@top_builddir@/logging/.libs/libbabeltrace2-logging.a', - '@top_builddir@/common/.libs/libbabeltrace2-common.a'],) - - dist = setup(name='bt2', - version='@PACKAGE_VERSION@', - description='Babeltrace 2 Python Bindings', - packages=['bt2'], - package_dir={'bt2': 'bt2'}, - options={'build': - { - 'build_base': 'build', - 'build_lib': 'build/build_lib' - }, - 'build_ext': - { - 'build_lib': 'build/build_lib' - } - }, - url='http://diamon.org/babeltrace', - ext_modules=[babeltrace_ext], - license='MIT', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: The MIT License', - 'Programming Language :: Python :: 3' - 'Topic :: System :: Logging', - ]) - -# After the installation, we check that the install directory is included in -# the Python search path and we print a warning message when it's not. -# We need to do this because Python search path differs depending on the distro -# and some distros don't include any /usr/local/ in the search path. This is -# also useful for out-of-tree installs and tests. -# It's only relevant to make this check on the `install` command. - - if 'install' in dist.command_obj: - install_dir = dist.command_obj['install'].install_libbase - if install_dir not in sys.path: - # We can't consider this an error because if affects every - # distro differently. We only warn the user that some - # extra configuration is needed to use the bindings - print(PY_PATH_WARN_MSG.format(install_dir, install_dir)) - -if __name__ == "__main__": - main() diff --git a/cli/Makefile.am b/cli/Makefile.am deleted file mode 100644 index 6a7cc881..00000000 --- a/cli/Makefile.am +++ /dev/null @@ -1,100 +0,0 @@ -PLUGINS_PATH = $(abs_top_builddir)/plugins -LTTNG_UTILS_PLUGIN_PATH = - -if ENABLE_DEBUG_INFO -LTTNG_UTILS_PLUGIN_PATH += :$(PLUGINS_PATH)/lttng-utils -endif - -if BABELTRACE_BUILD_WITH_MINGW -IN_TREE_PLUGIN_PATH := $(shell cygpath -pm "$(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/utils$(LTTNG_UTILS_PLUGIN_PATH)") -else -IN_TREE_PLUGIN_PATH = $(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/utils$(LTTNG_UTILS_PLUGIN_PATH) -endif - -AM_CPPFLAGS += '-DCONFIG_IN_TREE_PLUGIN_PATH="$(IN_TREE_PLUGIN_PATH)"' - -bin_PROGRAMS = babeltrace2.bin babeltrace2-log.bin -noinst_PROGRAMS = babeltrace2 babeltrace2-log - -babeltrace2_bin_SOURCES = \ - babeltrace2.c \ - babeltrace2-cfg.c \ - babeltrace2-cfg.h \ - babeltrace2-cfg-cli-args.c \ - babeltrace2-cfg-cli-args.h \ - babeltrace2-cfg-cli-args-connect.c \ - babeltrace2-cfg-cli-args-connect.h \ - babeltrace2-cfg-cli-args-default.h \ - babeltrace2-cfg-cli-args-default.c \ - logging.c logging.h - -# -Wl,--no-as-needed is needed for recent gold linker who seems to think -# it knows better and considers libraries with constructors having -# side-effects as dead code. -babeltrace2_bin_LDFLAGS = $(LD_NO_AS_NEEDED) - -# Add all the convenience libraries used by Babeltrace plugins and the -# library. They will be used when embedding plugins (--enable-built-in-plugins), -# otherwise we're looking after multiple definitions of the same symbols if -# a plugin's archive (.a) includes the convenience library because -# we're using --whole-archive below (needed to make sure the linker does -# not discard the plugins since the CLI does not use their symbols -# directly). -babeltrace2_bin_LDADD = \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/compat/libcompat.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(POPT_LIBS) - -if ENABLE_BUILT_IN_PLUGINS -# Takes a plugin name and outputs the needed LDFLAGS to embed it. -# -# The --whole-archive option is important here. From the GNU linker's -# documentation: -# -# For each archive mentioned on the command line after the -# --whole-archive option, include every object file in the archive in -# the link, rather than searching the archive for the required object -# files. -# -# In our case, we find the plugins thanks to special sections in the -# binary that are filled by plugin objects. If the linker discards those -# symbols because the CLI does not use them directly, the CLI reports -# no plugins found (plugins are effectively not embedded). -pluginarchive = $(LD_WHOLE_ARCHIVE)$(PLUGINS_PATH)/$(1)/.libs/babeltrace-plugin-$(1).a$(LD_NO_WHOLE_ARCHIVE) - -# Built-in plugins -babeltrace2_bin_LDFLAGS += $(call pluginarchive,ctf) -babeltrace2_bin_LDFLAGS += $(call pluginarchive,text) -babeltrace2_bin_LDFLAGS += $(call pluginarchive,utils) - -if ENABLE_DEBUG_INFO -babeltrace2_bin_LDFLAGS += $(call pluginarchive,lttng-utils) -babeltrace2_bin_LDADD += $(ELFUTILS_LIBS) -endif -endif - -if BABELTRACE_BUILD_WITH_MINGW -babeltrace2_bin_LDADD += -lws2_32 -lrpcrt4 -lintl -liconv -lole32 -lpthread -endif - -# Only used for in-tree execution and tests -babeltrace2_SOURCES = $(babeltrace2_bin_SOURCES) -babeltrace2_LDFLAGS = $(babeltrace2_bin_LDFLAGS) -babeltrace2_LDADD = $(babeltrace2_bin_LDADD) -babeltrace2_CFLAGS = $(AM_CFLAGS) -DBT_SET_DEFAULT_IN_TREE_CONFIGURATION - -# babeltrace2-log rules and config below -babeltrace2_log_bin_SOURCES = babeltrace2-log.c -babeltrace2_log_bin_LDADD = \ - $(top_builddir)/compat/libcompat.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(POPT_LIBS) -babeltrace2_log_bin_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(abs_top_builddir)/cli/babeltrace2$(EXEEXT)"' - -# Only used for in-tree execution and tests -babeltrace2_log_SOURCES = $(babeltrace2_log_bin_SOURCES) -babeltrace2_log_LDADD = $(babeltrace2_log_bin_LDADD) -babeltrace2_log_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(bindir)/babeltrace2$(EXEEXT)"' diff --git a/cli/babeltrace2-cfg-cli-args-connect.c b/cli/babeltrace2-cfg-cli-args-connect.c deleted file mode 100644 index f269ecc6..00000000 --- a/cli/babeltrace2-cfg-cli-args-connect.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "babeltrace2-cfg.h" -#include "babeltrace2-cfg-cli-args-connect.h" - -static bool all_named_and_printable_in_array(GPtrArray *comps) -{ - size_t i; - bool all_named_and_printable = true; - - for (i = 0; i < comps->len; i++) { - struct bt_config_component *comp = g_ptr_array_index(comps, i); - - if (comp->instance_name->len == 0) { - all_named_and_printable = false; - goto end; - } - - if (!bt_common_string_is_printable(comp->instance_name->str)) { - all_named_and_printable = false; - goto end; - } - } - -end: - return all_named_and_printable; -} - -static bool all_named_and_printable(struct bt_config *cfg) -{ - return all_named_and_printable_in_array(cfg->cmd_data.run.sources) && - all_named_and_printable_in_array(cfg->cmd_data.run.filters) && - all_named_and_printable_in_array(cfg->cmd_data.run.sinks); -} - -static struct bt_config_connection *bt_config_connection_create(const char *arg) -{ - struct bt_config_connection *cfg_connection; - - cfg_connection = g_new0(struct bt_config_connection, 1); - if (!cfg_connection) { - goto error; - } - - cfg_connection->upstream_comp_name = g_string_new(NULL); - if (!cfg_connection->upstream_comp_name) { - goto error; - } - - cfg_connection->downstream_comp_name = g_string_new(NULL); - if (!cfg_connection->downstream_comp_name) { - goto error; - } - - cfg_connection->upstream_port_glob = g_string_new("*"); - if (!cfg_connection->upstream_port_glob) { - goto error; - } - - cfg_connection->downstream_port_glob = g_string_new("*"); - if (!cfg_connection->downstream_port_glob) { - goto error; - } - - cfg_connection->arg = g_string_new(arg); - if (!cfg_connection->arg) { - goto error; - } - - goto end; - -error: - g_free(cfg_connection); - cfg_connection = NULL; - -end: - return cfg_connection; -} - -static bool validate_port_glob(const char *port_glob) -{ - bool is_valid = true; - const char *ch = port_glob; - - BT_ASSERT(port_glob); - - while (*ch != '\0') { - switch (*ch) { - case '\\': - switch (ch[1]) { - case '\0': - goto end; - default: - ch += 2; - continue; - } - case '?': - case '[': - /* - * This is reserved for future use, to support - * full globbing patterns. Those characters must - * be escaped with `\`. - */ - is_valid = false; - goto end; - default: - ch++; - break; - } - } - -end: - return is_valid; -} - -static int normalize_glob_pattern(GString *glob_pattern_gs) -{ - int ret = 0; - char *glob_pattern = strdup(glob_pattern_gs->str); - - if (!glob_pattern) { - ret = -1; - goto end; - } - - bt_common_normalize_star_glob_pattern(glob_pattern); - g_string_assign(glob_pattern_gs, glob_pattern); - free(glob_pattern); - -end: - return ret; -} - -static struct bt_config_connection *cfg_connection_from_arg(const char *arg) -{ - const char *at = arg; - size_t end_pos; - struct bt_config_connection *cfg_conn = NULL; - GString *gs = NULL; - enum { - UPSTREAM_NAME, - DOWNSTREAM_NAME, - UPSTREAM_PORT_GLOB, - DOWNSTREAM_PORT_GLOB, - } state = UPSTREAM_NAME; - - if (!bt_common_string_is_printable(arg)) { - goto error; - } - - cfg_conn = bt_config_connection_create(arg); - if (!cfg_conn) { - goto error; - } - - while (true) { - switch (state) { - case UPSTREAM_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->upstream_comp_name, TRUE); - cfg_conn->upstream_comp_name = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DOWNSTREAM_NAME; - } else if (at[end_pos] == '.') { - state = UPSTREAM_PORT_GLOB; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DOWNSTREAM_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->downstream_comp_name, TRUE); - cfg_conn->downstream_comp_name = gs; - gs = NULL; - - if (at[end_pos] == '.') { - state = DOWNSTREAM_PORT_GLOB; - } else if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - - at += end_pos + 1; - break; - case UPSTREAM_PORT_GLOB: - gs = bt_common_string_until(at, ".:", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - if (!validate_port_glob(gs->str)) { - goto error; - } - - if (normalize_glob_pattern(gs)) { - goto error; - } - - g_string_free(cfg_conn->upstream_port_glob, TRUE); - cfg_conn->upstream_port_glob = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DOWNSTREAM_NAME; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DOWNSTREAM_PORT_GLOB: - gs = bt_common_string_until(at, ".:", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - if (!validate_port_glob(gs->str)) { - goto error; - } - - if (normalize_glob_pattern(gs)) { - goto error; - } - - g_string_free(cfg_conn->downstream_port_glob, TRUE); - cfg_conn->downstream_port_glob = gs; - gs = NULL; - - if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - break; - default: - abort(); - } - } - -error: - bt_config_connection_destroy(cfg_conn); - cfg_conn = NULL; - -end: - if (gs) { - g_string_free(gs, TRUE); - } - - return cfg_conn; -} - -static struct bt_config_component *find_component_in_array(GPtrArray *comps, - const char *name) -{ - size_t i; - struct bt_config_component *found_comp = NULL; - - for (i = 0; i < comps->len; i++) { - struct bt_config_component *comp = g_ptr_array_index(comps, i); - - if (strcmp(name, comp->instance_name->str) == 0) { - found_comp = comp; - bt_object_get_ref(found_comp); - goto end; - } - } - -end: - return found_comp; -} - -static struct bt_config_component *find_component(struct bt_config *cfg, - const char *name) -{ - struct bt_config_component *comp; - - comp = find_component_in_array(cfg->cmd_data.run.sources, name); - if (comp) { - goto end; - } - - comp = find_component_in_array(cfg->cmd_data.run.filters, name); - if (comp) { - goto end; - } - - comp = find_component_in_array(cfg->cmd_data.run.sinks, name); - if (comp) { - goto end; - } - -end: - return comp; -} - -static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf, - size_t error_buf_size) -{ - size_t i; - int ret = 0; - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - struct bt_config_component *comp; - - comp = find_component(cfg, connection->upstream_comp_name->str); - bt_object_put_ref(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find upstream component `%s`:\n %s\n", - connection->upstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - comp = find_component(cfg, connection->downstream_comp_name->str); - bt_object_put_ref(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find downstream component `%s`:\n %s\n", - connection->downstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -static int validate_connection_directions(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - struct bt_config_component *src_comp = NULL; - struct bt_config_component *dst_comp = NULL; - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - src_comp = find_component(cfg, - connection->upstream_comp_name->str); - BT_ASSERT(src_comp); - dst_comp = find_component(cfg, - connection->downstream_comp_name->str); - BT_ASSERT(dst_comp); - - if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { - if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && - dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) { - snprintf(error_buf, error_buf_size, - "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n", - connection->upstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) { - if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && - dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) { - snprintf(error_buf, error_buf_size, - "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n", - connection->upstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } else { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n", - connection->upstream_comp_name->str, - connection->downstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - BT_OBJECT_PUT_REF_AND_RESET(src_comp); - BT_OBJECT_PUT_REF_AND_RESET(dst_comp); - } - -end: - bt_object_put_ref(src_comp); - bt_object_put_ref(dst_comp); - return ret; -} - -static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path, - char *error_buf, size_t error_buf_size) -{ - int ret = 0; - size_t conn_i; - const char *src_comp_name; - - BT_ASSERT(path && path->len > 0); - src_comp_name = g_ptr_array_index(path, path->len - 1); - - for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) { - struct bt_config_connection *conn = - g_ptr_array_index(cfg->cmd_data.run.connections, conn_i); - - if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) { - size_t path_i; - - for (path_i = 0; path_i < path->len; path_i++) { - const char *comp_name = - g_ptr_array_index(path, path_i); - - if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) { - snprintf(error_buf, error_buf_size, - "Invalid connection: connection forms a cycle:\n %s\n", - conn->arg->str); - ret = -1; - goto end; - } - } - - g_ptr_array_add(path, conn->downstream_comp_name->str); - ret = validate_no_cycles_rec(cfg, path, error_buf, - error_buf_size); - if (ret) { - goto end; - } - - g_ptr_array_remove_index(path, path->len - 1); - } - } - -end: - return ret; -} - -static int validate_no_cycles(struct bt_config *cfg, char *error_buf, - size_t error_buf_size) -{ - size_t i; - int ret = 0; - GPtrArray *path; - - path = g_ptr_array_new(); - if (!path) { - ret = -1; - goto end; - } - - g_ptr_array_add(path, NULL); - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *conn = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - g_ptr_array_index(path, 0) = conn->upstream_comp_name->str; - ret = validate_no_cycles_rec(cfg, path, - error_buf, error_buf_size); - if (ret) { - goto end; - } - } - -end: - if (path) { - g_ptr_array_free(path, TRUE); - } - - return ret; -} - -static int validate_all_components_connected_in_array(GPtrArray *comps, - const bt_value *connected_components, - char *error_buf, size_t error_buf_size) -{ - int ret = 0; - size_t i; - - for (i = 0; i < comps->len; i++) { - struct bt_config_component *comp = g_ptr_array_index(comps, i); - - if (!bt_value_map_has_entry(connected_components, - comp->instance_name->str)) { - snprintf(error_buf, error_buf_size, - "Component `%s` is not connected\n", - comp->instance_name->str); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -static int validate_all_components_connected(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - bt_value *connected_components = bt_value_map_create(); - - if (!connected_components) { - ret = -1; - goto end; - } - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - ret = bt_value_map_insert_entry(connected_components, - connection->upstream_comp_name->str, bt_value_null); - if (ret) { - goto end; - } - - ret = bt_value_map_insert_entry(connected_components, - connection->downstream_comp_name->str, bt_value_null); - if (ret) { - goto end; - } - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.sources, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.filters, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.sinks, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - -end: - bt_value_put_ref(connected_components); - return ret; -} - -static int validate_no_duplicate_connection(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - bt_value *flat_connection_names = - bt_value_map_create(); - GString *flat_connection_name = NULL; - - if (!flat_connection_names) { - ret = -1; - goto end; - } - - flat_connection_name = g_string_new(NULL); - if (!flat_connection_name) { - ret = -1; - goto end; - } - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s", - connection->upstream_comp_name->str, - connection->upstream_port_glob->str, - connection->downstream_comp_name->str, - connection->downstream_port_glob->str); - - if (bt_value_map_has_entry(flat_connection_names, - flat_connection_name->str)) { - snprintf(error_buf, error_buf_size, - "Duplicate connection:\n %s\n", - connection->arg->str); - ret = -1; - goto end; - } - - ret = bt_value_map_insert_entry(flat_connection_names, - flat_connection_name->str, bt_value_null); - if (ret) { - goto end; - } - } - -end: - bt_value_put_ref(flat_connection_names); - - if (flat_connection_name) { - g_string_free(flat_connection_name, TRUE); - } - - return ret; -} - -static int validate_connections(struct bt_config *cfg, char *error_buf, - size_t error_buf_size) -{ - int ret; - - ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_connection_directions(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_all_components_connected(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_no_cycles(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - -end: - return ret; -} - -int bt_config_cli_args_create_connections(struct bt_config *cfg, - const bt_value *connection_args, - char *error_buf, size_t error_buf_size) -{ - int ret; - size_t i; - - if (!all_named_and_printable(cfg)) { - snprintf(error_buf, error_buf_size, - "One or more components are unnamed (use --name) or contain a non-printable character\n"); - goto error; - } - - for (i = 0; i < bt_value_array_get_size(connection_args); i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index_const( - connection_args, i); - const char *arg; - struct bt_config_connection *cfg_connection; - - arg = bt_value_string_get(arg_value); - cfg_connection = cfg_connection_from_arg(arg); - if (!cfg_connection) { - snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n", - arg); - goto error; - } - - g_ptr_array_add(cfg->cmd_data.run.connections, - cfg_connection); - } - - - ret = validate_connections(cfg, error_buf, error_buf_size); - if (ret) { - goto error; - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} diff --git a/cli/babeltrace2-cfg-cli-args-connect.h b/cli/babeltrace2-cfg-cli-args-connect.h deleted file mode 100644 index 37799bb8..00000000 --- a/cli/babeltrace2-cfg-cli-args-connect.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include "babeltrace2-cfg.h" - -int bt_config_cli_args_create_connections(struct bt_config *cfg, - const bt_value *connection_args, - char *error_buf, size_t error_buf_size); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H */ diff --git a/cli/babeltrace2-cfg-cli-args-default.c b/cli/babeltrace2-cfg-cli-args-default.c deleted file mode 100644 index aef23a53..00000000 --- a/cli/babeltrace2-cfg-cli-args-default.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2016 - Jérémie Galarneau - * Copyright 2017 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "babeltrace2-cfg.h" -#include "babeltrace2-cfg-cli-args.h" -#include "babeltrace2-cfg-cli-args-default.h" - -#ifdef ENABLE_DEBUG_INFO -# define BT_ENABLE_DEBUG_INFO 1 -#else -# define BT_ENABLE_DEBUG_INFO 0 -#endif - -#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode) -{ - bt_value *initial_plugin_paths; - struct bt_config *cfg = NULL; - int ret; - - initial_plugin_paths = bt_value_array_create(); - if (!initial_plugin_paths) { - goto error; - } - - ret = bt_config_append_plugin_paths(initial_plugin_paths, - CONFIG_IN_TREE_PLUGIN_PATH); - if (ret) { - goto error; - } - - cfg = bt_config_cli_args_create(argc, argv, retcode, true, true, - initial_plugin_paths); - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - bt_value_put_ref(initial_plugin_paths); - return cfg; -} - -#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode) -{ - return bt_config_cli_args_create(argc, argv, retcode, false, false, - NULL); -} - -#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/cli/babeltrace2-cfg-cli-args-default.h b/cli/babeltrace2-cfg-cli-args-default.h deleted file mode 100644 index 839b08c1..00000000 --- a/cli/babeltrace2-cfg-cli-args-default.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H - -/* - * Babeltrace Trace Converter - Default Configuration - * - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "babeltrace2-cfg.h" - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H */ diff --git a/cli/babeltrace2-cfg-cli-args.c b/cli/babeltrace2-cfg-cli-args.c deleted file mode 100644 index a042d89a..00000000 --- a/cli/babeltrace2-cfg-cli-args.c +++ /dev/null @@ -1,5107 +0,0 @@ -/* - * Babeltrace trace converter - parameter parsing - * - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CLI-CFG-CLI-ARGS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace2-cfg.h" -#include "babeltrace2-cfg-cli-args.h" -#include "babeltrace2-cfg-cli-args-connect.h" -#include "version.h" - -/* - * Error printf() macro which prepends "Error: " the first time it's - * called. This gives a nicer feel than having a bunch of error prefixes - * (since the following lines usually describe the error and possible - * solutions), or the error prefix just at the end. - */ -#define printf_err(fmt, args...) \ - do { \ - if (is_first_error) { \ - fprintf(stderr, "Command line error: "); \ - is_first_error = false; \ - } \ - fprintf(stderr, fmt, ##args); \ - } while (0) - -static bool is_first_error = true; - -/* INI-style parsing FSM states */ -enum ini_parsing_fsm_state { - /* Expect a map key (identifier) */ - INI_EXPECT_MAP_KEY, - - /* Expect an equal character ('=') */ - INI_EXPECT_EQUAL, - - /* Expect a value */ - INI_EXPECT_VALUE, - - /* Expect a comma character (',') */ - INI_EXPECT_COMMA, -}; - -/* INI-style parsing state variables */ -struct ini_parsing_state { - /* Lexical scanner (owned by this) */ - GScanner *scanner; - - /* Output map value object being filled (owned by this) */ - bt_value *params; - - /* Next expected FSM state */ - enum ini_parsing_fsm_state expecting; - - /* Last decoded map key (owned by this) */ - char *last_map_key; - - /* Complete INI-style string to parse (not owned by this) */ - const char *arg; - - /* Error buffer (not owned by this) */ - GString *ini_error; -}; - -/* Offset option with "is set" boolean */ -struct offset_opt { - int64_t value; - bool is_set; -}; - -/* Legacy "ctf"/"lttng-live" format options */ -struct ctf_legacy_opts { - struct offset_opt offset_s; - struct offset_opt offset_ns; - bool stream_intersection; -}; - -/* Legacy "text" format options */ -struct text_legacy_opts { - /* - * output, dbg_info_dir, dbg_info_target_prefix, names, - * and fields are owned by this. - */ - GString *output; - GString *dbg_info_dir; - GString *dbg_info_target_prefix; - const bt_value *names; - const bt_value *fields; - - /* Flags */ - bool no_delta; - bool clock_cycles; - bool clock_seconds; - bool clock_date; - bool clock_gmt; - bool dbg_info_full_path; - bool verbose; -}; - -/* Legacy input format format */ -enum legacy_input_format { - LEGACY_INPUT_FORMAT_NONE = 0, - LEGACY_INPUT_FORMAT_CTF, - LEGACY_INPUT_FORMAT_LTTNG_LIVE, -}; - -/* Legacy output format format */ -enum legacy_output_format { - LEGACY_OUTPUT_FORMAT_NONE = 0, - LEGACY_OUTPUT_FORMAT_TEXT, - LEGACY_OUTPUT_FORMAT_DUMMY, -}; - -/* - * Prints the "out of memory" error. - */ -static -void print_err_oom(void) -{ - printf_err("Out of memory\n"); -} - -/* - * Appends an "expecting token" error to the INI-style parsing state's - * error buffer. - */ -static -void ini_append_error_expecting(struct ini_parsing_state *state, - GScanner *scanner, const char *expecting) -{ - size_t i; - size_t pos; - - g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting); - - /* Only print error if there's one line */ - if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) { - return; - } - - g_string_append_printf(state->ini_error, "\n %s\n", state->arg); - pos = g_scanner_cur_position(scanner) + 4; - - if (!g_scanner_eof(scanner)) { - pos--; - } - - for (i = 0; i < pos; ++i) { - g_string_append_printf(state->ini_error, " "); - } - - g_string_append_printf(state->ini_error, "^\n\n"); -} - -/* Parse the next token as an unsigned integer. */ -static -bt_value *ini_parse_uint(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - if (token_type != G_TOKEN_INT) { - ini_append_error_expecting(state, state->scanner, - "integer value"); - goto end; - } - - value = bt_value_unsigned_integer_create_init( - state->scanner->value.v_int64); - -end: - return value; -} - -/* Parse the next token as a number and return its negation. */ -static -bt_value *ini_parse_neg_number(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - switch (token_type) { - case G_TOKEN_INT: - { - /* Negative integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > (((uint64_t) INT64_MAX) + 1)) { - g_string_append_printf(state->ini_error, - "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - -((int64_t) int_val)); - } - - break; - } - case G_TOKEN_FLOAT: - /* Negative floating point number */ - value = bt_value_real_create_init(-state->scanner->value.v_float); - break; - default: - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static bt_value *ini_parse_value(struct ini_parsing_state *state); - -/* - * Parse the current and following tokens as an array. Arrays are formatted as - * an opening `[`, a list of comma-separated values and a closing `]`. For - * convenience we support an optional trailing comma, after the last value. - * - * The current token of the parser must be the opening square bracket of the - * array. - */ -static -bt_value *ini_parse_array(struct ini_parsing_state *state) -{ - /* The [ character must have already been ingested. */ - BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR); - BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '['); - - bt_value *array_value; - GTokenType token_type; - - array_value = bt_value_array_create (); - token_type = g_scanner_get_next_token(state->scanner); - - /* While the current token is not a ]... */ - while (!(token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ']')) { - /* Parse the item... */ - bt_value *item_value; - bt_value_status status; - - item_value = ini_parse_value(state); - if (!item_value) { - goto error; - } - - /* ... and add it to the result array. */ - status = bt_value_array_append_element(array_value, item_value); - BT_VALUE_PUT_REF_AND_RESET(item_value); - - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - - /* - * Ingest the token following the value, it should be either a - * comma or closing square brace. - */ - token_type = g_scanner_get_next_token(state->scanner); - - if (token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ',') { - /* - * Ingest the token following the comma. If it happens - * to be a closing square bracket, we'll exit the loop - * and we are done (we allow trailing commas). - * Otherwise, we are ready for the next ini_parse_value call. - */ - token_type = g_scanner_get_next_token(state->scanner); - } else if (token_type != G_TOKEN_CHAR || g_scanner_cur_value(state->scanner).v_char != ']') { - ini_append_error_expecting(state, state->scanner, ", or ]"); - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(array_value); - -end: - return array_value; -} - -/* - * Parse the current token (and the following ones if needed) as a value, return - * it as a bt_value. - */ -static -bt_value *ini_parse_value(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = state->scanner->token; - - switch (token_type) { - case G_TOKEN_CHAR: - if (state->scanner->value.v_char == '-') { - /* Negative number */ - value = ini_parse_neg_number(state); - } else if (state->scanner->value.v_char == '+') { - /* Unsigned integer */ - value = ini_parse_uint(state); - } else if (state->scanner->value.v_char == '[') { - /* Array */ - value = ini_parse_array(state); - } else { - ini_append_error_expecting(state, state->scanner, "value"); - } - break; - case G_TOKEN_INT: - { - /* Positive, signed integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > INT64_MAX) { - g_string_append_printf(state->ini_error, - "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - (int64_t) int_val); - } - break; - } - case G_TOKEN_FLOAT: - /* Positive floating point number */ - value = bt_value_real_create_init(state->scanner->value.v_float); - break; - case G_TOKEN_STRING: - /* Quoted string */ - value = bt_value_string_create_init(state->scanner->value.v_string); - break; - case G_TOKEN_IDENTIFIER: - { - /* - * Using symbols would be appropriate here, - * but said symbols are allowed as map key, - * so it's easier to consider everything an - * identifier. - * - * If one of the known symbols is not - * recognized here, then fall back to creating - * a string value. - */ - const char *id = state->scanner->value.v_identifier; - - if (!strcmp(id, "null") || !strcmp(id, "NULL") || - !strcmp(id, "nul")) { - value = bt_value_null; - } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") || - !strcmp(id, "yes") || - !strcmp(id, "YES")) { - value = bt_value_bool_create_init(true); - } else if (!strcmp(id, "false") || - !strcmp(id, "FALSE") || - !strcmp(id, "no") || - !strcmp(id, "NO")) { - value = bt_value_bool_create_init(false); - } else { - value = bt_value_string_create_init(id); - } - break; - } - default: - /* Unset value variable will trigger the error */ - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static -int ini_handle_state(struct ini_parsing_state *state) -{ - int ret = 0; - GTokenType token_type; - bt_value *value = NULL; - - token_type = g_scanner_get_next_token(state->scanner); - if (token_type == G_TOKEN_EOF) { - if (state->expecting != INI_EXPECT_COMMA) { - switch (state->expecting) { - case INI_EXPECT_EQUAL: - ini_append_error_expecting(state, - state->scanner, "'='"); - break; - case INI_EXPECT_VALUE: - ini_append_error_expecting(state, - state->scanner, "value"); - break; - case INI_EXPECT_MAP_KEY: - ini_append_error_expecting(state, - state->scanner, "unquoted map key"); - break; - default: - break; - } - goto error; - } - - /* We're done! */ - ret = 1; - goto success; - } - - switch (state->expecting) { - case INI_EXPECT_MAP_KEY: - if (token_type != G_TOKEN_IDENTIFIER) { - ini_append_error_expecting(state, state->scanner, - "unquoted map key"); - goto error; - } - - free(state->last_map_key); - state->last_map_key = - strdup(state->scanner->value.v_identifier); - if (!state->last_map_key) { - g_string_append(state->ini_error, - "Out of memory\n"); - goto error; - } - - if (bt_value_map_has_entry(state->params, - state->last_map_key)) { - g_string_append_printf(state->ini_error, - "Duplicate parameter key: `%s`\n", - state->last_map_key); - goto error; - } - - state->expecting = INI_EXPECT_EQUAL; - goto success; - case INI_EXPECT_EQUAL: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - if (state->scanner->value.v_char != '=') { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - state->expecting = INI_EXPECT_VALUE; - goto success; - case INI_EXPECT_VALUE: - { - value = ini_parse_value(state); - if (!value) { - goto error; - } - - state->expecting = INI_EXPECT_COMMA; - goto success; - } - case INI_EXPECT_COMMA: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - if (state->scanner->value.v_char != ',') { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - state->expecting = INI_EXPECT_MAP_KEY; - goto success; - default: - abort(); - } - -error: - ret = -1; - goto end; - -success: - if (value) { - if (bt_value_map_insert_entry(state->params, - state->last_map_key, value)) { - /* Only override return value on error */ - ret = -1; - } - } - -end: - BT_VALUE_PUT_REF_AND_RESET(value); - return ret; -} - -/* - * Converts an INI-style argument to an equivalent map value object. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_ini(const char *arg, - GString *ini_error) -{ - /* Lexical scanner configuration */ - GScannerConfig scanner_config = { - /* Skip whitespaces */ - .cset_skip_characters = " \t\n", - - /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */ - .cset_identifier_first = - G_CSET_a_2_z - "_" - G_CSET_A_2_Z, - .cset_identifier_nth = - G_CSET_a_2_z - "_0123456789-.:" - G_CSET_A_2_Z, - - /* "hello" and "Hello" two different keys */ - .case_sensitive = TRUE, - - /* No comments */ - .cpair_comment_single = NULL, - .skip_comment_multi = TRUE, - .skip_comment_single = TRUE, - .scan_comment_multi = FALSE, - - /* - * Do scan identifiers, including 1-char identifiers, - * but NULL is a normal identifier. - */ - .scan_identifier = TRUE, - .scan_identifier_1char = TRUE, - .scan_identifier_NULL = FALSE, - - /* - * No specific symbols: null and boolean "symbols" are - * scanned as plain identifiers. - */ - .scan_symbols = FALSE, - .symbol_2_token = FALSE, - .scope_0_fallback = FALSE, - - /* - * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not - * integers prefixed with "$". - */ - .scan_binary = TRUE, - .scan_octal = TRUE, - .scan_float = TRUE, - .scan_hex = TRUE, - .scan_hex_dollar = FALSE, - - /* Convert scanned numbers to integer tokens */ - .numbers_2_int = TRUE, - - /* Support both integers and floating-point numbers */ - .int_2_float = FALSE, - - /* Scan integers as 64-bit signed integers */ - .store_int64 = TRUE, - - /* Only scan double-quoted strings */ - .scan_string_sq = FALSE, - .scan_string_dq = TRUE, - - /* Do not converter identifiers to string tokens */ - .identifier_2_string = FALSE, - - /* Scan characters as G_TOKEN_CHAR token */ - .char_2_token = FALSE, - }; - struct ini_parsing_state state = { - .scanner = NULL, - .params = NULL, - .expecting = INI_EXPECT_MAP_KEY, - .arg = arg, - .ini_error = ini_error, - }; - - state.params = bt_value_map_create(); - if (!state.params) { - goto error; - } - - state.scanner = g_scanner_new(&scanner_config); - if (!state.scanner) { - goto error; - } - - /* Let the scan begin */ - g_scanner_input_text(state.scanner, arg, strlen(arg)); - - while (true) { - int ret = ini_handle_state(&state); - - if (ret < 0) { - /* Error */ - goto error; - } else if (ret > 0) { - /* Done */ - break; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(state.params); - -end: - if (state.scanner) { - g_scanner_destroy(state.scanner); - } - - free(state.last_map_key); - return state.params; -} - -/* - * Returns the parameters map value object from a command-line - * parameter option's argument. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_arg(const char *arg) -{ - bt_value *params = NULL; - GString *ini_error = NULL; - - ini_error = g_string_new(NULL); - if (!ini_error) { - print_err_oom(); - goto end; - } - - /* Try INI-style parsing */ - params = bt_value_from_ini(arg, ini_error); - if (!params) { - printf_err("%s", ini_error->str); - goto end; - } - -end: - if (ini_error) { - g_string_free(ini_error, TRUE); - } - - return params; -} - -/* - * Returns the plugin name, component class name, component class type, - * and component name from a command-line --component option's argument. - * arg must have the following format: - * - * [NAME:]TYPE.PLUGIN.CLS - * - * where NAME is the optional component name, TYPE is either `source`, - * `filter`, or `sink`, PLUGIN is the plugin name, and CLS is the - * component class name. - * - * On success, both *plugin and *component are not NULL. *plugin - * and *comp_cls are owned by the caller. On success, *name can be NULL - * if no component class name was found, and *comp_cls_type is set. - */ -static -void plugin_comp_cls_names(const char *arg, char **name, char **plugin, - char **comp_cls, bt_component_class_type *comp_cls_type) -{ - const char *at = arg; - GString *gs_name = NULL; - GString *gs_comp_cls_type = NULL; - GString *gs_plugin = NULL; - GString *gs_comp_cls = NULL; - size_t end_pos; - - BT_ASSERT(arg); - BT_ASSERT(plugin); - BT_ASSERT(comp_cls); - BT_ASSERT(comp_cls_type); - - if (!bt_common_string_is_printable(arg)) { - printf_err("Argument contains a non-printable character\n"); - goto error; - } - - /* Parse the component name */ - gs_name = bt_common_string_until(at, ".:\\", ":", &end_pos); - if (!gs_name) { - goto error; - } - - if (arg[end_pos] == ':') { - at += end_pos + 1; - } else { - /* No name */ - g_string_assign(gs_name, ""); - } - - /* Parse the component class type */ - gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_comp_cls_type || at[end_pos] == '\0') { - printf_err("Missing component class type (`source`, `filter`, or `sink`)\n"); - goto error; - } - - if (strcmp(gs_comp_cls_type->str, "source") == 0 || - strcmp(gs_comp_cls_type->str, "src") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SOURCE; - } else if (strcmp(gs_comp_cls_type->str, "filter") == 0 || - strcmp(gs_comp_cls_type->str, "flt") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_FILTER; - } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK; - } else { - printf_err("Unknown component class type: `%s`\n", - gs_comp_cls_type->str); - goto error; - } - - at += end_pos + 1; - - /* Parse the plugin name */ - gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { - printf_err("Missing plugin or component class name\n"); - goto error; - } - - at += end_pos + 1; - - /* Parse the component class name */ - gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_comp_cls || gs_comp_cls->len == 0) { - printf_err("Missing component class name\n"); - goto error; - } - - if (at[end_pos] != '\0') { - /* Found a non-escaped `.` */ - goto error; - } - - if (name) { - if (gs_name->len == 0) { - *name = NULL; - g_string_free(gs_name, TRUE); - } else { - *name = gs_name->str; - g_string_free(gs_name, FALSE); - } - } else { - g_string_free(gs_name, TRUE); - } - - *plugin = gs_plugin->str; - *comp_cls = gs_comp_cls->str; - g_string_free(gs_plugin, FALSE); - g_string_free(gs_comp_cls, FALSE); - gs_name = NULL; - gs_plugin = NULL; - gs_comp_cls = NULL; - goto end; - -error: - if (name) { - *name = NULL; - } - - *plugin = NULL; - *comp_cls = NULL; - -end: - if (gs_name) { - g_string_free(gs_name, TRUE); - } - - if (gs_plugin) { - g_string_free(gs_plugin, TRUE); - } - - if (gs_comp_cls) { - g_string_free(gs_comp_cls, TRUE); - } - - if (gs_comp_cls_type) { - g_string_free(gs_comp_cls_type, TRUE); - } - - return; -} - -/* - * Prints the Babeltrace version. - */ -static -void print_version(void) -{ - if (GIT_VERSION[0] == '\0') { - puts("Babeltrace " VERSION); - } else { - puts("Babeltrace " VERSION " - " GIT_VERSION); - } -} - -/* - * Destroys a component configuration. - */ -static -void bt_config_component_destroy(bt_object *obj) -{ - struct bt_config_component *bt_config_component = - container_of(obj, struct bt_config_component, base); - - if (!obj) { - goto end; - } - - if (bt_config_component->plugin_name) { - g_string_free(bt_config_component->plugin_name, TRUE); - } - - if (bt_config_component->comp_cls_name) { - g_string_free(bt_config_component->comp_cls_name, TRUE); - } - - if (bt_config_component->instance_name) { - g_string_free(bt_config_component->instance_name, TRUE); - } - - BT_VALUE_PUT_REF_AND_RESET(bt_config_component->params); - g_free(bt_config_component); - -end: - return; -} - -/* - * Creates a component configuration using the given plugin name and - * component name. `plugin_name` and `comp_cls_name` are copied (belong - * to the return value). - * - * Return value is owned by the caller. - */ -static -struct bt_config_component *bt_config_component_create( - bt_component_class_type type, - const char *plugin_name, const char *comp_cls_name) -{ - struct bt_config_component *cfg_component = NULL; - - cfg_component = g_new0(struct bt_config_component, 1); - if (!cfg_component) { - print_err_oom(); - goto error; - } - - bt_object_init_shared(&cfg_component->base, - bt_config_component_destroy); - cfg_component->type = type; - cfg_component->plugin_name = g_string_new(plugin_name); - if (!cfg_component->plugin_name) { - print_err_oom(); - goto error; - } - - cfg_component->comp_cls_name = g_string_new(comp_cls_name); - if (!cfg_component->comp_cls_name) { - print_err_oom(); - goto error; - } - - cfg_component->instance_name = g_string_new(NULL); - if (!cfg_component->instance_name) { - print_err_oom(); - goto error; - } - - /* Start with empty parameters */ - cfg_component->params = bt_value_map_create(); - if (!cfg_component->params) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg_component); - -end: - return cfg_component; -} - -/* - * Creates a component configuration from a command-line --component - * option's argument. - */ -static -struct bt_config_component *bt_config_component_from_arg(const char *arg) -{ - struct bt_config_component *cfg_comp = NULL; - char *name = NULL; - char *plugin_name = NULL; - char *comp_cls_name = NULL; - bt_component_class_type type; - - plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name, &type); - if (!plugin_name || !comp_cls_name) { - goto error; - } - - cfg_comp = bt_config_component_create(type, plugin_name, comp_cls_name); - if (!cfg_comp) { - goto error; - } - - if (name) { - g_string_assign(cfg_comp->instance_name, name); - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg_comp); - -end: - g_free(name); - g_free(plugin_name); - g_free(comp_cls_name); - return cfg_comp; -} - -/* - * Destroys a configuration. - */ -static -void bt_config_destroy(bt_object *obj) -{ - struct bt_config *cfg = - container_of(obj, struct bt_config, base); - - if (!obj) { - goto end; - } - - BT_VALUE_PUT_REF_AND_RESET(cfg->plugin_paths); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - if (cfg->cmd_data.run.sources) { - g_ptr_array_free(cfg->cmd_data.run.sources, TRUE); - } - - if (cfg->cmd_data.run.filters) { - g_ptr_array_free(cfg->cmd_data.run.filters, TRUE); - } - - if (cfg->cmd_data.run.sinks) { - g_ptr_array_free(cfg->cmd_data.run.sinks, TRUE); - } - - if (cfg->cmd_data.run.connections) { - g_ptr_array_free(cfg->cmd_data.run.connections, - TRUE); - } - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - break; - case BT_CONFIG_COMMAND_HELP: - BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component); - break; - case BT_CONFIG_COMMAND_QUERY: - BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.query.cfg_component); - - if (cfg->cmd_data.query.object) { - g_string_free(cfg->cmd_data.query.object, TRUE); - } - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - if (cfg->cmd_data.print_ctf_metadata.path) { - g_string_free(cfg->cmd_data.print_ctf_metadata.path, - TRUE); - g_string_free( - cfg->cmd_data.print_ctf_metadata.output_path, - TRUE); - } - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - if (cfg->cmd_data.print_lttng_live_sessions.url) { - g_string_free( - cfg->cmd_data.print_lttng_live_sessions.url, - TRUE); - g_string_free( - cfg->cmd_data.print_lttng_live_sessions.output_path, - TRUE); - } - break; - default: - abort(); - } - - g_free(cfg); - -end: - return; -} - -static -void destroy_glist_of_gstring(GList *list) -{ - GList *at; - - if (!list) { - return; - } - - for (at = list; at != NULL; at = g_list_next(at)) { - g_string_free(at->data, TRUE); - } - - g_list_free(list); -} - -/* - * Creates a simple lexical scanner for parsing comma-delimited names - * and fields. - * - * Return value is owned by the caller. - */ -static -GScanner *create_csv_identifiers_scanner(void) -{ - GScanner *scanner; - GScannerConfig scanner_config = { - .cset_skip_characters = " \t\n", - .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_", - .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-", - .case_sensitive = TRUE, - .cpair_comment_single = NULL, - .skip_comment_multi = TRUE, - .skip_comment_single = TRUE, - .scan_comment_multi = FALSE, - .scan_identifier = TRUE, - .scan_identifier_1char = TRUE, - .scan_identifier_NULL = FALSE, - .scan_symbols = FALSE, - .symbol_2_token = FALSE, - .scope_0_fallback = FALSE, - .scan_binary = FALSE, - .scan_octal = FALSE, - .scan_float = FALSE, - .scan_hex = FALSE, - .scan_hex_dollar = FALSE, - .numbers_2_int = FALSE, - .int_2_float = FALSE, - .store_int64 = FALSE, - .scan_string_sq = FALSE, - .scan_string_dq = FALSE, - .identifier_2_string = FALSE, - .char_2_token = TRUE, - }; - - scanner = g_scanner_new(&scanner_config); - if (!scanner) { - print_err_oom(); - } - - return scanner; -} - -/* - * Converts a comma-delimited list of known names (--names option) to - * an array value object containing those names as string value objects. - * - * Return value is owned by the caller. - */ -static -bt_value *names_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - bt_value *names = NULL; - bool found_all = false, found_none = false, found_item = false; - - names = bt_value_array_create(); - if (!names) { - print_err_oom(); - goto error; - } - - scanner = create_csv_identifiers_scanner(); - if (!scanner) { - goto error; - } - - g_scanner_input_text(scanner, arg, strlen(arg)); - - while (true) { - GTokenType token_type = g_scanner_get_next_token(scanner); - - switch (token_type) { - case G_TOKEN_IDENTIFIER: - { - const char *identifier = scanner->value.v_identifier; - - if (!strcmp(identifier, "payload") || - !strcmp(identifier, "args") || - !strcmp(identifier, "arg")) { - found_item = true; - if (bt_value_array_append_string_element(names, - "payload")) { - goto error; - } - } else if (!strcmp(identifier, "context") || - !strcmp(identifier, "ctx")) { - found_item = true; - if (bt_value_array_append_string_element(names, - "context")) { - goto error; - } - } else if (!strcmp(identifier, "scope") || - !strcmp(identifier, "header")) { - found_item = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "all")) { - found_all = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "none")) { - found_none = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else { - printf_err("Unknown name: `%s`\n", - identifier); - goto error; - } - break; - } - case G_TOKEN_COMMA: - continue; - case G_TOKEN_EOF: - goto end; - default: - goto error; - } - } - -end: - if (found_none && found_all) { - printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n"); - goto error; - } - /* - * Legacy behavior is to clear the defaults (show none) when at - * least one item is specified. - */ - if (found_item && !found_none && !found_all) { - if (bt_value_array_append_string_element(names, "none")) { - goto error; - } - } - if (scanner) { - g_scanner_destroy(scanner); - } - return names; - -error: - BT_VALUE_PUT_REF_AND_RESET(names); - if (scanner) { - g_scanner_destroy(scanner); - } - return names; -} - -/* - * Converts a comma-delimited list of known fields (--fields option) to - * an array value object containing those fields as string - * value objects. - * - * Return value is owned by the caller. - */ -static -bt_value *fields_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - bt_value *fields; - - fields = bt_value_array_create(); - if (!fields) { - print_err_oom(); - goto error; - } - - scanner = create_csv_identifiers_scanner(); - if (!scanner) { - goto error; - } - - g_scanner_input_text(scanner, arg, strlen(arg)); - - while (true) { - GTokenType token_type = g_scanner_get_next_token(scanner); - - switch (token_type) { - case G_TOKEN_IDENTIFIER: - { - const char *identifier = scanner->value.v_identifier; - - if (!strcmp(identifier, "trace") || - !strcmp(identifier, "trace:hostname") || - !strcmp(identifier, "trace:domain") || - !strcmp(identifier, "trace:procname") || - !strcmp(identifier, "trace:vpid") || - !strcmp(identifier, "loglevel") || - !strcmp(identifier, "emf") || - !strcmp(identifier, "callsite") || - !strcmp(identifier, "all")) { - if (bt_value_array_append_string_element(fields, - identifier)) { - goto error; - } - } else { - printf_err("Unknown field: `%s`\n", - identifier); - goto error; - } - break; - } - case G_TOKEN_COMMA: - continue; - case G_TOKEN_EOF: - goto end; - default: - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(fields); - -end: - if (scanner) { - g_scanner_destroy(scanner); - } - return fields; -} - -static -void append_param_arg(GString *params_arg, const char *key, const char *value) -{ - BT_ASSERT(params_arg); - BT_ASSERT(key); - BT_ASSERT(value); - - if (params_arg->len != 0) { - g_string_append_c(params_arg, ','); - } - - g_string_append(params_arg, key); - g_string_append_c(params_arg, '='); - g_string_append(params_arg, value); -} - -/* - * Inserts the equivalent "prefix-NAME=yes" strings into params_arg - * where the names are in names_array. - */ -static -int insert_flat_params_from_array(GString *params_arg, - const bt_value *names_array, const char *prefix) -{ - int ret = 0; - int i; - GString *tmpstr = NULL, *default_value = NULL; - bool default_set = false, non_default_set = false; - - /* - * names_array may be NULL if no CLI options were specified to - * trigger its creation. - */ - if (!names_array) { - goto end; - } - - tmpstr = g_string_new(NULL); - if (!tmpstr) { - print_err_oom(); - ret = -1; - goto end; - } - - default_value = g_string_new(NULL); - if (!default_value) { - print_err_oom(); - ret = -1; - goto end; - } - - for (i = 0; i < bt_value_array_get_size(names_array); i++) { - const bt_value *str_obj = - bt_value_array_borrow_element_by_index_const(names_array, - i); - const char *suffix; - bool is_default = false; - - if (!str_obj) { - printf_err("Unexpected error\n"); - ret = -1; - goto end; - } - - suffix = bt_value_string_get(str_obj); - - g_string_assign(tmpstr, prefix); - g_string_append(tmpstr, "-"); - - /* Special-case for "all" and "none". */ - if (!strcmp(suffix, "all")) { - is_default = true; - g_string_assign(default_value, "show"); - } else if (!strcmp(suffix, "none")) { - is_default = true; - g_string_assign(default_value, "hide"); - } - if (is_default) { - default_set = true; - g_string_append(tmpstr, "default"); - append_param_arg(params_arg, tmpstr->str, - default_value->str); - } else { - non_default_set = true; - g_string_append(tmpstr, suffix); - append_param_arg(params_arg, tmpstr->str, "yes"); - } - } - - /* Implicit field-default=hide if any non-default option is set. */ - if (non_default_set && !default_set) { - g_string_assign(tmpstr, prefix); - g_string_append(tmpstr, "-default"); - g_string_assign(default_value, "hide"); - append_param_arg(params_arg, tmpstr->str, default_value->str); - } - -end: - if (default_value) { - g_string_free(default_value, TRUE); - } - - if (tmpstr) { - g_string_free(tmpstr, TRUE); - } - - return ret; -} - -/* popt options */ -enum { - OPT_NONE = 0, - OPT_BASE_PARAMS, - OPT_BEGIN, - OPT_CLOCK_CYCLES, - OPT_CLOCK_DATE, - OPT_CLOCK_FORCE_CORRELATE, - OPT_CLOCK_GMT, - OPT_CLOCK_OFFSET, - OPT_CLOCK_OFFSET_NS, - OPT_CLOCK_SECONDS, - OPT_COLOR, - OPT_COMPONENT, - OPT_CONNECT, - OPT_DEBUG, - OPT_DEBUG_INFO, - OPT_DEBUG_INFO_DIR, - OPT_DEBUG_INFO_FULL_PATH, - OPT_DEBUG_INFO_TARGET_PREFIX, - OPT_END, - OPT_FIELDS, - OPT_HELP, - OPT_INPUT_FORMAT, - OPT_LIST, - OPT_NAME, - OPT_NAMES, - OPT_NO_DELTA, - OPT_OMIT_HOME_PLUGIN_PATH, - OPT_OMIT_SYSTEM_PLUGIN_PATH, - OPT_OUTPUT, - OPT_OUTPUT_FORMAT, - OPT_PARAMS, - OPT_PATH, - OPT_PLUGIN_PATH, - OPT_RESET_BASE_PARAMS, - OPT_RETRY_DURATION, - OPT_RUN_ARGS, - OPT_RUN_ARGS_0, - OPT_STREAM_INTERSECTION, - OPT_TIMERANGE, - OPT_URL, - OPT_VERBOSE, -}; - -enum bt_config_component_dest { - BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1, - BT_CONFIG_COMPONENT_DEST_SOURCE, - BT_CONFIG_COMPONENT_DEST_FILTER, - BT_CONFIG_COMPONENT_DEST_SINK, -}; - -/* - * Adds a configuration component to the appropriate configuration - * array depending on the destination. - */ -static -void add_run_cfg_comp(struct bt_config *cfg, - struct bt_config_component *cfg_comp, - enum bt_config_component_dest dest) -{ - bt_object_get_ref(cfg_comp); - - switch (dest) { - case BT_CONFIG_COMPONENT_DEST_SOURCE: - g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp); - break; - case BT_CONFIG_COMPONENT_DEST_FILTER: - g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp); - break; - case BT_CONFIG_COMPONENT_DEST_SINK: - g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp); - break; - default: - abort(); - } -} - -static -int add_run_cfg_comp_check_name(struct bt_config *cfg, - struct bt_config_component *cfg_comp, - enum bt_config_component_dest dest, - bt_value *instance_names) -{ - int ret = 0; - - if (cfg_comp->instance_name->len == 0) { - printf_err("Found an unnamed component\n"); - ret = -1; - goto end; - } - - if (bt_value_map_has_entry(instance_names, - cfg_comp->instance_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", - cfg_comp->instance_name->str); - ret = -1; - goto end; - } - - if (bt_value_map_insert_entry(instance_names, - cfg_comp->instance_name->str, bt_value_null)) { - print_err_oom(); - ret = -1; - goto end; - } - - add_run_cfg_comp(cfg, cfg_comp, dest); - -end: - return ret; -} - -static -int append_env_var_plugin_paths(bt_value *plugin_paths) -{ - int ret = 0; - const char *envvar; - - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - goto end; - } - - envvar = getenv("BABELTRACE_PLUGIN_PATH"); - if (!envvar) { - goto end; - } - - ret = bt_config_append_plugin_paths(plugin_paths, envvar); - -end: - if (ret) { - printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); - } - - return ret; -} - -static -int append_home_and_system_plugin_paths(bt_value *plugin_paths, - bool omit_system_plugin_path, bool omit_home_plugin_path) -{ - int ret; - - if (!omit_home_plugin_path) { - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - } else { - char *home_plugin_dir = - bt_common_get_home_plugin_path(); - - if (home_plugin_dir) { - ret = bt_config_append_plugin_paths( - plugin_paths, home_plugin_dir); - free(home_plugin_dir); - - if (ret) { - printf_err("Invalid home plugin path\n"); - goto error; - } - } - } - } - - if (!omit_system_plugin_path) { - if (bt_config_append_plugin_paths(plugin_paths, - bt_common_get_system_plugin_path())) { - printf_err("Invalid system plugin path\n"); - goto error; - } - } - return 0; -error: - printf_err("Cannot append home and system plugin paths\n"); - return -1; -} - -static -int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg) -{ - return append_home_and_system_plugin_paths(cfg->plugin_paths, - cfg->omit_system_plugin_path, cfg->omit_home_plugin_path); -} - -static -struct bt_config *bt_config_base_create(enum bt_config_command command, - const bt_value *initial_plugin_paths, - bool needs_plugins) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = g_new0(struct bt_config, 1); - if (!cfg) { - print_err_oom(); - goto error; - } - - bt_object_init_shared(&cfg->base, bt_config_destroy); - cfg->command = command; - cfg->command_needs_plugins = needs_plugins; - - if (initial_plugin_paths) { - bt_value *initial_plugin_paths_copy; - - (void) bt_value_copy(initial_plugin_paths, - &initial_plugin_paths_copy); - cfg->plugin_paths = initial_plugin_paths_copy; - } else { - cfg->plugin_paths = bt_value_array_create(); - if (!cfg->plugin_paths) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_run_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.sources) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.filters) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.sinks) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_config_connection_destroy); - if (!cfg->cmd_data.run.connections) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_list_plugins_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_help_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.help.cfg_component = - bt_config_component_create(-1, NULL, NULL); - if (!cfg->cmd_data.help.cfg_component) { - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_query_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.query.object = g_string_new(NULL); - if (!cfg->cmd_data.query.object) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_print_ctf_metadata_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL); - if (!cfg->cmd_data.print_ctf_metadata.path) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL); - if (!cfg->cmd_data.print_ctf_metadata.output_path) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_print_lttng_live_sessions_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL); - if (!cfg->cmd_data.print_lttng_live_sessions.url) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.print_lttng_live_sessions.output_path = - g_string_new(NULL); - if (!cfg->cmd_data.print_lttng_live_sessions.output_path) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -int bt_config_append_plugin_paths_check_setuid_setgid( - bt_value *plugin_paths, const char *arg) -{ - int ret = 0; - - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - goto end; - } - - if (bt_config_append_plugin_paths(plugin_paths, arg)) { - printf_err("Invalid --plugin-path option's argument:\n %s\n", - arg); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * Prints the expected format for a --params option. - */ -static -void print_expected_params_format(FILE *fp) -{ - fprintf(fp, "Expected format of PARAMS\n"); - fprintf(fp, "-------------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); - fprintf(fp, "\n"); - fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); - fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n"); - fprintf(fp, "and VALUE can be one of:\n"); - fprintf(fp, "\n"); - fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); - fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); - fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); - fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); - fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n"); - fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); - fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); - fprintf(fp, " the null and boolean value symbols above.\n"); - fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); - fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n"); - fprintf(fp, " (as described by the current list) and a closing `]`.\n"); - fprintf(fp, "\n"); - fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Example:\n"); - fprintf(fp, "\n"); - fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); - fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); - fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n"); - fprintf(fp, " things=[1, \"2\", 3]\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); - fprintf(fp, "babeltrace2 from a shell.\n"); -} - - -/* - * Prints the help command usage. - */ -static -void print_help_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n"); - fprintf(fp, " babeltrace2 [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace2 list-plugins` to show the list of available plugins.\n"); -} - -static -struct poptOption help_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a help - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_help_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - char *plugin_name = NULL, *comp_cls_name = NULL; - - *retcode = 0; - cfg = bt_config_help_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - help_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (leftover) { - plugin_comp_cls_names(leftover, NULL, - &plugin_name, &comp_cls_name, - &cfg->cmd_data.help.cfg_component->type); - if (plugin_name && comp_cls_name) { - /* Component class help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - plugin_name); - g_string_assign( - cfg->cmd_data.help.cfg_component->comp_cls_name, - comp_cls_name); - } else { - /* Fall back to plugin help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - leftover); - } - } else { - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - g_free(plugin_name); - g_free(comp_cls_name); - - if (pc) { - poptFreeContext(pc); - } - - free(arg); - return cfg; -} - -/* - * Prints the help command usage. - */ -static -void print_query_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n"); - fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -static -struct poptOption query_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a query - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_query_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - bt_value *params; - - params = bt_value_null; - bt_value_get_ref(bt_value_null); - - *retcode = 0; - cfg = bt_config_query_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - query_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_PARAMS: - { - bt_value_put_ref(params); - params = bt_value_from_arg(arg); - if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); - goto error; - } - break; - } - case OPT_HELP: - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* - * We need exactly two leftover arguments which are the - * mandatory component class specification and query object. - */ - leftover = poptGetArg(pc); - if (leftover) { - cfg->cmd_data.query.cfg_component = - bt_config_component_from_arg(leftover); - if (!cfg->cmd_data.query.cfg_component) { - printf_err("Invalid format for component class specification:\n %s\n", - leftover); - goto error; - } - - BT_ASSERT(params); - BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, - params); - } else { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - if (strlen(leftover) == 0) { - printf_err("Invalid empty object\n"); - goto error; - } - - g_string_assign(cfg->cmd_data.query.object, leftover); - } else { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - bt_value_put_ref(params); - free(arg); - return cfg; -} - -/* - * Prints the list-plugins command usage. - */ -static -void print_list_plugins_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] list-plugins [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace2 help` to get help for a specific plugin or component class.\n"); -} - -static -struct poptOption list_plugins_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a - * list-plugins command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - - *retcode = 0; - cfg = bt_config_list_plugins_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - list_plugins_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_list_plugins_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - return cfg; -} - -/* - * Prints the run command usage. - */ -static -void print_run_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] run [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); - fprintf(fp, " for all the following components until\n"); - fprintf(fp, " --reset-base-params is encountered\n"); - fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); - fprintf(fp, " Instantiate the component class CLS of type\n"); - fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); - fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n"); - fprintf(fp, " and optionally name it NAME (you can also\n"); - fprintf(fp, " specify the name with --name)\n"); - fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n"); - fprintf(fp, " expected format of CONNECTION below)\n"); - fprintf(fp, " -n, --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); - fprintf(fp, " current component (see the expected format\n"); - fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n"); - fprintf(fp, " empty map\n"); - fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); - fprintf(fp, " the graph later, retry in DUR µs\n"); - fprintf(fp, " (default: 100000)\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "Expected format of CONNECTION\n"); - fprintf(fp, "-----------------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n"); - fprintf(fp, "\n"); - fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n"); - fprintf(fp, "components to connect together. You must escape the following characters\n\n"); - fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n"); - fprintf(fp, "component with the --name option.\n"); - fprintf(fp, "\n"); - fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n"); - fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n"); - fprintf(fp, "When the port is not specified, `*` is used.\n"); - fprintf(fp, "\n"); - fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n"); - fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n"); - fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n"); - fprintf(fp, "DOWNSTREAM.\n"); - fprintf(fp, "\n"); - fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n"); - fprintf(fp, "which matches anything. You must escape the following characters\n"); - fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\n"); - fprintf(fp, "\n"); - fprintf(fp, "You can connect a source component to a filter or sink component. You\n"); - fprintf(fp, "can connect a filter component to a sink component.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Examples:\n"); - fprintf(fp, "\n"); - fprintf(fp, " my-src:my-sink\n"); - fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); - fprintf(fp, "babeltrace2 from a shell.\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -/* - * Creates a Babeltrace config object from the arguments of a run - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_run_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - struct bt_config_component *cur_cfg_comp = NULL; - enum bt_config_component_dest cur_cfg_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; - bt_value *cur_base_params = NULL; - int opt, ret = 0; - struct bt_config *cfg = NULL; - bt_value *instance_names = NULL; - bt_value *connection_args = NULL; - char error_buf[256] = { 0 }; - long retry_duration = -1; - bt_value_status status; - struct poptOption run_long_options[] = { - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, - { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, - }; - - *retcode = 0; - - if (argc <= 1) { - print_run_usage(stdout); - *retcode = -1; - goto end; - } - - cfg = bt_config_run_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.retry_duration_us = 100000; - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; - } - - instance_names = bt_value_map_create(); - if (!instance_names) { - print_err_oom(); - goto error; - } - - connection_args = bt_value_array_create(); - if (!connection_args) { - print_err_oom(); - goto error; - } - - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - run_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_COMPONENT: - { - enum bt_config_component_dest new_dest; - - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, - cur_cfg_comp, cur_cfg_comp_dest, - instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } - - cur_cfg_comp = bt_config_component_from_arg(arg); - if (!cur_cfg_comp) { - printf_err("Invalid format for --component option's argument:\n %s\n", - arg); - goto error; - } - - switch (cur_cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - new_dest = BT_CONFIG_COMPONENT_DEST_SINK; - break; - default: - abort(); - } - - BT_ASSERT(cur_base_params); - bt_value_put_ref(cur_cfg_comp->params); - status = bt_value_copy(cur_base_params, - &cur_cfg_comp->params); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); - goto error; - } - - cur_cfg_comp_dest = new_dest; - break; - } - case OPT_PARAMS: - { - bt_value *params; - bt_value *params_to_set; - - if (!cur_cfg_comp) { - printf_err("Cannot add parameters to unavailable component:\n %s\n", - arg); - goto error; - } - - params = bt_value_from_arg(arg); - if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); - goto error; - } - - status = bt_value_map_extend(cur_cfg_comp->params, - params, ¶ms_to_set); - BT_VALUE_PUT_REF_AND_RESET(params); - if (status != BT_VALUE_STATUS_OK) { - printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", - arg); - goto error; - } - - BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set); - break; - } - case OPT_NAME: - if (!cur_cfg_comp) { - printf_err("Cannot set the name of unavailable component:\n %s\n", - arg); - goto error; - } - - g_string_assign(cur_cfg_comp->instance_name, arg); - break; - case OPT_BASE_PARAMS: - { - bt_value *params = - bt_value_from_arg(arg); - - if (!params) { - printf_err("Invalid format for --base-params option's argument:\n %s\n", - arg); - goto error; - } - - BT_OBJECT_MOVE_REF(cur_base_params, params); - break; - } - case OPT_RESET_BASE_PARAMS: - BT_VALUE_PUT_REF_AND_RESET(cur_base_params); - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; - } - break; - case OPT_CONNECT: - if (bt_value_array_append_string_element( - connection_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_RETRY_DURATION: - if (retry_duration < 0) { - printf_err("--retry-duration option's argument must be positive or 0: %ld\n", - retry_duration); - goto error; - } - - cfg->cmd_data.run.retry_duration_us = - (uint64_t) retry_duration; - break; - case OPT_HELP: - print_run_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* This command does not accept leftover arguments */ - if (poptPeekArg(pc)) { - printf_err("Unexpected argument: %s\n", poptPeekArg(pc)); - goto error; - } - - /* Add current component */ - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp, - cur_cfg_comp_dest, instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } - - if (cfg->cmd_data.run.sources->len == 0) { - printf_err("Incomplete graph: no source component\n"); - goto error; - } - - if (cfg->cmd_data.run.sinks->len == 0) { - printf_err("Incomplete graph: no sink component\n"); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - ret = bt_config_cli_args_create_connections(cfg, - connection_args, - error_buf, 256); - if (ret) { - printf_err("Cannot creation connections:\n%s", error_buf); - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - BT_VALUE_PUT_REF_AND_RESET(cur_base_params); - BT_VALUE_PUT_REF_AND_RESET(instance_names); - BT_VALUE_PUT_REF_AND_RESET(connection_args); - return cfg; -} - -static -struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg = NULL; - const char **argv; - int64_t i, len; - const size_t argc = bt_value_array_get_size(run_args) + 1; - - argv = calloc(argc, sizeof(*argv)); - if (!argv) { - print_err_oom(); - goto end; - } - - argv[0] = "run"; - - len = bt_value_array_get_size(run_args); - if (len < 0) { - printf_err("Invalid executable arguments\n"); - goto end; - } - for (i = 0; i < len; i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index_const(run_args, - i); - const char *arg; - - BT_ASSERT(arg_value); - arg = bt_value_string_get(arg_value); - BT_ASSERT(arg); - argv[i + 1] = arg; - } - - cfg = bt_config_run_from_args(argc, argv, retcode, - force_omit_system_plugin_path, force_omit_home_plugin_path, - initial_plugin_paths); - -end: - free(argv); - return cfg; -} - -/* - * Prints the convert command usage. - */ -static -void print_convert_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); - fprintf(fp, " Instantiate the component class CLS of type\n"); - fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); - fprintf(fp, " in the plugin PLUGIN, add it to the\n"); - fprintf(fp, " conversion graph, and optionally name it\n"); - fprintf(fp, " NAME (you can also specify the name with\n"); - fprintf(fp, " --name)\n"); - fprintf(fp, " --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); - fprintf(fp, " current component (see the expected format\n"); - fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n"); - fprintf(fp, " current component to PATH\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); - fprintf(fp, " the graph later, retry in DUR µs\n"); - fprintf(fp, " (default: 100000)\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " --run-args Print the equivalent arguments for the\n"); - fprintf(fp, " `run` command to the standard output,\n"); - fprintf(fp, " formatted for a shell, and quit\n"); - fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n"); - fprintf(fp, " `run` command to the standard output,\n"); - fprintf(fp, " formatted for `xargs -0`, and quit\n"); - fprintf(fp, " --stream-intersection Only process events when all streams\n"); - fprintf(fp, " are active\n"); - fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n"); - fprintf(fp, " current component to URL\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `source.ctf.fs` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); - fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `sink.text.pretty` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); - fprintf(fp, " --clock-date Print timestamp dates\n"); - fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n"); - fprintf(fp, " time zone instead of the local time zone\n"); - fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n"); - fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n"); - fprintf(fp, " --color=(never | auto | always)\n"); - fprintf(fp, " Never, automatically, or always emit\n"); - fprintf(fp, " console color codes\n"); - fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n"); - fprintf(fp, " `all`, `trace`, `trace:hostname`,\n"); - fprintf(fp, " `trace:domain`, `trace:procname`,\n"); - fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n"); - fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n"); - fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n"); - fprintf(fp, " `all`, `scope`, `header`, `context`\n"); - fprintf(fp, " (or `ctx`)\n"); - fprintf(fp, " --no-delta Do not print time delta between\n"); - fprintf(fp, " consecutive events\n"); - fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n"); - fprintf(fp, " the standard output\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.utils.muxer` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n"); - fprintf(fp, " correlated across traces\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n"); - fprintf(fp, " time range to BEGIN (see the format of\n"); - fprintf(fp, " BEGIN below)\n"); - fprintf(fp, " -e, --end=END Set the end time of the conversion time\n"); - fprintf(fp, " range to END (see the format of END below)\n"); - fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n"); - fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n"); - fprintf(fp, " `]`) (see the format of BEGIN/END below)\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --debug-info Create an implicit\n"); - fprintf(fp, " `filter.lttng-utils.debug-info` component\n"); - fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); - fprintf(fp, " instead of `/usr/lib/debug`\n"); - fprintf(fp, " --debug-info-full-path Show full debug info source and\n"); - fprintf(fp, " binary paths instead of just names\n"); - fprintf(fp, " --debug-info-target-prefix=DIR\n"); - fprintf(fp, " Use directory DIR as a prefix when\n"); - fprintf(fp, " looking up executables during debug\n"); - fprintf(fp, " info analysis\n"); - fprintf(fp, "\n"); - fprintf(fp, "Legacy options that still work:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n"); - fprintf(fp, " `ctf`:\n"); - fprintf(fp, " Create an implicit `source.ctf.fs`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `lttng-live`:\n"); - fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n"); - fprintf(fp, " `text`:\n"); - fprintf(fp, " Create an implicit `sink.text.pretty`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `ctf`:\n"); - fprintf(fp, " Create an implicit `sink.ctf.fs`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `dummy`:\n"); - fprintf(fp, " Create an implicit `sink.utils.dummy`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `ctf-metadata`:\n"); - fprintf(fp, " Query the `source.ctf.fs` component class\n"); - fprintf(fp, " for metadata text and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "Format of BEGIN and END\n"); - fprintf(fp, "-----------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -static -struct poptOption convert_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL }, - { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL }, - { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, - { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, - { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, - { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL }, - { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL }, - { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL }, - { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, - { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL }, - { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL }, - { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL }, - { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL }, - { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, - { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, - { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, - { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL }, - { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL }, - { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL }, - { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL }, - { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL }, - { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, - { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, - { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL }, - { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -static -GString *get_component_auto_name(const char *prefix, - const bt_value *existing_names) -{ - unsigned int i = 0; - GString *auto_name = g_string_new(NULL); - - if (!auto_name) { - print_err_oom(); - goto end; - } - - if (!bt_value_map_has_entry(existing_names, prefix)) { - g_string_assign(auto_name, prefix); - goto end; - } - - do { - g_string_printf(auto_name, "%s-%d", prefix, i); - i++; - } while (bt_value_map_has_entry(existing_names, auto_name->str)); - -end: - return auto_name; -} - -struct implicit_component_args { - bool exists; - GString *comp_arg; - GString *name_arg; - GString *params_arg; - bt_value *extra_params; -}; - -static -int assign_name_to_implicit_component(struct implicit_component_args *args, - const char *prefix, bt_value *existing_names, - GList **comp_names, bool append_to_comp_names) -{ - int ret = 0; - GString *name = NULL; - - if (!args->exists) { - goto end; - } - - name = get_component_auto_name(prefix, - existing_names); - - if (!name) { - ret = -1; - goto end; - } - - g_string_assign(args->name_arg, name->str); - - if (bt_value_map_insert_entry(existing_names, name->str, - bt_value_null)) { - print_err_oom(); - ret = -1; - goto end; - } - - if (append_to_comp_names) { - *comp_names = g_list_append(*comp_names, name); - name = NULL; - } - -end: - if (name) { - g_string_free(name, TRUE); - } - - return ret; -} - -static -int append_run_args_for_implicit_component( - struct implicit_component_args *impl_args, - bt_value *run_args) -{ - int ret = 0; - size_t i; - - if (!impl_args->exists) { - goto end; - } - - if (bt_value_array_append_string_element(run_args, "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) { - print_err_oom(); - goto error; - } - - if (impl_args->params_arg->len > 0) { - if (bt_value_array_append_string_element(run_args, "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, - impl_args->params_arg->str)) { - print_err_oom(); - goto error; - } - } - - for (i = 0; i < bt_value_array_get_size(impl_args->extra_params); - i++) { - const bt_value *elem; - const char *arg; - - elem = bt_value_array_borrow_element_by_index(impl_args->extra_params, - i); - if (!elem) { - goto error; - } - - BT_ASSERT(bt_value_is_string(elem)); - arg = bt_value_string_get(elem); - ret = bt_value_array_append_string_element(run_args, arg); - if (ret) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -void finalize_implicit_component_args(struct implicit_component_args *args) -{ - BT_ASSERT(args); - - if (args->comp_arg) { - g_string_free(args->comp_arg, TRUE); - } - - if (args->name_arg) { - g_string_free(args->name_arg, TRUE); - } - - if (args->params_arg) { - g_string_free(args->params_arg, TRUE); - } - - bt_value_put_ref(args->extra_params); -} - -static -int init_implicit_component_args(struct implicit_component_args *args, - const char *comp_arg, bool exists) -{ - int ret = 0; - - args->exists = exists; - args->comp_arg = g_string_new(comp_arg); - args->name_arg = g_string_new(NULL); - args->params_arg = g_string_new(NULL); - args->extra_params = bt_value_array_create(); - - if (!args->comp_arg || !args->name_arg || - !args->params_arg || !args->extra_params) { - ret = -1; - finalize_implicit_component_args(args); - print_err_oom(); - goto end; - } - -end: - return ret; -} - -static -void append_implicit_component_param(struct implicit_component_args *args, - const char *key, const char *value) -{ - BT_ASSERT(args); - BT_ASSERT(key); - BT_ASSERT(value); - append_param_arg(args->params_arg, key, value); -} - -/* Escape value to make it suitable to use as a string parameter value. */ -static -gchar *escape_string_value(const char *value) -{ - GString *ret; - const char *in; - - ret = g_string_new(NULL); - if (!ret) { - print_err_oom(); - goto end; - } - - in = value; - while (*in) { - switch (*in) { - case '"': - case '\\': - g_string_append_c(ret, '\\'); - break; - } - - g_string_append_c(ret, *in); - - in++; - } - -end: - return g_string_free(ret, FALSE); -} - -static -int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) -{ - BT_ASSERT(buf); - - int ret = -1; - - switch (bt_value_get_type(value)) { - case BT_VALUE_TYPE_STRING: - { - const char *str_value = bt_value_string_get(value); - gchar *escaped_str_value; - - escaped_str_value = escape_string_value(str_value); - if (!escaped_str_value) { - goto end; - } - - g_string_append_printf(buf, "\"%s\"", escaped_str_value); - - g_free(escaped_str_value); - break; - } - case BT_VALUE_TYPE_ARRAY: { - g_string_append_c(buf, '['); - uint64_t sz = bt_value_array_get_size(value); - for (uint64_t i = 0; i < sz; i++) { - const bt_value *item; - int ret; - - if (i > 0) { - g_string_append(buf, ", "); - } - - item = bt_value_array_borrow_element_by_index_const( - value, i); - ret = bt_value_to_cli_param_value_append(item, buf); - - if (ret) { - goto end; - } - } - g_string_append_c(buf, ']'); - break; - } - default: - abort(); - } - - ret = 0; - -end: - return ret; -} - -/* - * Convert `value` to its equivalent representation as a command line parameter - * value. - */ - -static -gchar *bt_value_to_cli_param_value(bt_value *value) -{ - GString *buf; - gchar *result = NULL; - - buf = g_string_new(NULL); - if (!buf) { - print_err_oom(); - goto error; - } - - if (bt_value_to_cli_param_value_append(value, buf)) { - goto error; - } - - result = g_string_free(buf, FALSE); - buf = NULL; - - goto end; - -error: - if (buf) { - g_string_free(buf, TRUE); - } - -end: - return result; -} - -static -int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) -{ - BT_ASSERT(args); - BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY); - BT_ASSERT(key); - BT_ASSERT(value); - - int ret = 0; - gchar *str_value = NULL; - GString *parameter = NULL; - - if (bt_value_array_append_string_element(args, "--params")) { - print_err_oom(); - ret = -1; - goto end; - } - - str_value = bt_value_to_cli_param_value(value); - if (!str_value) { - ret = -1; - goto end; - } - - parameter = g_string_new(NULL); - if (!parameter) { - print_err_oom(); - ret = -1; - goto end; - } - - g_string_printf(parameter, "%s=%s", key, str_value); - - if (bt_value_array_append_string_element(args, parameter->str)) { - print_err_oom(); - ret = -1; - goto end; - } - -end: - if (parameter) { - g_string_free(parameter, TRUE); - parameter = NULL; - } - - if (str_value) { - g_free(str_value); - str_value = NULL; - } - - return ret; -} - -static -int append_string_parameter_to_args(bt_value *args, const char *key, const char *value) -{ - bt_value *str_value; - int ret; - - str_value = bt_value_string_create_init(value); - - if (!str_value) { - print_err_oom(); - ret = -1; - goto end; - } - - ret = append_parameter_to_args(args, key, str_value); - -end: - BT_VALUE_PUT_REF_AND_RESET(str_value); - return ret; -} - -static -int append_implicit_component_extra_param(struct implicit_component_args *args, - const char *key, const char *value) -{ - return append_string_parameter_to_args(args->extra_params, key, value); -} - -static -int convert_append_name_param(enum bt_config_component_dest dest, - GString *cur_name, GString *cur_name_prefix, - bt_value *run_args, - bt_value *all_names, - GList **source_names, GList **filter_names, - GList **sink_names) -{ - int ret = 0; - - if (cur_name_prefix->len > 0) { - /* We're after a --component option */ - GString *name = NULL; - bool append_name_opt = false; - - if (cur_name->len == 0) { - /* - * No explicit name was provided for the user - * component. - */ - name = get_component_auto_name(cur_name_prefix->str, - all_names); - append_name_opt = true; - } else { - /* - * An explicit name was provided for the user - * component. - */ - if (bt_value_map_has_entry(all_names, - cur_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", - cur_name->str); - goto error; - } - - name = g_string_new(cur_name->str); - } - - if (!name) { - print_err_oom(); - goto error; - } - - /* - * Remember this name globally, for the uniqueness of - * all component names. - */ - if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) { - print_err_oom(); - goto error; - } - - /* - * Append the --name option if necessary. - */ - if (append_name_opt) { - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, name->str)) { - print_err_oom(); - goto error; - } - } - - /* - * Remember this name specifically for the type of the - * component. This is to create connection arguments. - */ - switch (dest) { - case BT_CONFIG_COMPONENT_DEST_SOURCE: - *source_names = g_list_append(*source_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_FILTER: - *filter_names = g_list_append(*filter_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_SINK: - *sink_names = g_list_append(*sink_names, name); - break; - default: - abort(); - } - - g_string_assign(cur_name_prefix, ""); - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -/* - * Escapes `.`, `:`, and `\` of `input` with `\`. - */ -static -GString *escape_dot_colon(const char *input) -{ - GString *output = g_string_new(NULL); - const char *ch; - - if (!output) { - print_err_oom(); - goto end; - } - - for (ch = input; *ch != '\0'; ch++) { - if (*ch == '\\' || *ch == '.' || *ch == ':') { - g_string_append_c(output, '\\'); - } - - g_string_append_c(output, *ch); - } - -end: - return output; -} - -/* - * Appends a --connect option to a list of arguments. `upstream_name` - * and `downstream_name` are escaped with escape_dot_colon() in this - * function. - */ -static -int append_connect_arg(bt_value *run_args, - const char *upstream_name, const char *downstream_name) -{ - int ret = 0; - GString *e_upstream_name = escape_dot_colon(upstream_name); - GString *e_downstream_name = escape_dot_colon(downstream_name); - GString *arg = g_string_new(NULL); - - if (!e_upstream_name || !e_downstream_name || !arg) { - print_err_oom(); - ret = -1; - goto end; - } - - ret = bt_value_array_append_string_element(run_args, "--connect"); - if (ret) { - print_err_oom(); - ret = -1; - goto end; - } - - g_string_append(arg, e_upstream_name->str); - g_string_append_c(arg, ':'); - g_string_append(arg, e_downstream_name->str); - ret = bt_value_array_append_string_element(run_args, arg->str); - if (ret) { - print_err_oom(); - ret = -1; - goto end; - } - -end: - if (arg) { - g_string_free(arg, TRUE); - } - - if (e_upstream_name) { - g_string_free(e_upstream_name, TRUE); - } - - if (e_downstream_name) { - g_string_free(e_downstream_name, TRUE); - } - - return ret; -} - -/* - * Appends the run command's --connect options for the convert command. - */ -static -int convert_auto_connect(bt_value *run_args, - GList *source_names, GList *filter_names, - GList *sink_names) -{ - int ret = 0; - GList *source_at = source_names; - GList *filter_at = filter_names; - GList *filter_prev; - GList *sink_at = sink_names; - - BT_ASSERT(source_names); - BT_ASSERT(filter_names); - BT_ASSERT(sink_names); - - /* Connect all sources to the first filter */ - for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) { - GString *source_name = source_at->data; - GString *filter_name = filter_at->data; - - ret = append_connect_arg(run_args, source_name->str, - filter_name->str); - if (ret) { - goto error; - } - } - - filter_prev = filter_at; - filter_at = g_list_next(filter_at); - - /* Connect remaining filters */ - for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { - GString *filter_name = filter_at->data; - GString *filter_prev_name = filter_prev->data; - - ret = append_connect_arg(run_args, filter_prev_name->str, - filter_name->str); - if (ret) { - goto error; - } - } - - /* Connect last filter to all sinks */ - for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) { - GString *filter_name = filter_prev->data; - GString *sink_name = sink_at->data; - - ret = append_connect_arg(run_args, filter_name->str, - sink_name->str); - if (ret) { - goto error; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int split_timerange(const char *arg, char **begin, char **end) -{ - int ret = 0; - const char *ch = arg; - size_t end_pos; - GString *g_begin = NULL; - GString *g_end = NULL; - - BT_ASSERT(arg); - - if (*ch == '[') { - ch++; - } - - g_begin = bt_common_string_until(ch, "", ",", &end_pos); - if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) { - goto error; - } - - ch += end_pos + 1; - - g_end = bt_common_string_until(ch, "", "]", &end_pos); - if (!g_end || g_end->len == 0) { - goto error; - } - - BT_ASSERT(begin); - BT_ASSERT(end); - *begin = g_begin->str; - *end = g_end->str; - g_string_free(g_begin, FALSE); - g_string_free(g_end, FALSE); - g_begin = NULL; - g_end = NULL; - goto end; - -error: - ret = -1; - -end: - if (g_begin) { - g_string_free(g_begin, TRUE); - } - - if (g_end) { - g_string_free(g_end, TRUE); - } - - return ret; -} - -static -int g_list_prepend_gstring(GList **list, const char *string) -{ - int ret = 0; - GString *gs = g_string_new(string); - - BT_ASSERT(list); - - if (!gs) { - print_err_oom(); - goto end; - } - - *list = g_list_prepend(*list, gs); - -end: - return ret; -} - -/* - * Creates a Babeltrace config object from the arguments of a convert - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, char *log_level) -{ - poptContext pc = NULL; - char *arg = NULL; - enum bt_config_component_dest cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; - int opt, ret = 0; - struct bt_config *cfg = NULL; - bool got_input_format_opt = false; - bool got_output_format_opt = false; - bool trimmer_has_begin = false; - bool trimmer_has_end = false; - bool stream_intersection_mode = false; - GString *cur_name = NULL; - GString *cur_name_prefix = NULL; - const char *leftover = NULL; - bool print_run_args = false; - bool print_run_args_0 = false; - bool print_ctf_metadata = false; - bt_value *run_args = NULL; - bt_value *all_names = NULL; - GList *source_names = NULL; - GList *filter_names = NULL; - GList *sink_names = NULL; - bt_value *leftovers = NULL; - struct implicit_component_args implicit_ctf_input_args = { 0 }; - struct implicit_component_args implicit_ctf_output_args = { 0 }; - struct implicit_component_args implicit_lttng_live_args = { 0 }; - struct implicit_component_args implicit_dummy_args = { 0 }; - struct implicit_component_args implicit_text_args = { 0 }; - struct implicit_component_args implicit_debug_info_args = { 0 }; - struct implicit_component_args implicit_muxer_args = { 0 }; - struct implicit_component_args implicit_trimmer_args = { 0 }; - bt_value *plugin_paths; - char error_buf[256] = { 0 }; - size_t i; - struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; - char *output = NULL; - - (void) bt_value_copy(initial_plugin_paths, &plugin_paths); - - *retcode = 0; - - if (argc <= 1) { - print_convert_usage(stdout); - *retcode = -1; - goto end; - } - - if (init_implicit_component_args(&implicit_ctf_input_args, - "source.ctf.fs", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_ctf_output_args, - "sink.ctf.fs", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_lttng_live_args, - "source.ctf.lttng-live", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_text_args, - "sink.text.pretty", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_dummy_args, - "sink.utils.dummy", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_debug_info_args, - "filter.lttng-utils.debug-info", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_muxer_args, - "filter.utils.muxer", true)) { - goto error; - } - - if (init_implicit_component_args(&implicit_trimmer_args, - "filter.utils.trimmer", false)) { - goto error; - } - - all_names = bt_value_map_create(); - if (!all_names) { - print_err_oom(); - goto error; - } - - run_args = bt_value_array_create(); - if (!run_args) { - print_err_oom(); - goto error; - } - - cur_name = g_string_new(NULL); - if (!cur_name) { - print_err_oom(); - goto error; - } - - cur_name_prefix = g_string_new(NULL); - if (!cur_name_prefix) { - print_err_oom(); - goto error; - } - - ret = append_env_var_plugin_paths(plugin_paths); - if (ret) { - goto error; - } - - leftovers = bt_value_array_create(); - if (!leftovers) { - print_err_oom(); - goto error; - } - - /* - * First pass: collect all arguments which need to be passed - * as is to the run command. This pass can also add --name - * arguments if needed to automatically name unnamed component - * instances. Also it does the following transformations: - * - * --path=PATH -> --params=path="PATH" - * --url=URL -> --params=url="URL" - * - * Also it appends the plugin paths of --plugin-path to - * `plugin_paths`. - */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - char *name = NULL; - char *plugin_name = NULL; - char *comp_cls_name = NULL; - - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_COMPONENT: - { - bt_component_class_type type; - const char *type_prefix; - - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, - &source_names, &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Parse the argument */ - plugin_comp_cls_names(arg, &name, &plugin_name, - &comp_cls_name, &type); - if (!plugin_name || !comp_cls_name) { - printf_err("Invalid format for --component option's argument:\n %s\n", - arg); - goto error; - } - - if (name) { - g_string_assign(cur_name, name); - } else { - g_string_assign(cur_name, ""); - } - - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - type_prefix = "source"; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - type_prefix = "filter"; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; - type_prefix = "sink"; - break; - default: - abort(); - } - - if (bt_value_array_append_string_element(run_args, - "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name_prefix, ""); - g_string_append_printf(cur_name_prefix, "%s.%s.%s", - type_prefix, plugin_name, comp_cls_name); - free(name); - free(plugin_name); - free(comp_cls_name); - name = NULL; - plugin_name = NULL; - comp_cls_name = NULL; - break; - } - case OPT_PARAMS: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set parameters:\n %s\n", - arg); - goto error; - } - - if (bt_value_array_append_string_element(run_args, - "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_PATH: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `path` parameter:\n %s\n", - arg); - goto error; - } - - if (append_string_parameter_to_args(run_args, "path", arg)) { - goto error; - } - break; - case OPT_URL: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `url` parameter:\n %s\n", - arg); - goto error; - } - - - if (append_string_parameter_to_args(run_args, "url", arg)) { - goto error; - } - break; - case OPT_NAME: - if (cur_name_prefix->len == 0) { - printf_err("No current component to name:\n %s\n", - arg); - goto error; - } - - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name, arg); - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - force_omit_home_plugin_path = true; - - if (bt_value_array_append_string_element(run_args, - "--omit-home-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_RETRY_DURATION: - if (bt_value_array_append_string_element(run_args, - "--retry-duration")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - force_omit_system_plugin_path = true; - - if (bt_value_array_append_string_element(run_args, - "--omit-system-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - plugin_paths, arg)) { - goto error; - } - - if (bt_value_array_append_string_element(run_args, - "--plugin-path")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_HELP: - print_convert_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - case OPT_BEGIN: - case OPT_CLOCK_CYCLES: - case OPT_CLOCK_DATE: - case OPT_CLOCK_FORCE_CORRELATE: - case OPT_CLOCK_GMT: - case OPT_CLOCK_OFFSET: - case OPT_CLOCK_OFFSET_NS: - case OPT_CLOCK_SECONDS: - case OPT_COLOR: - case OPT_DEBUG: - case OPT_DEBUG_INFO: - case OPT_DEBUG_INFO_DIR: - case OPT_DEBUG_INFO_FULL_PATH: - case OPT_DEBUG_INFO_TARGET_PREFIX: - case OPT_END: - case OPT_FIELDS: - case OPT_INPUT_FORMAT: - case OPT_NAMES: - case OPT_NO_DELTA: - case OPT_OUTPUT_FORMAT: - case OPT_OUTPUT: - case OPT_RUN_ARGS: - case OPT_RUN_ARGS_0: - case OPT_STREAM_INTERSECTION: - case OPT_TIMERANGE: - case OPT_VERBOSE: - /* Ignore in this pass */ - break; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, &source_names, - &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - poptFreeContext(pc); - free(arg); - arg = NULL; - - /* - * Second pass: transform the convert-specific options and - * arguments into implicit component instances for the run - * command. - */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_BEGIN: - if (trimmer_has_begin) { - printf("At --begin option: --begin or --timerange option already specified\n %s\n", - arg); - goto error; - } - - trimmer_has_begin = true; - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "begin", arg); - implicit_trimmer_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_END: - if (trimmer_has_end) { - printf("At --end option: --end or --timerange option already specified\n %s\n", - arg); - goto error; - } - - trimmer_has_end = true; - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "end", arg); - implicit_trimmer_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_TIMERANGE: - { - char *begin; - char *end; - - if (trimmer_has_begin || trimmer_has_end) { - printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", - arg); - goto error; - } - - ret = split_timerange(arg, &begin, &end); - if (ret) { - printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n", - arg); - goto error; - } - - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "begin", begin); - ret |= append_implicit_component_extra_param( - &implicit_trimmer_args, "end", end); - implicit_trimmer_args.exists = true; - free(begin); - free(end); - if (ret) { - goto error; - } - break; - } - case OPT_CLOCK_CYCLES: - append_implicit_component_param( - &implicit_text_args, "clock-cycles", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_DATE: - append_implicit_component_param( - &implicit_text_args, "clock-date", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_FORCE_CORRELATE: - append_implicit_component_param( - &implicit_muxer_args, - "assume-absolute-clock-classes", "yes"); - break; - case OPT_CLOCK_GMT: - append_implicit_component_param( - &implicit_text_args, "clock-gmt", "yes"); - append_implicit_component_param( - &implicit_trimmer_args, "gmt", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_OFFSET: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-s", arg); - break; - case OPT_CLOCK_OFFSET_NS: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-ns", arg); - break; - case OPT_CLOCK_SECONDS: - append_implicit_component_param( - &implicit_text_args, "clock-seconds", "yes"); - implicit_text_args.exists = true; - break; - case OPT_COLOR: - implicit_text_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_text_args, "color", arg); - if (ret) { - goto error; - } - break; - case OPT_DEBUG_INFO: - implicit_debug_info_args.exists = true; - break; - case OPT_DEBUG_INFO_DIR: - implicit_debug_info_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_debug_info_args, "debug-info-dir", arg); - if (ret) { - goto error; - } - break; - case OPT_DEBUG_INFO_FULL_PATH: - implicit_debug_info_args.exists = true; - append_implicit_component_param( - &implicit_debug_info_args, "full-path", "yes"); - break; - case OPT_DEBUG_INFO_TARGET_PREFIX: - implicit_debug_info_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_debug_info_args, - "target-prefix", arg); - if (ret) { - goto error; - } - break; - case OPT_FIELDS: - { - bt_value *fields = fields_from_arg(arg); - - if (!fields) { - goto error; - } - - implicit_text_args.exists = true; - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - fields, "field"); - bt_value_put_ref(fields); - if (ret) { - goto error; - } - break; - } - case OPT_NAMES: - { - bt_value *names = names_from_arg(arg); - - if (!names) { - goto error; - } - - implicit_text_args.exists = true; - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - names, "name"); - bt_value_put_ref(names); - if (ret) { - goto error; - } - break; - } - case OPT_NO_DELTA: - append_implicit_component_param( - &implicit_text_args, "no-delta", "yes"); - implicit_text_args.exists = true; - break; - case OPT_INPUT_FORMAT: - if (got_input_format_opt) { - printf_err("Duplicate --input-format option\n"); - goto error; - } - - got_input_format_opt = true; - - if (strcmp(arg, "ctf") == 0) { - implicit_ctf_input_args.exists = true; - } else if (strcmp(arg, "lttng-live") == 0) { - implicit_lttng_live_args.exists = true; - } else { - printf_err("Unknown legacy input format:\n %s\n", - arg); - goto error; - } - break; - case OPT_OUTPUT_FORMAT: - if (got_output_format_opt) { - printf_err("Duplicate --output-format option\n"); - goto error; - } - - got_output_format_opt = true; - - if (strcmp(arg, "text") == 0) { - implicit_text_args.exists = true; - } else if (strcmp(arg, "ctf") == 0) { - implicit_ctf_output_args.exists = true; - } else if (strcmp(arg, "dummy") == 0) { - implicit_dummy_args.exists = true; - } else if (strcmp(arg, "ctf-metadata") == 0) { - print_ctf_metadata = true; - } else { - printf_err("Unknown legacy output format:\n %s\n", - arg); - goto error; - } - break; - case OPT_OUTPUT: - if (output) { - printf_err("Duplicate --output option\n"); - goto error; - } - - output = strdup(arg); - if (!output) { - print_err_oom(); - goto error; - } - break; - case OPT_RUN_ARGS: - if (print_run_args_0) { - printf_err("Cannot specify --run-args and --run-args-0\n"); - goto error; - } - - print_run_args = true; - break; - case OPT_RUN_ARGS_0: - if (print_run_args) { - printf_err("Cannot specify --run-args and --run-args-0\n"); - goto error; - } - - print_run_args_0 = true; - break; - case OPT_STREAM_INTERSECTION: - /* - * Applies to all traces implementing the trace-info - * query. - */ - stream_intersection_mode = true; - break; - case OPT_VERBOSE: - if (*log_level != 'V' && *log_level != 'D') { - *log_level = 'I'; - } - break; - case OPT_DEBUG: - *log_level = 'V'; - break; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* - * Legacy behaviour: --verbose used to make the `text` output - * format print more information. --verbose is now equivalent to - * the INFO log level, which is why we compare to 'I' here. - */ - if (*log_level == 'I') { - append_implicit_component_param(&implicit_text_args, - "verbose", "yes"); - } - - /* - * Append home and system plugin paths now that we possibly got - * --plugin-path. - */ - if (append_home_and_system_plugin_paths(plugin_paths, - force_omit_system_plugin_path, - force_omit_home_plugin_path)) { - goto error; - } - - /* Consume and keep leftover arguments */ - while ((leftover = poptGetArg(pc))) { - bt_value_status status = bt_value_array_append_string_element(leftovers, leftover); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); - goto error; - } - } - - /* Print CTF metadata or print LTTng live sessions */ - if (print_ctf_metadata) { - const bt_value *bt_val_leftover; - - if (bt_value_array_is_empty(leftovers)) { - printf_err("--output-format=ctf-metadata specified without a path\n"); - goto error; - } - - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many paths specified for --output-format=ctf-metadata\n"); - goto error; - } - - cfg = bt_config_print_ctf_metadata_create(plugin_paths); - if (!cfg) { - goto error; - } - - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); - g_string_assign(cfg->cmd_data.print_ctf_metadata.path, - bt_value_string_get(bt_val_leftover)); - - if (output) { - g_string_assign( - cfg->cmd_data.print_ctf_metadata.output_path, - output); - } - - goto end; - } - - /* - * If -o ctf was specified, make sure an output path (--output) - * was also specified. --output does not imply -o ctf because - * it's also used for the default, implicit -o text if -o ctf - * is not specified. - */ - if (implicit_ctf_output_args.exists) { - if (!output) { - printf_err("--output-format=ctf specified without --output (trace output path)\n"); - goto error; - } - - /* - * At this point we know that -o ctf AND --output were - * specified. Make sure that no options were specified - * which would imply -o text because --output would be - * ambiguous in this case. For example, this is wrong: - * - * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace - * - * because --names=all implies -o text, and --output - * could apply to both the sink.text.pretty and - * sink.ctf.fs implicit components. - */ - if (implicit_text_args.exists) { - printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n"); - goto error; - } - } - - /* - * If -o dummy and -o ctf were not specified, and if there are - * no explicit sink components, then use an implicit - * `sink.text.pretty` component. - */ - if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists && - !sink_names) { - implicit_text_args.exists = true; - } - - /* - * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's - * `path` parameter if --output was specified. - */ - if (output) { - if (implicit_text_args.exists) { - append_implicit_component_extra_param(&implicit_text_args, - "path", output); - } else if (implicit_ctf_output_args.exists) { - append_implicit_component_extra_param(&implicit_ctf_output_args, - "path", output); - } - } - - /* Decide where the leftover argument(s) go */ - if (bt_value_array_get_size(leftovers) > 0) { - if (implicit_lttng_live_args.exists) { - const bt_value *bt_val_leftover; - - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many URLs specified for --output-format=lttng-live\n"); - goto error; - } - - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); - lttng_live_url_parts = - bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover), - error_buf, sizeof(error_buf)); - if (!lttng_live_url_parts.proto) { - printf_err("Invalid LTTng live URL format: %s\n", - error_buf); - goto error; - } - - if (!lttng_live_url_parts.session_name) { - /* Print LTTng live sessions */ - cfg = bt_config_print_lttng_live_sessions_create( - plugin_paths); - if (!cfg) { - goto error; - } - - g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, - bt_value_string_get(bt_val_leftover)); - - if (output) { - g_string_assign( - cfg->cmd_data.print_lttng_live_sessions.output_path, - output); - } - - goto end; - } - - ret = append_implicit_component_extra_param( - &implicit_lttng_live_args, "url", - bt_value_string_get(bt_val_leftover)); - if (ret) { - goto error; - } - - ret = append_implicit_component_extra_param( - &implicit_lttng_live_args, - "session-not-found-action", "end"); - if (ret) { - goto error; - } - } else { - /* - * Create one source.ctf.fs component, pass it an array - * with the leftovers. - * Note that it still has to be named later. - */ - implicit_ctf_input_args.exists = true; - ret = append_parameter_to_args(implicit_ctf_input_args.extra_params, - "paths", leftovers); - if (ret) { - goto error; - } - } - } - - /* - * Ensure mutual exclusion between implicit `source.ctf.fs` and - * `source.ctf.lttng-live` components. - */ - if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) { - printf_err("Cannot create both implicit `%s` and `%s` components\n", - implicit_ctf_input_args.comp_arg->str, - implicit_lttng_live_args.comp_arg->str); - goto error; - } - - /* - * If the implicit `source.ctf.fs` or `source.ctf.lttng-live` - * components exists, make sure there's at least one leftover - * (which is the path or URL). - */ - if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing path for implicit `%s` component\n", - implicit_ctf_input_args.comp_arg->str); - goto error; - } - - if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing URL for implicit `%s` component\n", - implicit_lttng_live_args.comp_arg->str); - goto error; - } - - /* Assign names to implicit components */ - ret = assign_name_to_implicit_component(&implicit_ctf_input_args, - "source-ctf-fs", all_names, &source_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_lttng_live_args, - "lttng-live", all_names, &source_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_text_args, - "pretty", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_ctf_output_args, - "sink-ctf-fs", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_dummy_args, - "dummy", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_muxer_args, - "muxer", all_names, NULL, false); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_trimmer_args, - "trimmer", all_names, NULL, false); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_debug_info_args, - "debug-info", all_names, NULL, false); - if (ret) { - goto error; - } - - /* Make sure there's at least one source and one sink */ - if (!source_names) { - printf_err("No source component\n"); - goto error; - } - - if (!sink_names) { - printf_err("No sink component\n"); - goto error; - } - - /* - * Prepend the muxer, the trimmer, and the debug info to the - * filter chain so that we have: - * - * sources -> muxer -> [trimmer] -> [debug info] -> - * [user filters] -> sinks - */ - if (implicit_debug_info_args.exists) { - if (g_list_prepend_gstring(&filter_names, - implicit_debug_info_args.name_arg->str)) { - goto error; - } - } - - if (implicit_trimmer_args.exists) { - if (g_list_prepend_gstring(&filter_names, - implicit_trimmer_args.name_arg->str)) { - goto error; - } - } - - if (g_list_prepend_gstring(&filter_names, - implicit_muxer_args.name_arg->str)) { - goto error; - } - - /* - * Append the equivalent run arguments for the implicit - * components. - */ - ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_lttng_live_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_text_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_ctf_output_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_dummy_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_muxer_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_trimmer_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_debug_info_args, - run_args); - if (ret) { - goto error; - } - - /* Auto-connect components */ - ret = convert_auto_connect(run_args, source_names, filter_names, - sink_names); - if (ret) { - printf_err("Cannot auto-connect components\n"); - goto error; - } - - /* - * We have all the run command arguments now. Depending on - * --run-args, we pass this to the run command or print them - * here. - */ - if (print_run_args || print_run_args_0) { - if (stream_intersection_mode) { - printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n"); - goto error; - } - - for (i = 0; i < bt_value_array_get_size(run_args); i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index(run_args, - i); - const char *arg; - GString *quoted = NULL; - const char *arg_to_print; - - BT_ASSERT(arg_value); - arg = bt_value_string_get(arg_value); - - if (print_run_args) { - quoted = bt_common_shell_quote(arg, true); - if (!quoted) { - goto error; - } - - arg_to_print = quoted->str; - } else { - arg_to_print = arg; - } - - printf("%s", arg_to_print); - - if (quoted) { - g_string_free(quoted, TRUE); - } - - if (i < bt_value_array_get_size(run_args) - 1) { - if (print_run_args) { - putchar(' '); - } else { - putchar('\0'); - } - } - } - - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - cfg = bt_config_run_from_args_array(run_args, retcode, - force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode; - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - free(output); - - if (cur_name) { - g_string_free(cur_name, TRUE); - } - - if (cur_name_prefix) { - g_string_free(cur_name_prefix, TRUE); - } - - bt_value_put_ref(run_args); - bt_value_put_ref(all_names); - destroy_glist_of_gstring(source_names); - destroy_glist_of_gstring(filter_names); - destroy_glist_of_gstring(sink_names); - bt_value_put_ref(leftovers); - finalize_implicit_component_args(&implicit_ctf_input_args); - finalize_implicit_component_args(&implicit_ctf_output_args); - finalize_implicit_component_args(&implicit_lttng_live_args); - finalize_implicit_component_args(&implicit_dummy_args); - finalize_implicit_component_args(&implicit_text_args); - finalize_implicit_component_args(&implicit_debug_info_args); - finalize_implicit_component_args(&implicit_muxer_args); - finalize_implicit_component_args(&implicit_trimmer_args); - bt_value_put_ref(plugin_paths); - bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); - return cfg; -} - -/* - * Prints the Babeltrace 2.x general usage. - */ -static -void print_gen_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "General options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, " -l, --log-level=LVL Set all log levels to LVL (`N`, `V`, `D`,\n"); - fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); - fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); - fprintf(fp, " -V, --version Show version and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "Available commands:\n"); - fprintf(fp, "\n"); - fprintf(fp, " convert Convert and trim traces (default)\n"); - fprintf(fp, " help Get help for a plugin or a component class\n"); - fprintf(fp, " list-plugins List available plugins and their content\n"); - fprintf(fp, " query Query objects from a component class\n"); - fprintf(fp, " run Build a processing graph and run it\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n"); -} - -static -char log_level_from_arg(const char *arg) -{ - char level = 'U'; - - if (strcmp(arg, "VERBOSE") == 0 || - strcmp(arg, "V") == 0) { - level = 'V'; - } else if (strcmp(arg, "DEBUG") == 0 || - strcmp(arg, "D") == 0) { - level = 'D'; - } else if (strcmp(arg, "INFO") == 0 || - strcmp(arg, "I") == 0) { - level = 'I'; - } else if (strcmp(arg, "WARN") == 0 || - strcmp(arg, "WARNING") == 0 || - strcmp(arg, "W") == 0) { - level = 'W'; - } else if (strcmp(arg, "ERROR") == 0 || - strcmp(arg, "E") == 0) { - level = 'E'; - } else if (strcmp(arg, "FATAL") == 0 || - strcmp(arg, "F") == 0) { - level = 'F'; - } else if (strcmp(arg, "NONE") == 0 || - strcmp(arg, "N") == 0) { - level = 'N'; - } - - return level; -} - -struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - struct bt_config *config = NULL; - int i; - const char **command_argv = NULL; - int command_argc = -1; - const char *command_name = NULL; - char log_level = 'U'; - - enum command_type { - COMMAND_TYPE_NONE = -1, - COMMAND_TYPE_RUN = 0, - COMMAND_TYPE_CONVERT, - COMMAND_TYPE_LIST_PLUGINS, - COMMAND_TYPE_HELP, - COMMAND_TYPE_QUERY, - } command_type = COMMAND_TYPE_NONE; - - *retcode = -1; - - if (!initial_plugin_paths) { - initial_plugin_paths = bt_value_array_create(); - if (!initial_plugin_paths) { - *retcode = 1; - goto end; - } - } else { - bt_value_get_ref(initial_plugin_paths); - } - - if (argc <= 1) { - print_version(); - puts(""); - print_gen_usage(stdout); - goto end; - } - - for (i = 1; i < argc; i++) { - const char *cur_arg = argv[i]; - const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1]; - - if (strcmp(cur_arg, "-d") == 0 || - strcmp(cur_arg, "--debug") == 0) { - log_level = 'V'; - } else if (strcmp(cur_arg, "-v") == 0 || - strcmp(cur_arg, "--verbose") == 0) { - if (log_level != 'V' && log_level != 'D') { - /* - * Legacy: do not override a previous - * --debug because --verbose and --debug - * can be specified together (in this - * case we want the lowest log level to - * apply, VERBOSE). - */ - log_level = 'I'; - } - } else if (strcmp(cur_arg, "--log-level") == 0 || - strcmp(cur_arg, "-l") == 0) { - if (!next_arg) { - printf_err("Missing log level value for --log-level option\n"); - *retcode = 1; - goto end; - } - - log_level = log_level_from_arg(next_arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - next_arg); - *retcode = 1; - goto end; - } - - i++; - } else if (strncmp(cur_arg, "--log-level=", 12) == 0) { - const char *arg = &cur_arg[12]; - - log_level = log_level_from_arg(arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; - } - } else if (strncmp(cur_arg, "-l", 2) == 0) { - const char *arg = &cur_arg[2]; - - log_level = log_level_from_arg(arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; - } - } else if (strcmp(cur_arg, "-V") == 0 || - strcmp(cur_arg, "--version") == 0) { - print_version(); - goto end; - } else if (strcmp(cur_arg, "-h") == 0 || - strcmp(cur_arg, "--help") == 0) { - print_gen_usage(stdout); - goto end; - } else { - /* - * First unknown argument: is it a known command - * name? - */ - command_argv = &argv[i]; - command_argc = argc - i; - - if (strcmp(cur_arg, "convert") == 0) { - command_type = COMMAND_TYPE_CONVERT; - } else if (strcmp(cur_arg, "list-plugins") == 0) { - command_type = COMMAND_TYPE_LIST_PLUGINS; - } else if (strcmp(cur_arg, "help") == 0) { - command_type = COMMAND_TYPE_HELP; - } else if (strcmp(cur_arg, "query") == 0) { - command_type = COMMAND_TYPE_QUERY; - } else if (strcmp(cur_arg, "run") == 0) { - command_type = COMMAND_TYPE_RUN; - } else { - /* - * Unknown argument, but not a known - * command name: assume the default - * `convert` command. - */ - command_type = COMMAND_TYPE_CONVERT; - command_name = "convert"; - command_argv = &argv[i - 1]; - command_argc = argc - i + 1; - } - break; - } - } - - if (command_type == COMMAND_TYPE_NONE) { - /* - * We only got non-help, non-version general options - * like --verbose and --debug, without any other - * arguments, so we can't do anything useful: print the - * usage and quit. - */ - print_gen_usage(stdout); - goto end; - } - - BT_ASSERT(command_argv); - BT_ASSERT(command_argc >= 0); - - switch (command_type) { - case COMMAND_TYPE_RUN: - config = bt_config_run_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_CONVERT: - config = bt_config_convert_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths, &log_level); - break; - case COMMAND_TYPE_LIST_PLUGINS: - config = bt_config_list_plugins_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_HELP: - config = bt_config_help_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_QUERY: - config = bt_config_query_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - default: - abort(); - } - - if (config) { - if (log_level == 'U') { - log_level = 'W'; - } - - config->log_level = log_level; - config->command_name = command_name; - } - -end: - bt_value_put_ref(initial_plugin_paths); - return config; -} diff --git a/cli/babeltrace2-cfg-cli-args.h b/cli/babeltrace2-cfg-cli-args.h deleted file mode 100644 index 7f1fd003..00000000 --- a/cli/babeltrace2-cfg-cli-args.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_H - -/* - * Copyright 2016-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "babeltrace2-cfg.h" - -struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_H */ diff --git a/cli/babeltrace2-cfg.c b/cli/babeltrace2-cfg.c deleted file mode 100644 index 4704efb3..00000000 --- a/cli/babeltrace2-cfg.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Babeltrace trace converter - parameter parsing - * - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "babeltrace2-cfg.h" - -static -void destroy_gstring(void *data) -{ - g_string_free(data, TRUE); -} - -/* - * Extracts the various paths from the string arg, delimited by ':', - * and appends them to the array value object plugin_paths. - */ -int bt_config_append_plugin_paths( - bt_value *plugin_paths, const char *arg) -{ - int ret = 0; - GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); - size_t i; - - if (!dirs) { - ret = -1; - goto end; - } - - ret = bt_common_append_plugin_path_dirs(arg, dirs); - if (ret) { - ret = -1; - goto end; - } - - for (i = 0; i < dirs->len; i++) { - GString *dir = g_ptr_array_index(dirs, i); - - ret = bt_value_array_append_string_element( - plugin_paths, dir->str); - if (ret != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - } - -end: - g_ptr_array_free(dirs, TRUE); - return ret; -} - -void bt_config_connection_destroy(struct bt_config_connection *connection) -{ - if (!connection) { - return; - } - - if (connection->upstream_comp_name) { - g_string_free(connection->upstream_comp_name, TRUE); - } - - if (connection->downstream_comp_name) { - g_string_free(connection->downstream_comp_name, TRUE); - } - - if (connection->upstream_port_glob) { - g_string_free(connection->upstream_port_glob, TRUE); - } - - if (connection->downstream_port_glob) { - g_string_free(connection->downstream_port_glob, TRUE); - } - - if (connection->arg) { - g_string_free(connection->arg, TRUE); - } - - g_free(connection); -} diff --git a/cli/babeltrace2-cfg.h b/cli/babeltrace2-cfg.h deleted file mode 100644 index 0608e36d..00000000 --- a/cli/babeltrace2-cfg.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_H -#define CLI_BABELTRACE_CFG_H - -/* - * Babeltrace trace converter - CLI tool's configuration - * - * Copyright 2016-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -enum bt_config_command { - BT_CONFIG_COMMAND_RUN, - BT_CONFIG_COMMAND_PRINT_CTF_METADATA, - BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, - BT_CONFIG_COMMAND_LIST_PLUGINS, - BT_CONFIG_COMMAND_HELP, - BT_CONFIG_COMMAND_QUERY, -}; - -struct bt_config_component { - bt_object base; - bt_component_class_type type; - GString *plugin_name; - GString *comp_cls_name; - bt_value *params; - GString *instance_name; -}; - -struct bt_config_connection { - GString *upstream_comp_name; - GString *downstream_comp_name; - GString *upstream_port_glob; - GString *downstream_port_glob; - GString *arg; -}; - -struct bt_config { - bt_object base; - bool debug; - bool verbose; - bt_value *plugin_paths; - bool omit_system_plugin_path; - bool omit_home_plugin_path; - bool command_needs_plugins; - const char *command_name; - char log_level; - enum bt_config_command command; - union { - /* BT_CONFIG_COMMAND_RUN */ - struct { - /* Array of pointers to struct bt_config_component */ - GPtrArray *sources; - - /* Array of pointers to struct bt_config_component */ - GPtrArray *filters; - - /* Array of pointers to struct bt_config_component */ - GPtrArray *sinks; - - /* Array of pointers to struct bt_config_connection */ - GPtrArray *connections; - - /* - * Number of microseconds to sleep when we need - * to retry to run the graph. - */ - uint64_t retry_duration_us; - - /* - * Whether or not to trim the source trace to the - * intersection of its streams. - */ - bool stream_intersection_mode; - } run; - - /* BT_CONFIG_COMMAND_HELP */ - struct { - struct bt_config_component *cfg_component; - } help; - - /* BT_CONFIG_COMMAND_QUERY */ - struct { - GString *object; - struct bt_config_component *cfg_component; - } query; - - /* BT_CONFIG_COMMAND_PRINT_CTF_METADATA */ - struct { - GString *path; - GString *output_path; - } print_ctf_metadata; - - /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ - struct { - GString *url; - GString *output_path; - } print_lttng_live_sessions; - } cmd_data; -}; - -static inline -struct bt_config_component *bt_config_get_component(GPtrArray *array, - size_t index) -{ - struct bt_config_component *comp = g_ptr_array_index(array, index); - - bt_object_get_ref(comp); - return comp; -} - -int bt_config_append_plugin_paths(bt_value *plugin_paths, - const char *arg); - -void bt_config_connection_destroy(struct bt_config_connection *connection); - -#endif /* CLI_BABELTRACE_CFG_H */ diff --git a/cli/babeltrace2-log.c b/cli/babeltrace2-log.c deleted file mode 100644 index 7d6021d9..00000000 --- a/cli/babeltrace2-log.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static -void print_usage(FILE *fp) -{ - fprintf(stderr, "Usage: babeltrace2-log [OPTIONS] OUTPUT-PATH\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -t, --with-timestamps Extract timestamps from lines and map them to\n"); - fprintf(stderr, " a CTF clock class\n"); -} - -static -int parse_params(int argc, char *argv[], char **output_path, - bool *no_extract_ts) -{ - enum { - OPT_WITH_TIMESTAMPS = 1, - OPT_HELP = 2, - }; - static struct poptOption opts[] = { - { "with-timestamps", 't', POPT_ARG_NONE, NULL, OPT_WITH_TIMESTAMPS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { NULL, '\0', 0, NULL, 0, NULL, NULL }, - }; - poptContext pc = NULL; - int opt; - int ret = 0; - const char *leftover; - - *no_extract_ts = true; - pc = poptGetContext(NULL, argc, (const char **) argv, opts, 0); - if (!pc) { - fprintf(stderr, "Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - switch (opt) { - case OPT_HELP: - print_usage(stdout); - goto end; - case OPT_WITH_TIMESTAMPS: - *no_extract_ts = false; - break; - default: - fprintf(stderr, "Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - } - - if (opt < -1) { - fprintf(stderr, "While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (!leftover) { - fprintf(stderr, "Command line error: Missing output path\n"); - print_usage(stderr); - goto error; - } - - *output_path = strdup(leftover); - BT_ASSERT(*output_path); - goto end; - -error: - ret = 1; - -end: - if (pc) { - poptFreeContext(pc); - } - - return ret; -} - -int main(int argc, char *argv[]) -{ - char *output_path = NULL; - bool no_extract_ts; - int retcode; - GError *error = NULL; - gchar *bt_argv[] = { - BT_CLI_PATH, - "run", - "--component", - "dmesg:src.text.dmesg", - "--params", - NULL, /* no-extract-timestamp=? placeholder */ - "--component", - "ctf:sink.ctf.fs", - "--key", - "path", - "--value", - NULL, /* output path placeholder */ - "--params", - "single-trace=yes", - "--connect", - "dmesg:ctf", - NULL, /* sentinel */ - }; - - retcode = parse_params(argc, argv, &output_path, &no_extract_ts); - if (retcode) { - goto end; - } - - if (no_extract_ts) { - bt_argv[5] = "no-extract-timestamp=yes"; - } else { - bt_argv[5] = "no-extract-timestamp=no"; - } - - bt_argv[11] = output_path; - (void) g_spawn_sync(NULL, bt_argv, NULL, - G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, - NULL, NULL, &retcode, &error); - - if (error) { - fprintf(stderr, "Failed to execute \"%s\": %s (%d)\n", - bt_argv[0], error->message, error->code); - } - -end: - free(output_path); - - if (error) { - g_error_free(error); - } - - return retcode; -} diff --git a/cli/babeltrace2.c b/cli/babeltrace2.c deleted file mode 100644 index 91dc1ce5..00000000 --- a/cli/babeltrace2.c +++ /dev/null @@ -1,2950 +0,0 @@ -/* - * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation - * - * Author: Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CLI" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace2-cfg.h" -#include "babeltrace2-cfg-cli-args.h" -#include "babeltrace2-cfg-cli-args-default.h" - -#define ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH "BABELTRACE_CLI_WARN_COMMAND_NAME_DIRECTORY_CLASH" -#define ENV_BABELTRACE_CLI_LOG_LEVEL "BABELTRACE_CLI_LOG_LEVEL" -#define NSEC_PER_SEC 1000000000LL - -/* - * Known environment variable names for the log levels of the project's - * modules. - */ -static const char* log_level_env_var_names[] = { - "BABELTRACE_COMMON_LOG_LEVEL", - "BABELTRACE_COMPAT_LOG_LEVEL", - "BABELTRACE_CTFSER_LOG_LEVEL", - "BABELTRACE_FD_CACHE_LOG_LEVEL", - "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL", - "BABELTRACE_PYTHON_BT2_LOG_LEVEL", - "BABELTRACE_SINK_CTF_FS_LOG_LEVEL", - "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL", - "BABELTRACE_SRC_CTF_FS_LOG_LEVEL", - "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL", - "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL", - NULL, -}; - -/* Application's processing graph (weak) */ -static bt_graph *the_graph; -static bt_query_executor *the_query_executor; -static bool canceled = false; - -GPtrArray *loaded_plugins; - -#ifdef __MINGW32__ - -#include - -static -BOOL WINAPI signal_handler(DWORD signal) { - if (the_graph) { - bt_graph_cancel(the_graph); - } - - canceled = true; - - return TRUE; -} - -static -void set_signal_handler(void) -{ - if (!SetConsoleCtrlHandler(signal_handler, TRUE)) { - BT_LOGE("Failed to set the ctrl+c handler."); - } -} - -#else /* __MINGW32__ */ - -static -void signal_handler(int signum) -{ - if (signum != SIGINT) { - return; - } - - if (the_graph) { - bt_graph_cancel(the_graph); - } - - if (the_query_executor) { - bt_query_executor_cancel(the_query_executor); - } - - canceled = true; -} - -static -void set_signal_handler(void) -{ - struct sigaction new_action, old_action; - - new_action.sa_handler = signal_handler; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGINT, NULL, &old_action); - - if (old_action.sa_handler != SIG_IGN) { - sigaction(SIGINT, &new_action, NULL); - } -} - -#endif /* __MINGW32__ */ - -static -void init_static_data(void) -{ - loaded_plugins = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); -} - -static -void fini_static_data(void) -{ - g_ptr_array_free(loaded_plugins, TRUE); -} - -static -int create_the_query_executor(void) -{ - int ret = 0; - - the_query_executor = bt_query_executor_create(); - if (!the_query_executor) { - BT_LOGE_STR("Cannot create a query executor."); - ret = -1; - } - - return ret; -} - -static -void destroy_the_query_executor(void) -{ - BT_QUERY_EXECUTOR_PUT_REF_AND_RESET(the_query_executor); -} - -static -int query(const bt_component_class *comp_cls, const char *obj, - const bt_value *params, const bt_value **user_result, - const char **fail_reason) -{ - const bt_value *result = NULL; - bt_query_executor_status status; - *fail_reason = "unknown error"; - int ret = 0; - - BT_ASSERT(fail_reason); - BT_ASSERT(user_result); - ret = create_the_query_executor(); - if (ret) { - /* create_the_query_executor() logs errors */ - goto end; - } - - if (canceled) { - BT_LOGI("Canceled by user before executing the query: " - "comp-cls-addr=%p, comp-cls-name=\"%s\", " - "query-obj=\"%s\"", comp_cls, - bt_component_class_get_name(comp_cls), obj); - *fail_reason = "canceled by user"; - goto error; - } - - while (true) { - status = bt_query_executor_query(the_query_executor, - comp_cls, obj, params, &result); - switch (status) { - case BT_QUERY_EXECUTOR_STATUS_OK: - goto ok; - case BT_QUERY_EXECUTOR_STATUS_AGAIN: - { - const uint64_t sleep_time_us = 100000; - - /* Wait 100 ms and retry */ - BT_LOGV("Got BT_QUERY_EXECUTOR_STATUS_AGAIN: sleeping: " - "time-us=%" PRIu64, sleep_time_us); - - if (usleep(sleep_time_us)) { - if (bt_query_executor_is_canceled(the_query_executor)) { - BT_LOGI("Query was canceled by user: " - "comp-cls-addr=%p, comp-cls-name=\"%s\", " - "query-obj=\"%s\"", comp_cls, - bt_component_class_get_name(comp_cls), - obj); - *fail_reason = "canceled by user"; - goto error; - } - } - - continue; - } - case BT_QUERY_EXECUTOR_STATUS_CANCELED: - *fail_reason = "canceled by user"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_ERROR: - goto error; - case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: - *fail_reason = "invalid or unknown query object"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: - *fail_reason = "invalid query parameters"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: - *fail_reason = "unsupported action"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_NOMEM: - *fail_reason = "not enough memory"; - goto error; - default: - BT_LOGF("Unknown query status: status=%d", status); - abort(); - } - } - -ok: - *user_result = result; - result = NULL; - goto end; - -error: - ret = -1; - -end: - destroy_the_query_executor(); - bt_value_put_ref(result); - return ret; -} - -static -const bt_plugin *find_plugin(const char *name) -{ - int i; - const bt_plugin *plugin = NULL; - - BT_ASSERT(name); - BT_LOGD("Finding plugin: name=\"%s\"", name); - - for (i = 0; i < loaded_plugins->len; i++) { - plugin = g_ptr_array_index(loaded_plugins, i); - - if (strcmp(name, bt_plugin_get_name(plugin)) == 0) { - break; - } - - plugin = NULL; - } - - if (BT_LOG_ON_DEBUG) { - if (plugin) { - BT_LOGD("Found plugin: plugin-addr=%p", plugin); - } else { - BT_LOGD("Cannot find plugin."); - } - } - - bt_plugin_get_ref(plugin); - return plugin; -} - -typedef const void *(*plugin_borrow_comp_cls_func_t)( - const bt_plugin *, const char *); - -static -const void *find_component_class_from_plugin(const char *plugin_name, - const char *comp_class_name, - plugin_borrow_comp_cls_func_t plugin_borrow_comp_cls_func) -{ - const void *comp_class = NULL; - const bt_plugin *plugin; - - BT_LOGD("Finding component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\"", plugin_name, comp_class_name); - - plugin = find_plugin(plugin_name); - if (!plugin) { - goto end; - } - - comp_class = plugin_borrow_comp_cls_func(plugin, comp_class_name); - bt_object_get_ref(comp_class); - BT_PLUGIN_PUT_REF_AND_RESET(plugin); - -end: - if (BT_LOG_ON_DEBUG) { - if (comp_class) { - BT_LOGD("Found component class: comp-cls-addr=%p", - comp_class); - } else { - BT_LOGD("Cannot find source component class."); - } - } - - return comp_class; -} - -static -const bt_component_class_source *find_source_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin( - plugin_name, comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_source_component_class_by_name_const); -} - -static -const bt_component_class_filter *find_filter_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin( - plugin_name, comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_filter_component_class_by_name_const); -} - -static -const bt_component_class_sink *find_sink_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin(plugin_name, - comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_sink_component_class_by_name_const); -} - -static -const bt_component_class *find_component_class(const char *plugin_name, - const char *comp_class_name, - bt_component_class_type comp_class_type) -{ - const bt_component_class *comp_cls = NULL; - - switch (comp_class_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - comp_cls = bt_component_class_source_as_component_class_const(find_source_component_class(plugin_name, comp_class_name)); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - comp_cls = bt_component_class_filter_as_component_class_const(find_filter_component_class(plugin_name, comp_class_name)); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - comp_cls = bt_component_class_sink_as_component_class_const(find_sink_component_class(plugin_name, comp_class_name)); - break; - default: - abort(); - } - - return comp_cls; -} - -static -void print_indent(FILE *fp, size_t indent) -{ - size_t i; - - for (i = 0; i < indent; i++) { - fprintf(fp, " "); - } -} - -static -const char *component_type_str(bt_component_class_type type) -{ - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - return "source"; - case BT_COMPONENT_CLASS_TYPE_SINK: - return "sink"; - case BT_COMPONENT_CLASS_TYPE_FILTER: - return "filter"; - default: - return "(unknown)"; - } -} - -static -void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, - const char *comp_cls_name, bt_component_class_type type) -{ - GString *shell_plugin_name = NULL; - GString *shell_comp_cls_name = NULL; - - shell_plugin_name = bt_common_shell_quote(plugin_name, false); - if (!shell_plugin_name) { - goto end; - } - - shell_comp_cls_name = bt_common_shell_quote(comp_cls_name, false); - if (!shell_comp_cls_name) { - goto end; - } - - fprintf(fh, "'%s%s%s%s.%s%s%s.%s%s%s'", - bt_common_color_bold(), - bt_common_color_fg_cyan(), - component_type_str(type), - bt_common_color_fg_default(), - bt_common_color_fg_blue(), - shell_plugin_name->str, - bt_common_color_fg_default(), - bt_common_color_fg_yellow(), - shell_comp_cls_name->str, - bt_common_color_reset()); - -end: - if (shell_plugin_name) { - g_string_free(shell_plugin_name, TRUE); - } - - if (shell_comp_cls_name) { - g_string_free(shell_comp_cls_name, TRUE); - } -} - -static -void print_value(FILE *, const bt_value *, size_t); - -static -void print_value_rec(FILE *, const bt_value *, size_t); - -struct print_map_value_data { - size_t indent; - FILE *fp; -}; - -static -bt_bool print_map_value(const char *key, const bt_value *object, - void *data) -{ - struct print_map_value_data *print_map_value_data = data; - - print_indent(print_map_value_data->fp, print_map_value_data->indent); - fprintf(print_map_value_data->fp, "%s: ", key); - BT_ASSERT(object); - - if (bt_value_is_array(object) && - bt_value_array_is_empty(object)) { - fprintf(print_map_value_data->fp, "[ ]\n"); - return true; - } - - if (bt_value_is_map(object) && - bt_value_map_is_empty(object)) { - fprintf(print_map_value_data->fp, "{ }\n"); - return true; - } - - if (bt_value_is_array(object) || - bt_value_is_map(object)) { - fprintf(print_map_value_data->fp, "\n"); - } - - print_value_rec(print_map_value_data->fp, object, - print_map_value_data->indent + 2); - return BT_TRUE; -} - -static -void print_value_rec(FILE *fp, const bt_value *value, size_t indent) -{ - bt_bool bool_val; - int64_t int_val; - uint64_t uint_val; - double dbl_val; - const char *str_val; - int size; - int i; - - if (!value) { - return; - } - - switch (bt_value_get_type(value)) { - case BT_VALUE_TYPE_NULL: - fprintf(fp, "%snull%s\n", bt_common_color_bold(), - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_BOOL: - bool_val = bt_value_bool_get(value); - fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), - bt_common_color_fg_cyan(), bool_val ? "yes" : "no", - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_UNSIGNED_INTEGER: - uint_val = bt_value_unsigned_integer_get(value); - fprintf(fp, "%s%s%" PRIu64 "%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), uint_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_SIGNED_INTEGER: - int_val = bt_value_signed_integer_get(value); - fprintf(fp, "%s%s%" PRId64 "%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), int_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_REAL: - dbl_val = bt_value_real_get(value); - fprintf(fp, "%s%s%lf%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), dbl_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_STRING: - str_val = bt_value_string_get(value); - fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), - bt_common_color_fg_green(), str_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_ARRAY: - size = bt_value_array_get_size(value); - if (size < 0) { - goto error; - } - - if (size == 0) { - print_indent(fp, indent); - fprintf(fp, "[ ]\n"); - break; - } - - for (i = 0; i < size; i++) { - const bt_value *element = - bt_value_array_borrow_element_by_index_const( - value, i); - - if (!element) { - goto error; - } - print_indent(fp, indent); - fprintf(fp, "- "); - - if (bt_value_is_array(element) && - bt_value_array_is_empty(element)) { - fprintf(fp, "[ ]\n"); - continue; - } - - if (bt_value_is_map(element) && - bt_value_map_is_empty(element)) { - fprintf(fp, "{ }\n"); - continue; - } - - if (bt_value_is_array(element) || - bt_value_is_map(element)) { - fprintf(fp, "\n"); - } - - print_value_rec(fp, element, indent + 2); - } - break; - case BT_VALUE_TYPE_MAP: - { - struct print_map_value_data data = { - .indent = indent, - .fp = fp, - }; - - if (bt_value_map_is_empty(value)) { - print_indent(fp, indent); - fprintf(fp, "{ }\n"); - break; - } - - bt_value_map_foreach_entry_const(value, print_map_value, &data); - break; - } - default: - abort(); - } - return; - -error: - BT_LOGE("Error printing value of type %s.", - bt_common_value_type_string(bt_value_get_type(value))); -} - -static -void print_value(FILE *fp, const bt_value *value, size_t indent) -{ - if (!bt_value_is_array(value) && !bt_value_is_map(value)) { - print_indent(fp, indent); - } - - print_value_rec(fp, value, indent); -} - -static -void print_bt_config_component(struct bt_config_component *bt_config_component) -{ - fprintf(stderr, " "); - print_plugin_comp_cls_opt(stderr, bt_config_component->plugin_name->str, - bt_config_component->comp_cls_name->str, - bt_config_component->type); - fprintf(stderr, ":\n"); - - if (bt_config_component->instance_name->len > 0) { - fprintf(stderr, " Name: %s\n", - bt_config_component->instance_name->str); - } - - fprintf(stderr, " Parameters:\n"); - print_value(stderr, bt_config_component->params, 8); -} - -static -void print_bt_config_components(GPtrArray *array) -{ - size_t i; - - for (i = 0; i < array->len; i++) { - struct bt_config_component *cfg_component = - bt_config_get_component(array, i); - print_bt_config_component(cfg_component); - BT_OBJECT_PUT_REF_AND_RESET(cfg_component); - } -} - -static -void print_plugin_paths(const bt_value *plugin_paths) -{ - fprintf(stderr, " Plugin paths:\n"); - print_value(stderr, plugin_paths, 4); -} - -static -void print_cfg_run(struct bt_config *cfg) -{ - size_t i; - - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Source component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sources); - - if (cfg->cmd_data.run.filters->len > 0) { - fprintf(stderr, " Filter component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.filters); - } - - fprintf(stderr, " Sink component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sinks); - fprintf(stderr, " Connections:\n"); - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *cfg_connection = - g_ptr_array_index(cfg->cmd_data.run.connections, - i); - - fprintf(stderr, " %s%s%s -> %s%s%s\n", - cfg_connection->upstream_comp_name->str, - cfg_connection->upstream_port_glob->len > 0 ? "." : "", - cfg_connection->upstream_port_glob->str, - cfg_connection->downstream_comp_name->str, - cfg_connection->downstream_port_glob->len > 0 ? "." : "", - cfg_connection->downstream_port_glob->str); - } -} - -static -void print_cfg_list_plugins(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); -} - -static -void print_cfg_help(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); -} - -static -void print_cfg_print_ctf_metadata(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Path: %s\n", - cfg->cmd_data.print_ctf_metadata.path->str); -} - -static -void print_cfg_print_lttng_live_sessions(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " URL: %s\n", - cfg->cmd_data.print_lttng_live_sessions.url->str); -} - -static -void print_cfg_query(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Object: `%s`\n", cfg->cmd_data.query.object->str); - fprintf(stderr, " Component class:\n"); - print_bt_config_component(cfg->cmd_data.query.cfg_component); -} - -static -void print_cfg(struct bt_config *cfg) -{ - if (!BT_LOG_ON_INFO) { - return; - } - - BT_LOGI_STR("Configuration:"); - fprintf(stderr, " Debug mode: %s\n", cfg->debug ? "yes" : "no"); - fprintf(stderr, " Verbose mode: %s\n", cfg->verbose ? "yes" : "no"); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - print_cfg_run(cfg); - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - print_cfg_list_plugins(cfg); - break; - case BT_CONFIG_COMMAND_HELP: - print_cfg_help(cfg); - break; - case BT_CONFIG_COMMAND_QUERY: - print_cfg_query(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - print_cfg_print_ctf_metadata(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - print_cfg_print_lttng_live_sessions(cfg); - break; - default: - abort(); - } -} - -static -void add_to_loaded_plugins(const bt_plugin_set *plugin_set) -{ - int64_t i; - int64_t count; - - count = bt_plugin_set_get_plugin_count(plugin_set); - BT_ASSERT(count >= 0); - - for (i = 0; i < count; i++) { - const bt_plugin *plugin = - bt_plugin_set_borrow_plugin_by_index_const(plugin_set, i); - const bt_plugin *loaded_plugin = - find_plugin(bt_plugin_get_name(plugin)); - - BT_ASSERT(plugin); - - if (loaded_plugin) { - BT_LOGI("Not using plugin: another one already exists with the same name: " - "plugin-name=\"%s\", plugin-path=\"%s\", " - "existing-plugin-path=\"%s\"", - bt_plugin_get_name(plugin), - bt_plugin_get_path(plugin), - bt_plugin_get_path(loaded_plugin)); - bt_plugin_put_ref(loaded_plugin); - } else { - /* Add to global array. */ - BT_LOGD("Adding plugin to loaded plugins: plugin-path=\"%s\"", - bt_plugin_get_name(plugin)); - bt_plugin_get_ref(plugin); - g_ptr_array_add(loaded_plugins, (void *) plugin); - } - } -} - -static -int load_dynamic_plugins(const bt_value *plugin_paths) -{ - int nr_paths, i, ret = 0; - - nr_paths = bt_value_array_get_size(plugin_paths); - if (nr_paths < 0) { - BT_LOGE_STR("Cannot load dynamic plugins: no plugin path."); - ret = -1; - goto end; - } - - BT_LOGI("Loading dynamic plugins."); - - for (i = 0; i < nr_paths; i++) { - const bt_value *plugin_path_value = NULL; - const char *plugin_path; - const bt_plugin_set *plugin_set; - - plugin_path_value = - bt_value_array_borrow_element_by_index_const( - plugin_paths, i); - plugin_path = bt_value_string_get(plugin_path_value); - - /* - * Skip this if the directory does not exist because - * bt_plugin_find_all_from_dir() expects an existing - * directory. - */ - if (!g_file_test(plugin_path, G_FILE_TEST_IS_DIR)) { - BT_LOGV("Skipping nonexistent directory path: " - "path=\"%s\"", plugin_path); - continue; - } - - plugin_set = bt_plugin_find_all_from_dir(plugin_path, false); - if (!plugin_set) { - BT_LOGD("Unable to load dynamic plugins: path=\"%s\"", - plugin_path); - continue; - } - - add_to_loaded_plugins(plugin_set); - bt_plugin_set_put_ref(plugin_set); - } -end: - return ret; -} - -static -int load_static_plugins(void) -{ - int ret = 0; - const bt_plugin_set *plugin_set; - - BT_LOGI("Loading static plugins."); - plugin_set = bt_plugin_find_all_from_static(); - if (!plugin_set) { - BT_LOGE("Unable to load static plugins."); - ret = -1; - goto end; - } - - add_to_loaded_plugins(plugin_set); - bt_plugin_set_put_ref(plugin_set); -end: - return ret; -} - -static -int load_all_plugins(const bt_value *plugin_paths) -{ - int ret = 0; - - if (load_dynamic_plugins(plugin_paths)) { - ret = -1; - goto end; - } - - if (load_static_plugins()) { - ret = -1; - goto end; - } - - BT_LOGI("Loaded all plugins: count=%u", loaded_plugins->len); - -end: - return ret; -} - -static -void print_plugin_info(const bt_plugin *plugin) -{ - unsigned int major, minor, patch; - const char *extra; - bt_property_availability version_avail; - const char *plugin_name; - const char *path; - const char *author; - const char *license; - const char *plugin_description; - - plugin_name = bt_plugin_get_name(plugin); - path = bt_plugin_get_path(plugin); - author = bt_plugin_get_author(plugin); - license = bt_plugin_get_license(plugin); - plugin_description = bt_plugin_get_description(plugin); - version_avail = bt_plugin_get_version(plugin, &major, &minor, - &patch, &extra); - printf("%s%s%s%s:\n", bt_common_color_bold(), - bt_common_color_fg_blue(), plugin_name, - bt_common_color_reset()); - if (path) { - printf(" %sPath%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), path); - } else { - puts(" Built-in"); - } - - if (version_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { - printf(" %sVersion%s: %u.%u.%u", - bt_common_color_bold(), bt_common_color_reset(), - major, minor, patch); - - if (extra) { - printf("%s", extra); - } - - printf("\n"); - } - - printf(" %sDescription%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - plugin_description ? plugin_description : "(None)"); - printf(" %sAuthor%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), author ? author : "(Unknown)"); - printf(" %sLicense%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - license ? license : "(Unknown)"); -} - -static -int cmd_query(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - const char *fail_reason = NULL; - - comp_cls = find_component_class( - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - ret = query(comp_cls, cfg->cmd_data.query.object->str, - cfg->cmd_data.query.cfg_component->params, - &results, &fail_reason); - if (ret) { - goto failed; - } - - print_value(stdout, results, 0); - goto end; - -failed: - BT_LOGE("Failed to query component class: %s: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d " - "object=\"%s\"", fail_reason, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type, - cfg->cmd_data.query.object->str); - fprintf(stderr, "%s%sFailed to query info to %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "%s%s with object `%s`: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - cfg->cmd_data.query.object->str, - fail_reason, - bt_common_color_reset()); - ret = -1; - -end: - bt_component_class_put_ref(comp_cls); - bt_value_put_ref(results); - return ret; -} - -static -void print_component_class_help(const char *plugin_name, - const bt_component_class *comp_cls) -{ - const char *comp_class_name = - bt_component_class_get_name(comp_cls); - const char *comp_class_description = - bt_component_class_get_description(comp_cls); - const char *comp_class_help = - bt_component_class_get_help(comp_cls); - bt_component_class_type type = - bt_component_class_get_type(comp_cls); - - print_plugin_comp_cls_opt(stdout, plugin_name, comp_class_name, type); - printf("\n"); - printf(" %sDescription%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - comp_class_description ? comp_class_description : "(None)"); - - if (comp_class_help) { - printf("\n%s\n", comp_class_help); - } -} - -static -int cmd_help(struct bt_config *cfg) -{ - int ret = 0; - const bt_plugin *plugin = NULL; - const bt_component_class *needed_comp_cls = NULL; - - plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); - if (!plugin) { - BT_LOGE("Cannot find plugin: plugin-name=\"%s\"", - cfg->cmd_data.help.cfg_component->plugin_name->str); - fprintf(stderr, "%s%sCannot find plugin %s%s%s\n", - bt_common_color_bold(), bt_common_color_fg_red(), - bt_common_color_fg_blue(), - cfg->cmd_data.help.cfg_component->plugin_name->str, - bt_common_color_reset()); - ret = -1; - goto end; - } - - print_plugin_info(plugin); - printf(" %sSource component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_source_component_class_count(plugin)); - printf(" %sFilter component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_filter_component_class_count(plugin)); - printf(" %sSink component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_sink_component_class_count(plugin)); - - if (strlen(cfg->cmd_data.help.cfg_component->comp_cls_name->str) == 0) { - /* Plugin help only */ - goto end; - } - - needed_comp_cls = find_component_class( - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - if (!needed_comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - fprintf(stderr, "\n%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - printf("\n"); - print_component_class_help( - cfg->cmd_data.help.cfg_component->plugin_name->str, - needed_comp_cls); - -end: - bt_component_class_put_ref(needed_comp_cls); - bt_plugin_put_ref(plugin); - return ret; -} - -typedef void *(* plugin_borrow_comp_cls_by_index_func_t)(const bt_plugin *, - uint64_t); -typedef const bt_component_class *(* spec_comp_cls_borrow_comp_cls_func_t)( - void *); - -void cmd_list_plugins_print_component_classes(const bt_plugin *plugin, - const char *cc_type_name, uint64_t count, - plugin_borrow_comp_cls_by_index_func_t borrow_comp_cls_by_index_func, - spec_comp_cls_borrow_comp_cls_func_t spec_comp_cls_borrow_comp_cls_func) -{ - uint64_t i; - - if (count == 0) { - printf(" %s%s component classes%s: (none)\n", - bt_common_color_bold(), - cc_type_name, - bt_common_color_reset()); - goto end; - } else { - printf(" %s%s component classes%s:\n", - bt_common_color_bold(), - cc_type_name, - bt_common_color_reset()); - } - - for (i = 0; i < count; i++) { - const bt_component_class *comp_class = - spec_comp_cls_borrow_comp_cls_func( - borrow_comp_cls_by_index_func(plugin, i)); - const char *comp_class_name = - bt_component_class_get_name(comp_class); - const char *comp_class_description = - bt_component_class_get_description(comp_class); - bt_component_class_type type = - bt_component_class_get_type(comp_class); - - printf(" "); - print_plugin_comp_cls_opt(stdout, - bt_plugin_get_name(plugin), comp_class_name, - type); - - if (comp_class_description) { - printf(": %s", comp_class_description); - } - - printf("\n"); - } - -end: - return; -} - -static -int cmd_list_plugins(struct bt_config *cfg) -{ - int ret = 0; - int plugins_count, component_classes_count = 0, i; - - printf("From the following plugin paths:\n\n"); - print_value(stdout, cfg->plugin_paths, 2); - printf("\n"); - plugins_count = loaded_plugins->len; - if (plugins_count == 0) { - printf("No plugins found.\n"); - goto end; - } - - for (i = 0; i < plugins_count; i++) { - const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - component_classes_count += - bt_plugin_get_source_component_class_count(plugin) + - bt_plugin_get_filter_component_class_count(plugin) + - bt_plugin_get_sink_component_class_count(plugin); - } - - printf("Found %s%d%s component classes in %s%d%s plugins.\n", - bt_common_color_bold(), - component_classes_count, - bt_common_color_reset(), - bt_common_color_bold(), - plugins_count, - bt_common_color_reset()); - - for (i = 0; i < plugins_count; i++) { - const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - printf("\n"); - print_plugin_info(plugin); - cmd_list_plugins_print_component_classes(plugin, "Source", - bt_plugin_get_source_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_source_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_source_as_component_class); - cmd_list_plugins_print_component_classes(plugin, "Filter", - bt_plugin_get_filter_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_filter_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_filter_as_component_class); - cmd_list_plugins_print_component_classes(plugin, "Sink", - bt_plugin_get_sink_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_sink_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_sink_as_component_class); - } - -end: - return ret; -} - -static -int cmd_print_lttng_live_sessions(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - bt_value *params = NULL; - const bt_value *map = NULL; - const bt_value *v = NULL; - static const char * const plugin_name = "ctf"; - static const char * const comp_cls_name = "lttng-live"; - static const bt_component_class_type comp_cls_type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - int64_t array_size, i; - const char *fail_reason = NULL; - FILE *out_stream = stdout; - - BT_ASSERT(cfg->cmd_data.print_lttng_live_sessions.url); - comp_cls = find_component_class(plugin_name, comp_cls_name, - comp_cls_type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - plugin_name, comp_cls_name, - BT_COMPONENT_CLASS_TYPE_SOURCE); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, plugin_name, - comp_cls_name, comp_cls_type); - fprintf(stderr, "\n"); - goto error; - } - - params = bt_value_map_create(); - if (!params) { - goto error; - } - - ret = bt_value_map_insert_string_entry(params, "url", - cfg->cmd_data.print_lttng_live_sessions.url->str); - if (ret) { - goto error; - } - - ret = query(comp_cls, "sessions", params, - &results, &fail_reason); - if (ret) { - goto failed; - } - - BT_ASSERT(results); - - if (!bt_value_is_array(results)) { - BT_LOGE_STR("Expecting an array for sessions query."); - fprintf(stderr, "%s%sUnexpected type returned by session query%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - goto error; - } - - if (cfg->cmd_data.print_lttng_live_sessions.output_path->len > 0) { - out_stream = - fopen(cfg->cmd_data.print_lttng_live_sessions.output_path->str, - "w"); - if (!out_stream) { - ret = -1; - BT_LOGE_ERRNO("Cannot open file for writing", - ": path=\"%s\"", - cfg->cmd_data.print_lttng_live_sessions.output_path->str); - goto end; - } - } - - array_size = bt_value_array_get_size(results); - for (i = 0; i < array_size; i++) { - const char *url_text; - int64_t timer_us, streams, clients; - - map = bt_value_array_borrow_element_by_index_const(results, i); - if (!map) { - BT_LOGE_STR("Unexpected empty array entry."); - goto error; - } - if (!bt_value_is_map(map)) { - BT_LOGE_STR("Unexpected entry type."); - goto error; - } - - v = bt_value_map_borrow_entry_value_const(map, "url"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"url\" entry."); - goto error; - } - url_text = bt_value_string_get(v); - fprintf(out_stream, "%s", url_text); - v = bt_value_map_borrow_entry_value_const(map, "timer-us"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"timer-us\" entry."); - goto error; - } - timer_us = bt_value_signed_integer_get(v); - fprintf(out_stream, " (timer = %" PRIu64 ", ", timer_us); - v = bt_value_map_borrow_entry_value_const(map, "stream-count"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"stream-count\" entry."); - goto error; - } - streams = bt_value_signed_integer_get(v); - fprintf(out_stream, "%" PRIu64 " stream(s), ", streams); - v = bt_value_map_borrow_entry_value_const(map, "client-count"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"client-count\" entry."); - goto error; - } - clients = bt_value_signed_integer_get(v); - fprintf(out_stream, "%" PRIu64 " client(s) connected)\n", clients); - } - - goto end; - -failed: - BT_LOGE("Failed to query for sessions: %s", fail_reason); - fprintf(stderr, "%s%sFailed to request sessions: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - fail_reason, - bt_common_color_reset()); - -error: - ret = -1; - -end: - bt_value_put_ref(results); - bt_value_put_ref(params); - bt_component_class_put_ref(comp_cls); - - if (out_stream && out_stream != stdout) { - int fclose_ret = fclose(out_stream); - - if (fclose_ret) { - BT_LOGE_ERRNO("Cannot close file stream", - ": path=\"%s\"", - cfg->cmd_data.print_lttng_live_sessions.output_path->str); - } - } - - return ret; -} - -static -int cmd_print_ctf_metadata(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - bt_value *params = NULL; - const bt_value *metadata_text_value = NULL; - const char *metadata_text = NULL; - static const char * const plugin_name = "ctf"; - static const char * const comp_cls_name = "fs"; - static const bt_component_class_type comp_cls_type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - const char *fail_reason = NULL; - FILE *out_stream = stdout; - - BT_ASSERT(cfg->cmd_data.print_ctf_metadata.path); - comp_cls = find_component_class(plugin_name, comp_cls_name, - comp_cls_type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - plugin_name, comp_cls_name, - BT_COMPONENT_CLASS_TYPE_SOURCE); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, plugin_name, - comp_cls_name, comp_cls_type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - params = bt_value_map_create(); - if (!params) { - ret = -1; - goto end; - } - - ret = bt_value_map_insert_string_entry(params, "path", - cfg->cmd_data.print_ctf_metadata.path->str); - if (ret) { - ret = -1; - goto end; - } - - ret = query(comp_cls, "metadata-info", - params, &results, &fail_reason); - if (ret) { - goto failed; - } - - metadata_text_value = bt_value_map_borrow_entry_value_const(results, - "text"); - if (!metadata_text_value) { - BT_LOGE_STR("Cannot find `text` string value in the resulting metadata info object."); - ret = -1; - goto end; - } - - metadata_text = bt_value_string_get(metadata_text_value); - - if (cfg->cmd_data.print_ctf_metadata.output_path->len > 0) { - out_stream = - fopen(cfg->cmd_data.print_ctf_metadata.output_path->str, - "w"); - if (!out_stream) { - ret = -1; - BT_LOGE_ERRNO("Cannot open file for writing", - ": path=\"%s\"", - cfg->cmd_data.print_ctf_metadata.output_path->str); - goto end; - } - } - - ret = fprintf(out_stream, "%s\n", metadata_text); - if (ret < 0) { - BT_LOGE("Cannot write whole metadata text to output stream: " - "ret=%d", ret); - goto end; - } - - ret = 0; - - goto end; - -failed: - ret = -1; - BT_LOGE("Failed to query for metadata info: %s", fail_reason); - fprintf(stderr, "%s%sFailed to request metadata info: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - fail_reason, - bt_common_color_reset()); - -end: - bt_value_put_ref(results); - bt_value_put_ref(params); - bt_component_class_put_ref(comp_cls); - - if (out_stream && out_stream != stdout) { - int fclose_ret = fclose(out_stream); - - if (fclose_ret) { - BT_LOGE_ERRNO("Cannot close file stream", - ": path=\"%s\"", - cfg->cmd_data.print_ctf_metadata.output_path->str); - } - } - - return ret; -} - -struct port_id { - char *instance_name; - char *port_name; -}; - -struct trace_range { - uint64_t intersection_range_begin_ns; - uint64_t intersection_range_end_ns; -}; - -static -guint port_id_hash(gconstpointer v) -{ - const struct port_id *id = v; - - BT_ASSERT(id->instance_name); - BT_ASSERT(id->port_name); - - return g_str_hash(id->instance_name) ^ g_str_hash(id->port_name); -} - -static -gboolean port_id_equal(gconstpointer v1, gconstpointer v2) -{ - const struct port_id *id1 = v1; - const struct port_id *id2 = v2; - - return !strcmp(id1->instance_name, id2->instance_name) && - !strcmp(id1->port_name, id2->port_name); -} - -static -void port_id_destroy(gpointer data) -{ - struct port_id *id = data; - - free(id->instance_name); - free(id->port_name); - free(id); -} - -static -void trace_range_destroy(gpointer data) -{ - free(data); -} - -struct cmd_run_ctx { - /* Owned by this */ - GHashTable *src_components; - - /* Owned by this */ - GHashTable *flt_components; - - /* Owned by this */ - GHashTable *sink_components; - - /* Owned by this */ - bt_graph *graph; - - /* Weak */ - struct bt_config *cfg; - - bool connect_ports; - - bool stream_intersection_mode; - - /* - * Association of struct port_id -> struct trace_range. - */ - GHashTable *intersections; -}; - -/* Returns a timestamp of the form "(-)s.ns" */ -static -char *s_from_ns(int64_t ns) -{ - int ret; - char *s_ret = NULL; - bool is_negative; - int64_t ts_sec_abs, ts_nsec_abs; - int64_t ts_sec = ns / NSEC_PER_SEC; - int64_t ts_nsec = ns % NSEC_PER_SEC; - - if (ts_sec >= 0 && ts_nsec >= 0) { - is_negative = false; - ts_sec_abs = ts_sec; - ts_nsec_abs = ts_nsec; - } else if (ts_sec > 0 && ts_nsec < 0) { - is_negative = false; - ts_sec_abs = ts_sec - 1; - ts_nsec_abs = NSEC_PER_SEC + ts_nsec; - } else if (ts_sec == 0 && ts_nsec < 0) { - is_negative = true; - ts_sec_abs = ts_sec; - ts_nsec_abs = -ts_nsec; - } else if (ts_sec < 0 && ts_nsec > 0) { - is_negative = true; - ts_sec_abs = -(ts_sec + 1); - ts_nsec_abs = NSEC_PER_SEC - ts_nsec; - } else if (ts_sec < 0 && ts_nsec == 0) { - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = ts_nsec; - } else { /* (ts_sec < 0 && ts_nsec < 0) */ - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = -ts_nsec; - } - - ret = asprintf(&s_ret, "%s%" PRId64 ".%09" PRId64, - is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); - if (ret < 0) { - s_ret = NULL; - } - return s_ret; -} - -static -int cmd_run_ctx_connect_upstream_port_to_downstream_component( - struct cmd_run_ctx *ctx, - const bt_component *upstream_comp, - const bt_port_output *out_upstream_port, - struct bt_config_connection *cfg_conn) -{ - typedef uint64_t (*input_port_count_func_t)(void *); - typedef const bt_port_input *(*borrow_input_port_by_index_func_t)( - const void *, uint64_t); - const bt_port *upstream_port = - bt_port_output_as_port_const(out_upstream_port); - - int ret = 0; - GQuark downstreamp_comp_name_quark; - void *downstream_comp; - uint64_t downstream_port_count; - uint64_t i; - input_port_count_func_t port_count_fn; - borrow_input_port_by_index_func_t port_by_index_fn; - bt_graph_status status = BT_GRAPH_STATUS_ERROR; - bool insert_trimmer = false; - bt_value *trimmer_params = NULL; - char *intersection_begin = NULL; - char *intersection_end = NULL; - const bt_component_filter *trimmer = NULL; - const bt_component_class_filter *trimmer_class = NULL; - const bt_port_input *trimmer_input = NULL; - const bt_port_output *trimmer_output = NULL; - - if (ctx->intersections && - bt_component_get_class_type(upstream_comp) == - BT_COMPONENT_CLASS_TYPE_SOURCE) { - struct trace_range *range; - struct port_id port_id = { - .instance_name = (char *) bt_component_get_name(upstream_comp), - .port_name = (char *) bt_port_get_name(upstream_port) - }; - - if (!port_id.instance_name || !port_id.port_name) { - goto error; - } - - range = (struct trace_range *) g_hash_table_lookup( - ctx->intersections, &port_id); - if (range) { - bt_value_status status; - - intersection_begin = s_from_ns( - range->intersection_range_begin_ns); - intersection_end = s_from_ns( - range->intersection_range_end_ns); - if (!intersection_begin || !intersection_end) { - BT_LOGE_STR("Cannot create trimmer argument timestamp string."); - goto error; - } - - insert_trimmer = true; - trimmer_params = bt_value_map_create(); - if (!trimmer_params) { - goto error; - } - - status = bt_value_map_insert_string_entry( - trimmer_params, "begin", intersection_begin); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - status = bt_value_map_insert_string_entry( - trimmer_params, - "end", intersection_end); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - } - - trimmer_class = find_filter_component_class("utils", "trimmer"); - if (!trimmer_class) { - goto error; - } - } - - BT_LOGI("Connecting upstream port to the next available downstream port: " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-name=\"%s\", conn-arg=\"%s\"", - upstream_port, bt_port_get_name(upstream_port), - cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - downstreamp_comp_name_quark = g_quark_from_string( - cfg_conn->downstream_comp_name->str); - BT_ASSERT(downstreamp_comp_name_quark > 0); - downstream_comp = g_hash_table_lookup(ctx->flt_components, - GUINT_TO_POINTER(downstreamp_comp_name_quark)); - port_count_fn = (input_port_count_func_t) - bt_component_filter_get_input_port_count; - port_by_index_fn = (borrow_input_port_by_index_func_t) - bt_component_filter_borrow_input_port_by_index_const; - - if (!downstream_comp) { - downstream_comp = g_hash_table_lookup(ctx->sink_components, - GUINT_TO_POINTER(downstreamp_comp_name_quark)); - port_count_fn = (input_port_count_func_t) - bt_component_sink_get_input_port_count; - port_by_index_fn = (borrow_input_port_by_index_func_t) - bt_component_sink_borrow_input_port_by_index_const; - } - - if (!downstream_comp) { - BT_LOGE("Cannot find downstream component: comp-name=\"%s\", " - "conn-arg=\"%s\"", cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - fprintf(stderr, "Cannot create connection: cannot find downstream component: %s\n", - cfg_conn->arg->str); - goto error; - } - - downstream_port_count = port_count_fn(downstream_comp); - - for (i = 0; i < downstream_port_count; i++) { - const bt_port_input *in_downstream_port = - port_by_index_fn(downstream_comp, i); - const bt_port *downstream_port = - bt_port_input_as_port_const(in_downstream_port); - const char *upstream_port_name; - const char *downstream_port_name; - - BT_ASSERT(downstream_port); - - /* Skip port if it's already connected. */ - if (bt_port_is_connected(downstream_port)) { - BT_LOGD("Skipping downstream port: already connected: " - "port-addr=%p, port-name=\"%s\"", - downstream_port, - bt_port_get_name(downstream_port)); - continue; - } - - downstream_port_name = bt_port_get_name(downstream_port); - BT_ASSERT(downstream_port_name); - upstream_port_name = bt_port_get_name(upstream_port); - BT_ASSERT(upstream_port_name); - - if (!bt_common_star_glob_match( - cfg_conn->downstream_port_glob->str, SIZE_MAX, - downstream_port_name, SIZE_MAX)) { - continue; - } - - if (insert_trimmer) { - /* - * In order to insert the trimmer between the - * two components that were being connected, we - * create a connection configuration entry which - * describes a connection from the trimmer's - * output to the original input that was being - * connected. - * - * Hence, the creation of the trimmer will cause - * the graph "new port" listener to establish - * all downstream connections as its output port - * is connected. We will then establish the - * connection between the original upstream - * source and the trimmer. - */ - char *trimmer_name = NULL; - bt_graph_status graph_status; - - ret = asprintf(&trimmer_name, - "stream-intersection-trimmer-%s", - upstream_port_name); - if (ret < 0) { - goto error; - } - ret = 0; - - ctx->connect_ports = false; - graph_status = bt_graph_add_filter_component( - ctx->graph, trimmer_class, trimmer_name, - trimmer_params, &trimmer); - free(trimmer_name); - if (graph_status != BT_GRAPH_STATUS_OK) { - goto error; - } - BT_ASSERT(trimmer); - - trimmer_input = - bt_component_filter_borrow_input_port_by_index_const( - trimmer, 0); - if (!trimmer_input) { - goto error; - } - trimmer_output = - bt_component_filter_borrow_output_port_by_index_const( - trimmer, 0); - if (!trimmer_output) { - goto error; - } - - /* - * Replace the current downstream port by the trimmer's - * upstream port. - */ - in_downstream_port = trimmer_input; - downstream_port = - bt_port_input_as_port_const(in_downstream_port); - downstream_port_name = bt_port_get_name( - downstream_port); - BT_ASSERT(downstream_port_name); - } - - /* We have a winner! */ - status = bt_graph_connect_ports(ctx->graph, - out_upstream_port, in_downstream_port, NULL); - downstream_port = NULL; - switch (status) { - case BT_GRAPH_STATUS_OK: - break; - case BT_GRAPH_STATUS_CANCELED: - BT_LOGI_STR("Graph was canceled by user."); - status = BT_GRAPH_STATUS_OK; - break; - case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - BT_LOGE("A component refused a connection to one of its ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - fprintf(stderr, - "A component refused a connection to one of its ports (`%s` to `%s`): %s\n", - bt_port_get_name(upstream_port), - downstream_port_name, - cfg_conn->arg->str); - break; - default: - BT_LOGE("Cannot create connection: graph refuses to connect ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - fprintf(stderr, - "Cannot create connection: graph refuses to connect ports (`%s` to `%s`): %s\n", - bt_port_get_name(upstream_port), - downstream_port_name, - cfg_conn->arg->str); - goto error; - } - - BT_LOGI("Connected component ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - - if (insert_trimmer) { - /* - * The first connection, from the source to the trimmer, - * has been done. We now connect the trimmer to the - * original downstream port. - */ - ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( - ctx, - bt_component_filter_as_component_const(trimmer), - trimmer_output, cfg_conn); - if (ret) { - goto error; - } - ctx->connect_ports = true; - } - - /* - * We found a matching downstream port: the search is - * over. - */ - goto end; - } - - /* No downstream port found */ - BT_LOGE("Cannot create connection: cannot find a matching downstream port for upstream port: " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-name=\"%s\", conn-arg=\"%s\"", - upstream_port, bt_port_get_name(upstream_port), - cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - fprintf(stderr, - "Cannot create connection: cannot find a matching downstream port for upstream port `%s`: %s\n", - bt_port_get_name(upstream_port), cfg_conn->arg->str); - -error: - ret = -1; - -end: - free(intersection_begin); - free(intersection_end); - BT_VALUE_PUT_REF_AND_RESET(trimmer_params); - BT_COMPONENT_CLASS_FILTER_PUT_REF_AND_RESET(trimmer_class); - BT_COMPONENT_FILTER_PUT_REF_AND_RESET(trimmer); - return ret; -} - -static -int cmd_run_ctx_connect_upstream_port(struct cmd_run_ctx *ctx, - const bt_port_output *upstream_port) -{ - int ret = 0; - const char *upstream_port_name; - const char *upstream_comp_name; - const bt_component *upstream_comp = NULL; - size_t i; - - BT_ASSERT(ctx); - BT_ASSERT(upstream_port); - upstream_port_name = bt_port_get_name( - bt_port_output_as_port_const(upstream_port)); - BT_ASSERT(upstream_port_name); - upstream_comp = bt_port_borrow_component_const( - bt_port_output_as_port_const(upstream_port)); - if (!upstream_comp) { - BT_LOGW("Upstream port to connect is not part of a component: " - "port-addr=%p, port-name=\"%s\"", - upstream_port, upstream_port_name); - ret = -1; - goto end; - } - - upstream_comp_name = bt_component_get_name(upstream_comp); - BT_ASSERT(upstream_comp_name); - BT_LOGI("Connecting upstream port: comp-addr=%p, comp-name=\"%s\", " - "port-addr=%p, port-name=\"%s\"", - upstream_comp, upstream_comp_name, - upstream_port, upstream_port_name); - - for (i = 0; i < ctx->cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *cfg_conn = - g_ptr_array_index( - ctx->cfg->cmd_data.run.connections, i); - - if (strcmp(cfg_conn->upstream_comp_name->str, - upstream_comp_name)) { - continue; - } - - if (!bt_common_star_glob_match( - cfg_conn->upstream_port_glob->str, - SIZE_MAX, upstream_port_name, SIZE_MAX)) { - continue; - } - - ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( - ctx, upstream_comp, upstream_port, cfg_conn); - if (ret) { - BT_LOGE("Cannot connect upstream port: " - "port-addr=%p, port-name=\"%s\"", - upstream_port, - upstream_port_name); - fprintf(stderr, - "Cannot connect port `%s` of component `%s` to a downstream port: %s\n", - upstream_port_name, - upstream_comp_name, - cfg_conn->arg->str); - goto error; - } - goto end; - } - - BT_LOGE("Cannot connect upstream port: port does not match any connection argument: " - "port-addr=%p, port-name=\"%s\"", upstream_port, - upstream_port_name); - fprintf(stderr, - "Cannot create connection: upstream port `%s` does not match any connection\n", - upstream_port_name); - -error: - ret = -1; - -end: - return ret; -} - -static -bt_graph_listener_status -graph_output_port_added_listener(struct cmd_run_ctx *ctx, - const bt_port_output *out_port) -{ - const bt_component *comp; - const bt_port *port = bt_port_output_as_port_const(out_port); - bt_graph_listener_status ret = BT_GRAPH_LISTENER_STATUS_OK; - - comp = bt_port_borrow_component_const(port); - BT_LOGI("Port added to a graph's component: comp-addr=%p, " - "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"", - comp, comp ? bt_component_get_name(comp) : "", - port, bt_port_get_name(port)); - - if (!ctx->connect_ports) { - goto end; - } - - if (!comp) { - BT_LOGW_STR("Port has no component."); - goto end; - } - - if (bt_port_is_connected(port)) { - BT_LOGW_STR("Port is already connected."); - goto end; - } - - if (cmd_run_ctx_connect_upstream_port(ctx, out_port)) { - BT_LOGF_STR("Cannot connect upstream port."); - fprintf(stderr, "Added port could not be connected: aborting\n"); - ret = BT_GRAPH_LISTENER_STATUS_ERROR; - goto end; - } - -end: - return ret; -} - -static -bt_graph_listener_status graph_source_output_port_added_listener( - const bt_component_source *component, - const bt_port_output *port, void *data) -{ - return graph_output_port_added_listener(data, port); -} - -static -bt_graph_listener_status graph_filter_output_port_added_listener( - const bt_component_filter *component, - const bt_port_output *port, void *data) -{ - return graph_output_port_added_listener(data, port); -} - -static -void cmd_run_ctx_destroy(struct cmd_run_ctx *ctx) -{ - if (!ctx) { - return; - } - - if (ctx->src_components) { - g_hash_table_destroy(ctx->src_components); - ctx->src_components = NULL; - } - - if (ctx->flt_components) { - g_hash_table_destroy(ctx->flt_components); - ctx->flt_components = NULL; - } - - if (ctx->sink_components) { - g_hash_table_destroy(ctx->sink_components); - ctx->sink_components = NULL; - } - - if (ctx->intersections) { - g_hash_table_destroy(ctx->intersections); - ctx->intersections = NULL; - } - - BT_GRAPH_PUT_REF_AND_RESET(ctx->graph); - the_graph = NULL; - ctx->cfg = NULL; -} - -static -int cmd_run_ctx_init(struct cmd_run_ctx *ctx, struct bt_config *cfg) -{ - int ret = 0; - bt_graph_status status; - - ctx->cfg = cfg; - ctx->connect_ports = false; - ctx->src_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->src_components) { - goto error; - } - - ctx->flt_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->flt_components) { - goto error; - } - - ctx->sink_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->sink_components) { - goto error; - } - - if (cfg->cmd_data.run.stream_intersection_mode) { - ctx->stream_intersection_mode = true; - ctx->intersections = g_hash_table_new_full(port_id_hash, - port_id_equal, port_id_destroy, trace_range_destroy); - if (!ctx->intersections) { - goto error; - } - } - - ctx->graph = bt_graph_create(); - if (!ctx->graph) { - goto error; - } - - the_graph = ctx->graph; - status = bt_graph_add_source_component_output_port_added_listener( - ctx->graph, graph_source_output_port_added_listener, NULL, ctx, - NULL); - if (status != BT_GRAPH_STATUS_OK) { - BT_LOGE_STR("Cannot add \"port added\" listener to graph."); - goto error; - } - - status = bt_graph_add_filter_component_output_port_added_listener( - ctx->graph, graph_filter_output_port_added_listener, NULL, ctx, - NULL); - if (status != BT_GRAPH_STATUS_OK) { - BT_LOGE_STR("Cannot add \"port added\" listener to graph."); - goto error; - } - - goto end; - -error: - cmd_run_ctx_destroy(ctx); - ret = -1; - -end: - return ret; -} - -static -int set_stream_intersections(struct cmd_run_ctx *ctx, - struct bt_config_component *cfg_comp, - const bt_component_class_source *src_comp_cls) -{ - int ret = 0; - uint64_t trace_idx; - int64_t trace_count; - const char *path = NULL; - const bt_value *query_result = NULL; - const bt_value *trace_info = NULL; - const bt_value *intersection_range = NULL; - const bt_value *intersection_begin = NULL; - const bt_value *intersection_end = NULL; - const bt_value *stream_infos = NULL; - const bt_value *stream_info = NULL; - struct port_id *port_id = NULL; - struct trace_range *trace_range = NULL; - const char *fail_reason = NULL; - const bt_component_class *comp_cls = - bt_component_class_source_as_component_class_const(src_comp_cls); - - ret = query(comp_cls, "trace-info", - cfg_comp->params, &query_result, - &fail_reason); - if (ret) { - BT_LOGD("Component class does not support the `trace-info` query: %s: " - "comp-class-name=\"%s\"", fail_reason, - bt_component_class_get_name(comp_cls)); - ret = -1; - goto error; - } - - BT_ASSERT(query_result); - - if (!bt_value_is_array(query_result)) { - BT_LOGD("Unexpected format of \'trace-info\' query result: " - "component-class-name=%s", - bt_component_class_get_name(comp_cls)); - ret = -1; - goto error; - } - - trace_count = bt_value_array_get_size(query_result); - if (trace_count < 0) { - ret = -1; - goto error; - } - - for (trace_idx = 0; trace_idx < trace_count; trace_idx++) { - int64_t begin, end; - uint64_t stream_idx; - int64_t stream_count; - - trace_info = bt_value_array_borrow_element_by_index_const( - query_result, trace_idx); - if (!trace_info || !bt_value_is_map(trace_info)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve trace from query result."); - goto error; - } - - intersection_range = bt_value_map_borrow_entry_value_const( - trace_info, "intersection-range-ns"); - if (!intersection_range) { - ret = -1; - BT_LOGD_STR("Cannot retrieve \'intersetion-range-ns\' field from query result."); - goto error; - } - - intersection_begin = bt_value_map_borrow_entry_value_const(intersection_range, - "begin"); - if (!intersection_begin) { - ret = -1; - BT_LOGD_STR("Cannot retrieve intersection-range-ns \'begin\' field from query result."); - goto error; - } - - intersection_end = bt_value_map_borrow_entry_value_const(intersection_range, - "end"); - if (!intersection_end) { - ret = -1; - BT_LOGD_STR("Cannot retrieve intersection-range-ns \'end\' field from query result."); - goto error; - } - - begin = bt_value_signed_integer_get(intersection_begin); - end = bt_value_signed_integer_get(intersection_end); - - if (begin < 0 || end < 0 || end < begin) { - BT_LOGW("Invalid trace stream intersection values: " - "intersection-range-ns:begin=%" PRId64 - ", intersection-range-ns:end=%" PRId64, - begin, end); - ret = -1; - goto error; - } - - stream_infos = bt_value_map_borrow_entry_value_const(trace_info, - "streams"); - if (!stream_infos || !bt_value_is_array(stream_infos)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve stream information from trace in query result."); - goto error; - } - - stream_count = bt_value_array_get_size(stream_infos); - if (stream_count < 0) { - ret = -1; - goto error; - } - - for (stream_idx = 0; stream_idx < stream_count; stream_idx++) { - const bt_value *port_name; - - port_id = g_new0(struct port_id, 1); - if (!port_id) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id structure."); - goto error; - } - port_id->instance_name = strdup(cfg_comp->instance_name->str); - if (!port_id->instance_name) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id component instance name."); - goto error; - } - - trace_range = g_new0(struct trace_range, 1); - if (!trace_range) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for trace_range structure."); - goto error; - } - trace_range->intersection_range_begin_ns = begin; - trace_range->intersection_range_end_ns = end; - - stream_info = bt_value_array_borrow_element_by_index_const( - stream_infos, stream_idx); - if (!stream_info || !bt_value_is_map(stream_info)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve stream informations from trace in query result."); - goto error; - } - - port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name"); - if (!port_name || !bt_value_is_string(port_name)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve port name in query result."); - goto error; - } - - port_id->port_name = g_strdup(bt_value_string_get(port_name)); - if (!port_id->port_name) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id port_name."); - goto error; - } - - BT_LOGD("Inserting stream intersection "); - - g_hash_table_insert(ctx->intersections, port_id, trace_range); - - port_id = NULL; - trace_range = NULL; - } - } - - goto end; - -error: - fprintf(stderr, "%s%sCannot determine stream intersection of trace at path \'%s\'.%s\n", - bt_common_color_bold(), - bt_common_color_fg_yellow(), - path ? path : "(unknown)", - bt_common_color_reset()); -end: - bt_value_put_ref(query_result); - g_free(port_id); - g_free(trace_range); - return ret; -} - -static -int cmd_run_ctx_create_components_from_config_components( - struct cmd_run_ctx *ctx, GPtrArray *cfg_components) -{ - size_t i; - const void *comp_cls = NULL; - const void *comp = NULL; - int ret = 0; - - for (i = 0; i < cfg_components->len; i++) { - struct bt_config_component *cfg_comp = - g_ptr_array_index(cfg_components, i); - GQuark quark; - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - comp_cls = find_source_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - comp_cls = find_filter_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - comp_cls = find_sink_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - default: - abort(); - } - - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type); - fprintf(stderr, "\n"); - goto error; - } - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - ret = bt_graph_add_source_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - ret = bt_graph_add_filter_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - ret = bt_graph_add_sink_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - default: - abort(); - } - - if (ret) { - BT_LOGE("Cannot create component: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d, " - "comp-name=\"%s\"", - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type, cfg_comp->instance_name->str); - fprintf(stderr, "%s%sCannot create component `%s`%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - cfg_comp->instance_name->str, - bt_common_color_reset()); - goto error; - } - - if (ctx->stream_intersection_mode && - cfg_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { - ret = set_stream_intersections(ctx, cfg_comp, comp_cls); - if (ret) { - goto error; - } - } - - BT_LOGI("Created and inserted component: comp-addr=%p, comp-name=\"%s\"", - comp, cfg_comp->instance_name->str); - quark = g_quark_from_string(cfg_comp->instance_name->str); - BT_ASSERT(quark > 0); - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - g_hash_table_insert(ctx->src_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - g_hash_table_insert(ctx->flt_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - g_hash_table_insert(ctx->sink_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - default: - abort(); - } - - comp = NULL; - BT_OBJECT_PUT_REF_AND_RESET(comp_cls); - } - - goto end; - -error: - ret = -1; - -end: - bt_object_put_ref(comp); - bt_object_put_ref(comp_cls); - return ret; -} - -static -int cmd_run_ctx_create_components(struct cmd_run_ctx *ctx) -{ - int ret = 0; - - /* - * Make sure that, during this phase, our graph's "port added" - * listener does not connect ports while we are creating the - * components because we have a special, initial phase for - * this. - */ - ctx->connect_ports = false; - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.sources); - if (ret) { - ret = -1; - goto end; - } - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.filters); - if (ret) { - ret = -1; - goto end; - } - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.sinks); - if (ret) { - ret = -1; - goto end; - } - -end: - return ret; -} - -typedef uint64_t (*output_port_count_func_t)(const void *); -typedef const bt_port_output *(*borrow_output_port_by_index_func_t)( - const void *, uint64_t); - -static -int cmd_run_ctx_connect_comp_ports(struct cmd_run_ctx *ctx, - void *comp, output_port_count_func_t port_count_fn, - borrow_output_port_by_index_func_t port_by_index_fn) -{ - int ret = 0; - uint64_t count; - uint64_t i; - - count = port_count_fn(comp); - - for (i = 0; i < count; i++) { - const bt_port_output *upstream_port = port_by_index_fn(comp, i); - - BT_ASSERT(upstream_port); - ret = cmd_run_ctx_connect_upstream_port(ctx, upstream_port); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -int cmd_run_ctx_connect_ports(struct cmd_run_ctx *ctx) -{ - int ret = 0; - GHashTableIter iter; - gpointer g_name_quark, g_comp; - - ctx->connect_ports = true; - g_hash_table_iter_init(&iter, ctx->src_components); - - while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { - ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, - (output_port_count_func_t) - bt_component_source_get_output_port_count, - (borrow_output_port_by_index_func_t) - bt_component_source_borrow_output_port_by_index_const); - if (ret) { - goto end; - } - } - - g_hash_table_iter_init(&iter, ctx->flt_components); - - while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { - ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, - (output_port_count_func_t) - bt_component_filter_get_output_port_count, - (borrow_output_port_by_index_func_t) - bt_component_filter_borrow_output_port_by_index_const); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static inline -const char *bt_graph_status_str(bt_graph_status status) -{ - switch (status) { - case BT_GRAPH_STATUS_OK: - return "BT_GRAPH_STATUS_OK"; - case BT_GRAPH_STATUS_END: - return "BT_GRAPH_STATUS_END"; - case BT_GRAPH_STATUS_AGAIN: - return "BT_GRAPH_STATUS_AGAIN"; - case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; - case BT_GRAPH_STATUS_CANCELED: - return "BT_GRAPH_STATUS_CANCELED"; - case BT_GRAPH_STATUS_ERROR: - return "BT_GRAPH_STATUS_ERROR"; - case BT_GRAPH_STATUS_NOMEM: - return "BT_GRAPH_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -static -int cmd_run(struct bt_config *cfg) -{ - int ret = 0; - struct cmd_run_ctx ctx = { 0 }; - - /* Initialize the command's context and the graph object */ - if (cmd_run_ctx_init(&ctx, cfg)) { - BT_LOGE_STR("Cannot initialize the command's context."); - fprintf(stderr, "Cannot initialize the command's context\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before creating components."); - goto error; - } - - BT_LOGI_STR("Creating components."); - - /* Create the requested component instances */ - if (cmd_run_ctx_create_components(&ctx)) { - BT_LOGE_STR("Cannot create components."); - fprintf(stderr, "Cannot create components\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before connecting components."); - goto error; - } - - BT_LOGI_STR("Connecting components."); - - /* Connect the initially visible component ports */ - if (cmd_run_ctx_connect_ports(&ctx)) { - BT_LOGE_STR("Cannot connect initial component ports."); - fprintf(stderr, "Cannot connect initial component ports\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before running the graph."); - goto error; - } - - BT_LOGI_STR("Running the graph."); - - /* Run the graph */ - while (true) { - bt_graph_status graph_status = bt_graph_run(ctx.graph); - - /* - * Reset console in case something messed with console - * codes during the graph's execution. - */ - printf("%s", bt_common_color_reset()); - fflush(stdout); - fprintf(stderr, "%s", bt_common_color_reset()); - BT_LOGV("bt_graph_run() returned: status=%s", - bt_graph_status_str(graph_status)); - - switch (graph_status) { - case BT_GRAPH_STATUS_OK: - break; - case BT_GRAPH_STATUS_CANCELED: - BT_LOGI_STR("Graph was canceled by user."); - goto error; - case BT_GRAPH_STATUS_AGAIN: - if (bt_graph_is_canceled(ctx.graph)) { - BT_LOGI_STR("Graph was canceled by user."); - goto error; - } - - if (cfg->cmd_data.run.retry_duration_us > 0) { - BT_LOGV("Got BT_GRAPH_STATUS_AGAIN: sleeping: " - "time-us=%" PRIu64, - cfg->cmd_data.run.retry_duration_us); - - if (usleep(cfg->cmd_data.run.retry_duration_us)) { - if (bt_graph_is_canceled(ctx.graph)) { - BT_LOGI_STR("Graph was canceled by user."); - goto error; - } - } - } - break; - case BT_GRAPH_STATUS_END: - goto end; - default: - BT_LOGE_STR("Graph failed to complete successfully"); - fprintf(stderr, "Graph failed to complete successfully\n"); - goto error; - } - } - - goto end; - -error: - if (ret == 0) { - ret = -1; - } - -end: - cmd_run_ctx_destroy(&ctx); - return ret; -} - -static -void warn_command_name_and_directory_clash(struct bt_config *cfg) -{ - const char *env_clash; - - if (!cfg->command_name) { - return; - } - - env_clash = getenv(ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH); - if (env_clash && strcmp(env_clash, "0") == 0) { - return; - } - - if (g_file_test(cfg->command_name, - G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - fprintf(stderr, "\nNOTE: The `%s` command was executed. If you meant to convert a\n", - cfg->command_name); - fprintf(stderr, "trace located in the local `%s` directory, please use:\n", - cfg->command_name); - fprintf(stderr, "\n"); - fprintf(stderr, " babeltrace2 convert %s [OPTIONS]\n", - cfg->command_name); - } -} - -static -void init_log_level(void) -{ - bt_cli_log_level = bt_log_get_level_from_env(ENV_BABELTRACE_CLI_LOG_LEVEL); -} - -static -void set_auto_log_levels(struct bt_config *cfg) -{ - const char **env_var_name; - - /* - * Override the configuration's default log level if - * BABELTRACE_VERBOSE or BABELTRACE_DEBUG environment variables - * are found for backward compatibility with legacy Babetrace 1. - */ - if (getenv("BABELTRACE_DEBUG") && - strcmp(getenv("BABELTRACE_DEBUG"), "1") == 0) { - cfg->log_level = 'V'; - } else if (getenv("BABELTRACE_VERBOSE") && - strcmp(getenv("BABELTRACE_VERBOSE"), "1") == 0) { - cfg->log_level = 'I'; - } - - /* - * Set log levels according to --debug or --verbose. For - * backward compatibility, --debug is more verbose than - * --verbose. So: - * - * --verbose: INFO log level - * --debug: VERBOSE log level (includes DEBUG, which is - * is less verbose than VERBOSE in the internal - * logging framework) - */ - if (!getenv("BABELTRACE_LOGGING_GLOBAL_LEVEL")) { - if (cfg->verbose) { - bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); - } else if (cfg->debug) { - bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); - } else { - /* - * Set library's default log level if not - * explicitly specified. - */ - switch (cfg->log_level) { - case 'N': - bt_logging_set_global_level(BT_LOGGING_LEVEL_NONE); - break; - case 'V': - bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); - break; - case 'D': - bt_logging_set_global_level(BT_LOGGING_LEVEL_DEBUG); - break; - case 'I': - bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); - break; - case 'W': - bt_logging_set_global_level(BT_LOGGING_LEVEL_WARN); - break; - case 'E': - bt_logging_set_global_level(BT_LOGGING_LEVEL_ERROR); - break; - case 'F': - bt_logging_set_global_level(BT_LOGGING_LEVEL_FATAL); - break; - default: - abort(); - } - } - } - - if (!getenv(ENV_BABELTRACE_CLI_LOG_LEVEL)) { - if (cfg->verbose) { - bt_cli_log_level = BT_LOG_INFO; - } else if (cfg->debug) { - bt_cli_log_level = BT_LOG_VERBOSE; - } else { - /* - * Set CLI's default log level if not explicitly - * specified. - */ - switch (cfg->log_level) { - case 'N': - bt_cli_log_level = BT_LOG_NONE; - break; - case 'V': - bt_cli_log_level = BT_LOG_VERBOSE; - break; - case 'D': - bt_cli_log_level = BT_LOG_DEBUG; - break; - case 'I': - bt_cli_log_level = BT_LOG_INFO; - break; - case 'W': - bt_cli_log_level = BT_LOG_WARN; - break; - case 'E': - bt_cli_log_level = BT_LOG_ERROR; - break; - case 'F': - bt_cli_log_level = BT_LOG_FATAL; - break; - default: - abort(); - } - } - } - - env_var_name = log_level_env_var_names; - - while (*env_var_name) { - if (!getenv(*env_var_name)) { - if (cfg->verbose) { - g_setenv(*env_var_name, "I", 1); - } else if (cfg->debug) { - g_setenv(*env_var_name, "V", 1); - } else { - char val[2] = { 0 }; - - /* - * Set module's default log level if not - * explicitly specified. - */ - val[0] = cfg->log_level; - g_setenv(*env_var_name, val, 1); - } - } - - env_var_name++; - } -} - -int main(int argc, const char **argv) -{ - int ret; - int retcode; - struct bt_config *cfg; - - init_log_level(); - set_signal_handler(); - init_static_data(); - cfg = bt_config_cli_args_create_with_default(argc, argv, &retcode); - - if (retcode < 0) { - /* Quit without errors; typically usage/version */ - retcode = 0; - BT_LOGI_STR("Quitting without errors."); - goto end; - } - - if (retcode > 0) { - BT_LOGE("Command-line error: retcode=%d", retcode); - goto end; - } - - if (!cfg) { - BT_LOGE_STR("Failed to create a valid Babeltrace configuration."); - fprintf(stderr, "Failed to create Babeltrace configuration\n"); - retcode = 1; - goto end; - } - - set_auto_log_levels(cfg); - print_cfg(cfg); - - if (cfg->command_needs_plugins) { - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - BT_LOGE("Failed to load plugins: ret=%d", ret); - retcode = 1; - goto end; - } - } - - BT_LOGI("Executing command: cmd=%d, command-name=\"%s\"", - cfg->command, cfg->command_name); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - ret = cmd_run(cfg); - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - ret = cmd_list_plugins(cfg); - break; - case BT_CONFIG_COMMAND_HELP: - ret = cmd_help(cfg); - break; - case BT_CONFIG_COMMAND_QUERY: - ret = cmd_query(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - ret = cmd_print_ctf_metadata(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - ret = cmd_print_lttng_live_sessions(cfg); - break; - default: - BT_LOGF("Invalid/unknown command: cmd=%d", cfg->command); - abort(); - } - - BT_LOGI("Command completed: cmd=%d, command-name=\"%s\", ret=%d", - cfg->command, cfg->command_name, ret); - warn_command_name_and_directory_clash(cfg); - retcode = ret ? 1 : 0; - -end: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - fini_static_data(); - return retcode; -} diff --git a/cli/logging.c b/cli/logging.c deleted file mode 100644 index a2a522bc..00000000 --- a/cli/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL"); diff --git a/cli/logging.h b/cli/logging.h deleted file mode 100644 index 6e5dc9fe..00000000 --- a/cli/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CLI_LOGGING_H -#define CLI_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_cli_log_level); - -#endif /* CLI_LOGGING_H */ diff --git a/common/Makefile.am b/common/Makefile.am deleted file mode 100644 index 75247fcd..00000000 --- a/common/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" - - -noinst_LTLIBRARIES = libbabeltrace2-common.la - -libbabeltrace2_common_la_SOURCES = assert.c common.c logging.c logging.h diff --git a/common/assert.c b/common/assert.c deleted file mode 100644 index 1e28219f..00000000 --- a/common/assert.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2019 EfficiOS Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -void bt_common_assert_failed(const char *file, int line, const char *func, - const char *assertion) -{ - fprintf(stderr, - "%s\n%s%s%s (╯°□°)╯︵ ┻━┻ %s %s%s%s%s:%s%d%s: %s%s()%s: " - "%sAssertion %s`%s`%s%s failed.%s\n", - bt_common_color_reset(), - bt_common_color_bold(), - bt_common_color_bg_yellow(), - bt_common_color_fg_red(), - bt_common_color_reset(), - bt_common_color_bold(), - bt_common_color_fg_magenta(), - file, - bt_common_color_reset(), - bt_common_color_fg_green(), - line, - bt_common_color_reset(), - bt_common_color_fg_cyan(), - func, - bt_common_color_reset(), - bt_common_color_fg_red(), - bt_common_color_bold(), - assertion, - bt_common_color_reset(), - bt_common_color_fg_red(), - bt_common_color_reset()); - abort(); -} diff --git a/common/common.c b/common/common.c deleted file mode 100644 index f2fd0b82..00000000 --- a/common/common.c +++ /dev/null @@ -1,1569 +0,0 @@ -/* - * Babeltrace common functions - * - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMMON" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __MINGW32__ -#include -#endif - -#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace2/plugins" -#define HOME_ENV_VAR "HOME" -#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins" - -static const char *bt_common_color_code_reset = ""; -static const char *bt_common_color_code_bold = ""; -static const char *bt_common_color_code_fg_default = ""; -static const char *bt_common_color_code_fg_red = ""; -static const char *bt_common_color_code_fg_green = ""; -static const char *bt_common_color_code_fg_yellow = ""; -static const char *bt_common_color_code_fg_blue = ""; -static const char *bt_common_color_code_fg_magenta = ""; -static const char *bt_common_color_code_fg_cyan = ""; -static const char *bt_common_color_code_fg_light_gray = ""; -static const char *bt_common_color_code_bg_default = ""; -static const char *bt_common_color_code_bg_red = ""; -static const char *bt_common_color_code_bg_green = ""; -static const char *bt_common_color_code_bg_yellow = ""; -static const char *bt_common_color_code_bg_blue = ""; -static const char *bt_common_color_code_bg_magenta = ""; -static const char *bt_common_color_code_bg_cyan = ""; -static const char *bt_common_color_code_bg_light_gray = ""; - -static -void __attribute__((constructor)) bt_common_color_ctor(void) -{ - if (bt_common_colors_supported()) { - bt_common_color_code_reset = BT_COMMON_COLOR_RESET; - bt_common_color_code_bold = BT_COMMON_COLOR_BOLD; - bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT; - bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED; - bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN; - bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW; - bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE; - bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA; - bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN; - bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY; - bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT; - bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED; - bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN; - bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW; - bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE; - bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA; - bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN; - bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY; - } -} - -BT_HIDDEN -const char *bt_common_get_system_plugin_path(void) -{ - return SYSTEM_PLUGIN_PATH; -} - -#ifdef __MINGW32__ -BT_HIDDEN -bool bt_common_is_setuid_setgid(void) -{ - return false; -} -#else /* __MINGW32__ */ -BT_HIDDEN -bool bt_common_is_setuid_setgid(void) -{ - return (geteuid() != getuid() || getegid() != getgid()); -} -#endif /* __MINGW32__ */ - -static -char *bt_secure_getenv(const char *name) -{ - if (bt_common_is_setuid_setgid()) { - BT_LOGD("Disregarding environment variable for setuid/setgid binary: " - "name=\"%s\"", name); - return NULL; - } - return getenv(name); -} - -#ifdef __MINGW32__ -static -const char *bt_get_home_dir(void) -{ - return g_get_home_dir(); -} -#else /* __MINGW32__ */ -static -const char *bt_get_home_dir(void) -{ - char *val = NULL; - struct passwd *pwd; - - val = bt_secure_getenv(HOME_ENV_VAR); - if (val) { - goto end; - } - /* Fallback on password file. */ - pwd = getpwuid(getuid()); - if (!pwd) { - goto end; - } - val = pwd->pw_dir; -end: - return val; -} -#endif /* __MINGW32__ */ - -BT_HIDDEN -char *bt_common_get_home_plugin_path(void) -{ - char *path = NULL; - const char *home_dir; - size_t length; - - home_dir = bt_get_home_dir(); - if (!home_dir) { - goto end; - } - - length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1; - - if (length >= PATH_MAX) { - BT_LOGW("Home directory path is too long: length=%zu", - length); - goto end; - } - - path = malloc(PATH_MAX); - if (!path) { - goto end; - } - - strcpy(path, home_dir); - strcat(path, HOME_PLUGIN_SUBPATH); - -end: - return path; -} - -BT_HIDDEN -int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs) -{ - int ret = 0; - const char *at; - const char *end; - size_t init_dirs_len; - - BT_ASSERT(dirs); - init_dirs_len = dirs->len; - - if (!paths) { - /* Nothing to append */ - goto end; - } - - at = paths; - end = paths + strlen(paths); - - while (at < end) { - GString *path; - const char *next_sep; - - next_sep = strchr(at, G_SEARCHPATH_SEPARATOR); - if (next_sep == at) { - /* - * Empty path: try next character (supported - * to conform to the typical parsing of $PATH). - */ - at++; - continue; - } else if (!next_sep) { - /* No more separator: use the remaining */ - next_sep = paths + strlen(paths); - } - - path = g_string_new(NULL); - if (!path) { - goto error; - } - - g_string_append_len(path, at, next_sep - at); - at = next_sep + 1; - g_ptr_array_add(dirs, path); - } - - goto end; - -error: - ret = -1; - - /* Remove the new entries in dirs */ - while (dirs->len > init_dirs_len) { - g_ptr_array_remove_index(dirs, init_dirs_len); - } - -end: - return ret; -} - -static -bool isarealtty(int fd) -{ - bool istty = false; - struct stat tty_stats; - - if (!isatty(fd)) { - /* Not a TTY */ - goto end; - } - - if (fstat(fd, &tty_stats) == 0) { - if (!S_ISCHR(tty_stats.st_mode)) { - /* Not a character device: not a TTY */ - goto end; - } - } - - istty = true; - -end: - return istty; -} - -BT_HIDDEN -bool bt_common_colors_supported(void) -{ - static bool supports_colors = false; - static bool supports_colors_set = false; - const char *term_env_var; - const char *term_color_env_var; - - if (supports_colors_set) { - goto end; - } - - supports_colors_set = true; - - /* - * `BABELTRACE_TERM_COLOR` environment variable always overrides - * the automatic color support detection. - */ - term_color_env_var = getenv("BABELTRACE_TERM_COLOR"); - if (term_color_env_var) { - if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) { - /* Force colors */ - supports_colors = true; - } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) { - /* Force no colors */ - goto end; - } - } - - /* We need a compatible, known terminal */ - term_env_var = getenv("TERM"); - if (!term_env_var) { - goto end; - } - - if (strncmp(term_env_var, "xterm", 5) != 0 && - strncmp(term_env_var, "rxvt", 4) != 0 && - strncmp(term_env_var, "konsole", 7) != 0 && - strncmp(term_env_var, "gnome", 5) != 0 && - strncmp(term_env_var, "screen", 5) != 0 && - strncmp(term_env_var, "tmux", 4) != 0 && - strncmp(term_env_var, "putty", 5) != 0) { - goto end; - } - - /* Both standard output and error streams need to be TTYs */ - if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) { - goto end; - } - - supports_colors = true; - -end: - return supports_colors; -} - -BT_HIDDEN -const char *bt_common_color_reset(void) -{ - return bt_common_color_code_reset; -} - -BT_HIDDEN -const char *bt_common_color_bold(void) -{ - return bt_common_color_code_bold; -} - -BT_HIDDEN -const char *bt_common_color_fg_default(void) -{ - return bt_common_color_code_fg_default; -} - -BT_HIDDEN -const char *bt_common_color_fg_red(void) -{ - return bt_common_color_code_fg_red; -} - -BT_HIDDEN -const char *bt_common_color_fg_green(void) -{ - return bt_common_color_code_fg_green; -} - -BT_HIDDEN -const char *bt_common_color_fg_yellow(void) -{ - return bt_common_color_code_fg_yellow; -} - -BT_HIDDEN -const char *bt_common_color_fg_blue(void) -{ - return bt_common_color_code_fg_blue; -} - -BT_HIDDEN -const char *bt_common_color_fg_magenta(void) -{ - return bt_common_color_code_fg_magenta; -} - -BT_HIDDEN -const char *bt_common_color_fg_cyan(void) -{ - return bt_common_color_code_fg_cyan; -} - -BT_HIDDEN -const char *bt_common_color_fg_light_gray(void) -{ - return bt_common_color_code_fg_light_gray; -} - -BT_HIDDEN -const char *bt_common_color_bg_default(void) -{ - return bt_common_color_code_bg_default; -} - -BT_HIDDEN -const char *bt_common_color_bg_red(void) -{ - return bt_common_color_code_bg_red; -} - -BT_HIDDEN -const char *bt_common_color_bg_green(void) -{ - return bt_common_color_code_bg_green; -} - -BT_HIDDEN -const char *bt_common_color_bg_yellow(void) -{ - return bt_common_color_code_bg_yellow; -} - -BT_HIDDEN -const char *bt_common_color_bg_blue(void) -{ - return bt_common_color_code_bg_blue; -} - -BT_HIDDEN -const char *bt_common_color_bg_magenta(void) -{ - return bt_common_color_code_bg_magenta; -} - -BT_HIDDEN -const char *bt_common_color_bg_cyan(void) -{ - return bt_common_color_code_bg_cyan; -} - -BT_HIDDEN -const char *bt_common_color_bg_light_gray(void) -{ - return bt_common_color_code_bg_light_gray; -} - -BT_HIDDEN -GString *bt_common_string_until(const char *input, const char *escapable_chars, - const char *end_chars, size_t *end_pos) -{ - GString *output = g_string_new(NULL); - const char *ch; - const char *es_char; - const char *end_char; - - if (!output) { - goto error; - } - - for (ch = input; *ch != '\0'; ch++) { - if (*ch == '\\') { - bool continue_loop = false; - - if (ch[1] == '\0') { - /* `\` at the end of the string: append `\` */ - g_string_append_c(output, *ch); - ch++; - goto set_end_pos; - } - - for (es_char = escapable_chars; *es_char != '\0'; es_char++) { - if (ch[1] == *es_char) { - /* - * `\` followed by an escapable - * character: append the escaped - * character only. - */ - g_string_append_c(output, ch[1]); - ch++; - continue_loop = true; - break; - } - } - - if (continue_loop) { - continue; - } - - /* - * `\` followed by a non-escapable character: - * append `\` and the character. - */ - g_string_append_c(output, *ch); - g_string_append_c(output, ch[1]); - ch++; - continue; - } else { - for (end_char = end_chars; *end_char != '\0'; end_char++) { - if (*ch == *end_char) { - /* - * End character found: - * terminate this loop. - */ - goto set_end_pos; - } - } - - /* Normal character: append */ - g_string_append_c(output, *ch); - } - } - -set_end_pos: - if (end_pos) { - *end_pos = ch - input; - } - - goto end; - -error: - if (output) { - g_string_free(output, TRUE); - } - -end: - return output; -} - -BT_HIDDEN -GString *bt_common_shell_quote(const char *input, bool with_single_quotes) -{ - GString *output = g_string_new(NULL); - const char *ch; - bool no_quote = true; - - if (!output) { - goto end; - } - - if (strlen(input) == 0) { - if (with_single_quotes) { - g_string_assign(output, "''"); - } - - goto end; - } - - for (ch = input; *ch != '\0'; ch++) { - const char c = *ch; - - if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' && - c != '@' && c != '%' && c != '+' && c != '=' && - c != ':' && c != ',' && c != '.' && c != '/' && - c != '-') { - no_quote = false; - break; - } - } - - if (no_quote) { - g_string_assign(output, input); - goto end; - } - - if (with_single_quotes) { - g_string_assign(output, "'"); - } - - for (ch = input; *ch != '\0'; ch++) { - if (*ch == '\'') { - g_string_append(output, "'\"'\"'"); - } else { - g_string_append_c(output, *ch); - } - } - - if (with_single_quotes) { - g_string_append_c(output, '\''); - } - -end: - return output; -} - -BT_HIDDEN -bool bt_common_string_is_printable(const char *input) -{ - const char *ch; - bool printable = true; - BT_ASSERT(input); - - for (ch = input; *ch != '\0'; ch++) { - if (!isprint(*ch) && *ch != '\n' && *ch != '\r' && - *ch != '\t' && *ch != '\v') { - printable = false; - goto end; - } - } - -end: - return printable; -} - -BT_HIDDEN -void bt_common_destroy_lttng_live_url_parts( - struct bt_common_lttng_live_url_parts *parts) -{ - if (!parts) { - goto end; - } - - if (parts->proto) { - g_string_free(parts->proto, TRUE); - parts->proto = NULL; - } - - if (parts->hostname) { - g_string_free(parts->hostname, TRUE); - parts->hostname = NULL; - } - - if (parts->target_hostname) { - g_string_free(parts->target_hostname, TRUE); - parts->target_hostname = NULL; - } - - if (parts->session_name) { - g_string_free(parts->session_name, TRUE); - parts->session_name = NULL; - } - -end: - return; -} - -BT_HIDDEN -struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url( - const char *url, char *error_buf, size_t error_buf_size) -{ - struct bt_common_lttng_live_url_parts parts; - const char *at = url; - size_t end_pos; - - BT_ASSERT(url); - memset(&parts, 0, sizeof(parts)); - parts.port = -1; - - /* Protocol */ - parts.proto = bt_common_string_until(at, "", ":", &end_pos); - if (!parts.proto || parts.proto->len == 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, "Missing protocol"); - } - - goto error; - } - - if (strcmp(parts.proto->str, "net") == 0) { - g_string_assign(parts.proto, "net4"); - } - - if (strcmp(parts.proto->str, "net4") != 0 && - strcmp(parts.proto->str, "net6") != 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Unknown protocol: `%s`", parts.proto->str); - } - - goto error; - } - - if (at[end_pos] != ':') { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Expecting `:` after `%s`", parts.proto->str); - } - - goto error; - } - - at += end_pos; - - /* :// */ - if (strncmp(at, "://", 3) != 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Expecting `://` after protocol"); - } - - goto error; - } - - at += 3; - - /* Hostname */ - parts.hostname = bt_common_string_until(at, "", ":/", &end_pos); - if (!parts.hostname || parts.hostname->len == 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, "Missing hostname"); - } - - goto error; - } - - if (at[end_pos] == ':') { - /* Port */ - GString *port; - - at += end_pos + 1; - port = bt_common_string_until(at, "", "/", &end_pos); - if (!port || port->len == 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, "Missing port"); - } - - goto error; - } - - if (sscanf(port->str, "%d", &parts.port) != 1) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Invalid port: `%s`", port->str); - } - - g_string_free(port, TRUE); - goto error; - } - - g_string_free(port, TRUE); - - if (parts.port < 0 || parts.port >= 65536) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Invalid port: %d", parts.port); - } - - goto error; - } - } - - if (at[end_pos] == '\0') { - goto end; - } - - at += end_pos; - - /* /host/ */ - if (strncmp(at, "/host/", 6) != 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Expecting `/host/` after hostname or port"); - } - - goto error; - } - - at += 6; - - /* Target hostname */ - parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos); - if (!parts.target_hostname || parts.target_hostname->len == 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Missing target hostname"); - } - - goto error; - } - - if (at[end_pos] == '\0') { - goto end; - } - - at += end_pos + 1; - - /* Session name */ - parts.session_name = bt_common_string_until(at, "", "/", &end_pos); - if (!parts.session_name || parts.session_name->len == 0) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Missing session name"); - } - - goto error; - } - - if (at[end_pos] == '/') { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "Unexpected `/` after session name (`%s`)", - parts.session_name->str); - } - - goto error; - } - - goto end; - -error: - bt_common_destroy_lttng_live_url_parts(&parts); - -end: - return parts; -} - -BT_HIDDEN -void bt_common_normalize_star_glob_pattern(char *pattern) -{ - const char *p; - char *np; - bool got_star = false; - - BT_ASSERT(pattern); - - for (p = pattern, np = pattern; *p != '\0'; p++) { - switch (*p) { - case '*': - if (got_star) { - /* Avoid consecutive stars. */ - continue; - } - - got_star = true; - break; - case '\\': - /* Copy backslash character. */ - *np = *p; - np++; - p++; - - if (*p == '\0') { - goto end; - } - - /* fall-through */ - default: - got_star = false; - break; - } - - /* Copy single character. */ - *np = *p; - np++; - } - -end: - *np = '\0'; -} - -static inline -bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len) -{ - return (p - pattern) == pattern_len || *p == '\0'; -} - -/* - * Globbing matching function with the star feature only (`?` and - * character sets are not supported). This matches `candidate` (plain - * string) against `pattern`. A literal star can be escaped with `\` in - * `pattern`. - * - * `pattern_len` or `candidate_len` can be greater than the actual - * string length of `pattern` or `candidate` if the string is - * null-terminated. - */ -BT_HIDDEN -bool bt_common_star_glob_match(const char *pattern, size_t pattern_len, - const char *candidate, size_t candidate_len) { - const char *retry_c = candidate, *retry_p = pattern, *c, *p; - bool got_a_star = false; - -retry: - c = retry_c; - p = retry_p; - - /* - * The concept here is to retry a match in the specific case - * where we already got a star. The retry position for the - * pattern is just after the most recent star, and the retry - * position for the candidate is the character following the - * last try's first character. - * - * Example: - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^^ - * pattern: hi*every*one - * ^^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^^ - * pattern: hi*every*one - * ^^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^^ - * pattern: hi*every*one - * ^^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ MISMATCH - * - * candidate: hi ev every onyx one - * ^ - * pattern: hi*every*one - * ^ - * - * candidate: hi ev every onyx one - * ^^ - * pattern: hi*every*one - * ^^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ - * - * candidate: hi ev every onyx one - * ^ ^ - * pattern: hi*every*one - * ^ ^ SUCCESS - */ - while ((c - candidate) < candidate_len && *c != '\0') { - BT_ASSERT(*c); - - if (at_end_of_pattern(p, pattern, pattern_len)) { - goto end_of_pattern; - } - - switch (*p) { - case '*': - got_a_star = true; - - /* - * Our first try starts at the current candidate - * character and after the star in the pattern. - */ - retry_c = c; - retry_p = p + 1; - - if (at_end_of_pattern(retry_p, pattern, pattern_len)) { - /* - * Star at the end of the pattern at - * this point: automatic match. - */ - return true; - } - - goto retry; - case '\\': - /* Go to escaped character. */ - p++; - - /* - * Fall through the default case which compares - * the escaped character now. - */ - /* fall-through */ - default: - if (at_end_of_pattern(p, pattern, pattern_len) || - *c != *p) { -end_of_pattern: - /* Character mismatch OR end of pattern. */ - if (!got_a_star) { - /* - * We didn't get any star yet, - * so this first mismatch - * automatically makes the whole - * test fail. - */ - return false; - } - - /* - * Next try: next candidate character, - * original pattern character (following - * the most recent star). - */ - retry_c++; - goto retry; - } - break; - } - - /* Next pattern and candidate characters. */ - c++; - p++; - } - - /* - * We checked every candidate character and we're still in a - * success state: the only pattern character allowed to remain - * is a star. - */ - if (at_end_of_pattern(p, pattern, pattern_len)) { - return true; - } - - p++; - return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len); -} - -static -void append_path_parts(const char *path, GPtrArray *parts) -{ - const char *ch = path; - const char *last = path; - - while (true) { - if (*ch == G_DIR_SEPARATOR || *ch == '\0') { - if (ch - last > 0) { - GString *part = g_string_new(NULL); - - BT_ASSERT(part); - g_string_append_len(part, last, ch - last); - g_ptr_array_add(parts, part); - } - - if (*ch == '\0') { - break; - } - - last = ch + 1; - } - - ch++; - } -} - -static -void destroy_gstring(void *gstring) -{ - (void) g_string_free(gstring, TRUE); -} - -#ifdef __MINGW32__ -BT_HIDDEN -GString *bt_common_normalize_path(const char *path, const char *wd) -{ - char *tmp; - GString *norm_path = NULL; - - BT_ASSERT(path); - - tmp = _fullpath(NULL, path, PATH_MAX); - if (!tmp) { - goto error; - } - - norm_path = g_string_new(tmp); - if (!norm_path) { - goto error; - } - - goto end; -error: - if (norm_path) { - g_string_free(norm_path, TRUE); - norm_path = NULL; - } -end: - if (tmp) { - free(tmp); - } - return norm_path; -} -#else -BT_HIDDEN -GString *bt_common_normalize_path(const char *path, const char *wd) -{ - size_t i; - GString *norm_path; - GPtrArray *parts = NULL; - - BT_ASSERT(path); - norm_path = g_string_new(G_DIR_SEPARATOR_S); - if (!norm_path) { - goto error; - } - - parts = g_ptr_array_new_with_free_func(destroy_gstring); - if (!parts) { - goto error; - } - - if (path[0] != G_DIR_SEPARATOR) { - /* Relative path: start with working directory */ - if (wd) { - append_path_parts(wd, parts); - } else { - gchar *cd = g_get_current_dir(); - - append_path_parts(cd, parts); - g_free(cd); - } - } - - /* Append parts of the path parameter */ - append_path_parts(path, parts); - - /* Resolve special `..` and `.` parts */ - for (i = 0; i < parts->len; i++) { - GString *part = g_ptr_array_index(parts, i); - - if (strcmp(part->str, "..") == 0) { - if (i == 0) { - /* - * First part of absolute path is `..`: - * this is invalid. - */ - goto error; - } - - /* Remove `..` and previous part */ - g_ptr_array_remove_index(parts, i - 1); - g_ptr_array_remove_index(parts, i - 1); - i -= 2; - } else if (strcmp(part->str, ".") == 0) { - /* Remove `.` */ - g_ptr_array_remove_index(parts, i); - i -= 1; - } - } - - /* Create normalized path with what's left */ - for (i = 0; i < parts->len; i++) { - GString *part = g_ptr_array_index(parts, i); - - g_string_append(norm_path, part->str); - - if (i < parts->len - 1) { - g_string_append_c(norm_path, G_DIR_SEPARATOR); - } - } - - goto end; - -error: - if (norm_path) { - g_string_free(norm_path, TRUE); - norm_path = NULL; - } - -end: - if (parts) { - g_ptr_array_free(parts, TRUE); - } - - return norm_path; -} -#endif - -BT_HIDDEN -size_t bt_common_get_page_size(void) -{ - int page_size; - - page_size = bt_sysconf(_SC_PAGESIZE); - if (page_size < 0) { - BT_LOGF("Cannot get system's page size: ret=%d", - page_size); - abort(); - } - - return page_size; -} - -#define BUF_STD_APPEND(...) \ - do { \ - char _tmp_fmt[64]; \ - int _count; \ - size_t _size = buf_size - (size_t) (*buf_ch - buf); \ - size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \ - strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \ - _tmp_fmt[_tmp_fmt_size] = '\0'; \ - _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \ - BT_ASSERT(_count >= 0); \ - *buf_ch += MIN(_count, _size); \ - } while (0) - -#define BUF_STD_APPEND_SINGLE_ARG(_type) \ - do { \ - _type _arg = va_arg(*args, _type); \ - BUF_STD_APPEND(_arg); \ - } while (0) - -static inline void handle_conversion_specifier_std(char *buf, char **buf_ch, - size_t buf_size, const char **out_fmt_ch, va_list *args) -{ - const char *fmt_ch = *out_fmt_ch; - enum LENGTH_MODIFIER { - LENGTH_MOD_H, - LENGTH_MOD_HH, - LENGTH_MOD_NONE, - LENGTH_MOD_LOW_L, - LENGTH_MOD_LOW_LL, - LENGTH_MOD_UP_L, - LENGTH_MOD_Z, - } length_mod = LENGTH_MOD_NONE; - - /* skip '%' */ - fmt_ch++; - - if (*fmt_ch == '%') { - fmt_ch++; - **buf_ch = '%'; - (*buf_ch)++; - goto update_rw_fmt; - } - - /* flags */ - for (;;) { - switch (*fmt_ch) { - case '-': - case '+': - case ' ': - case '#': - case '0': - case '\'': - fmt_ch++; - continue; - default: - break; - } - break; - } - - /* width */ - for (;;) { - if (*fmt_ch < '0' || *fmt_ch > '9') { - break; - } - - fmt_ch++; - } - - /* precision */ - if (*fmt_ch == '.') { - fmt_ch++; - - for (;;) { - if (*fmt_ch < '0' || *fmt_ch > '9') { - break; - } - - fmt_ch++; - } - } - - /* format (PRI*64) */ - if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) { - fmt_ch += sizeof(PRId64) - 1; - BUF_STD_APPEND_SINGLE_ARG(int64_t); - goto update_rw_fmt; - } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) { - fmt_ch += sizeof(PRIu64) - 1; - BUF_STD_APPEND_SINGLE_ARG(uint64_t); - goto update_rw_fmt; - } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) { - fmt_ch += sizeof(PRIx64) - 1; - BUF_STD_APPEND_SINGLE_ARG(uint64_t); - goto update_rw_fmt; - } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) { - fmt_ch += sizeof(PRIX64) - 1; - BUF_STD_APPEND_SINGLE_ARG(uint64_t); - goto update_rw_fmt; - } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) { - fmt_ch += sizeof(PRIo64) - 1; - BUF_STD_APPEND_SINGLE_ARG(uint64_t); - goto update_rw_fmt; - } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) { - fmt_ch += sizeof(PRIi64) - 1; - BUF_STD_APPEND_SINGLE_ARG(int64_t); - goto update_rw_fmt; - } - - // length modifier - switch (*fmt_ch) { - case 'h': - length_mod = LENGTH_MOD_H; - fmt_ch++; - - if (*fmt_ch == 'h') { - length_mod = LENGTH_MOD_HH; - fmt_ch++; - break; - } - break; - case 'l': - length_mod = LENGTH_MOD_LOW_L; - fmt_ch++; - - if (*fmt_ch == 'l') { - length_mod = LENGTH_MOD_LOW_LL; - fmt_ch++; - break; - } - break; - case 'L': - length_mod = LENGTH_MOD_UP_L; - fmt_ch++; - break; - case 'z': - length_mod = LENGTH_MOD_Z; - fmt_ch++; - break; - default: - break; - } - - // format - switch (*fmt_ch) { - case 'c': - { - fmt_ch++; - - switch (length_mod) { - case LENGTH_MOD_NONE: - BUF_STD_APPEND_SINGLE_ARG(int); - break; - case LENGTH_MOD_LOW_L: - BUF_STD_APPEND_SINGLE_ARG(wint_t); - break; - default: - abort(); - } - break; - } - case 's': - fmt_ch++; - - switch (length_mod) { - case LENGTH_MOD_NONE: - BUF_STD_APPEND_SINGLE_ARG(char *); - break; - case LENGTH_MOD_LOW_L: - BUF_STD_APPEND_SINGLE_ARG(wchar_t *); - break; - default: - abort(); - } - break; - case 'd': - case 'i': - fmt_ch++; - - switch (length_mod) { - case LENGTH_MOD_NONE: - case LENGTH_MOD_H: - case LENGTH_MOD_HH: - BUF_STD_APPEND_SINGLE_ARG(int); - break; - case LENGTH_MOD_LOW_L: - BUF_STD_APPEND_SINGLE_ARG(long); - break; - case LENGTH_MOD_LOW_LL: - BUF_STD_APPEND_SINGLE_ARG(long long); - break; - case LENGTH_MOD_Z: - BUF_STD_APPEND_SINGLE_ARG(size_t); - break; - default: - abort(); - } - break; - case 'o': - case 'x': - case 'X': - case 'u': - fmt_ch++; - - switch (length_mod) { - case LENGTH_MOD_NONE: - case LENGTH_MOD_H: - case LENGTH_MOD_HH: - BUF_STD_APPEND_SINGLE_ARG(unsigned int); - break; - case LENGTH_MOD_LOW_L: - BUF_STD_APPEND_SINGLE_ARG(unsigned long); - break; - case LENGTH_MOD_LOW_LL: - BUF_STD_APPEND_SINGLE_ARG(unsigned long long); - break; - case LENGTH_MOD_Z: - BUF_STD_APPEND_SINGLE_ARG(size_t); - break; - default: - abort(); - } - break; - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - fmt_ch++; - - switch (length_mod) { - case LENGTH_MOD_NONE: - BUF_STD_APPEND_SINGLE_ARG(double); - break; - case LENGTH_MOD_UP_L: - BUF_STD_APPEND_SINGLE_ARG(long double); - break; - default: - abort(); - } - break; - case 'p': - fmt_ch++; - - if (length_mod == LENGTH_MOD_NONE) { - BUF_STD_APPEND_SINGLE_ARG(void *); - } else { - abort(); - } - break; - default: - abort(); - } - -update_rw_fmt: - *out_fmt_ch = fmt_ch; -} - -BT_HIDDEN -void bt_common_custom_vsnprintf(char *buf, size_t buf_size, - char intro, - bt_common_handle_custom_specifier_func handle_specifier, - void *priv_data, const char *fmt, va_list *args) -{ - const char *fmt_ch = fmt; - char *buf_ch = buf; - - BT_ASSERT(buf); - BT_ASSERT(fmt); - - while (*fmt_ch != '\0') { - switch (*fmt_ch) { - case '%': - BT_ASSERT(fmt_ch[1] != '\0'); - - if (fmt_ch[1] == intro) { - handle_specifier(priv_data, &buf_ch, - buf_size - (size_t) (buf_ch - buf), - &fmt_ch, args); - } else { - handle_conversion_specifier_std(buf, &buf_ch, - buf_size, &fmt_ch, args); - } - - if (buf_ch >= buf + buf_size - 1) { - fmt_ch = ""; - } - break; - default: - *buf_ch = *fmt_ch; - buf_ch++; - if (buf_ch >= buf + buf_size - 1) { - fmt_ch = ""; - } - - fmt_ch++; - } - } - - *buf_ch = '\0'; -} - -BT_HIDDEN -void bt_common_custom_snprintf(char *buf, size_t buf_size, - char intro, - bt_common_handle_custom_specifier_func handle_specifier, - void *priv_data, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier, - priv_data, fmt, &args); - va_end(args); -} diff --git a/common/logging.c b/common/logging.c deleted file mode 100644 index 234ee628..00000000 --- a/common/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_common_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_common_log_level, "BABELTRACE_COMMON_LOG_LEVEL"); diff --git a/common/logging.h b/common/logging.h deleted file mode 100644 index a7029dc5..00000000 --- a/common/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef COMMON_LOGGING_H -#define COMMON_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_common_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_common_log_level); - -#endif /* COMMON_LOGGING_H */ diff --git a/compat/Makefile.am b/compat/Makefile.am deleted file mode 100644 index 91a34842..00000000 --- a/compat/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -noinst_LTLIBRARIES = libcompat.la - -libcompat_la_SOURCES = compat_uuid.c \ - compat_mman.c \ - logging.c logging.h - -libcompat_la_LDFLAGS = \ - $(LD_NO_AS_NEEDED) - -if BABELTRACE_BUILD_WITH_MINGW -libcompat_la_LDFLAGS += -lrpcrt4 -endif diff --git a/compat/compat_mman.c b/compat/compat_mman.c deleted file mode 100644 index bef80d48..00000000 --- a/compat/compat_mman.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * compat/compat_mman.h - * - * Copyright (C) 2013 JP Ikaheimonen - * 2016 Michael Jeanson - * - * These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c - * file by Doug Lea, released to the public domain. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMPAT-MMAN" -#include "logging.h" - -#ifdef __APPLE__ -/* - * On macOS, we need a dummy symbol so that the linker won't - * complain of an empty table of contents. - */ -BT_HIDDEN -int bt_mman_dummy_symbol; -#endif /* __APPLE__ */ - -#ifdef __MINGW32__ - -#include -#include -#include -#include -#include -#include - -struct mmap_mapping { - /* The duplicated handle. */ - HANDLE file_handle; - /* Handle returned by CreateFileMapping. */ - HANDLE map_handle; -}; - -static -GHashTable *mmap_mappings = NULL; - -/* - * This mutex protects the hashtable of memory mappings. - */ -static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; - -static -struct mmap_mapping *mapping_create(void) -{ - struct mmap_mapping *mapping; - - mapping = malloc(sizeof(struct mmap_mapping)); - if (mapping != NULL) { - mapping->file_handle = NULL; - mapping->map_handle = NULL; - } - - return mapping; -} - -static -void mapping_clean(struct mmap_mapping *mapping) -{ - if (mapping) { - if (!CloseHandle(mapping->map_handle)) { - BT_LOGF_STR("Failed to close mmap map_handle."); - abort(); - } - if (!CloseHandle(mapping->file_handle)) { - BT_LOGF_STR("Failed to close mmap file_handle."); - abort(); - } - free(mapping); - mapping = NULL; - } -} - -static -void addr_clean(void *addr) -{ - /* Cleanup of handles should never fail. */ - if (!UnmapViewOfFile(addr)) { - BT_LOGF_STR("Failed to unmap mmap mapping."); - abort(); - } -} - -static -void mmap_lock(void) -{ - if (pthread_mutex_lock(&mmap_mutex)) { - BT_LOGF_STR("Failed to acquire mmap_mutex."); - abort(); - } -} - -static -void mmap_unlock(void) -{ - if (pthread_mutex_unlock(&mmap_mutex)) { - BT_LOGF_STR("Failed to release mmap_mutex."); - abort(); - } -} - -/* - * Convert mmap memory protection flags to CreateFileMapping page protection - * flag and MapViewOfFile desired access flag. - */ -static -DWORD map_prot_flags(int prot, DWORD *dwDesiredAccess) -{ - if (prot & PROT_READ) { - if (prot & PROT_WRITE) { - *dwDesiredAccess = FILE_MAP_WRITE; - if (prot & PROT_EXEC) { - return PAGE_EXECUTE_READWRITE; - } - return PAGE_READWRITE; - } - if (prot & PROT_EXEC) { - *dwDesiredAccess = FILE_MAP_EXECUTE; - return PAGE_EXECUTE_READ; - } - *dwDesiredAccess = FILE_MAP_READ; - return PAGE_READONLY; - } - if (prot & PROT_WRITE) { - *dwDesiredAccess = FILE_MAP_COPY; - return PAGE_WRITECOPY; - } - if (prot & PROT_EXEC) { - *dwDesiredAccess = FILE_MAP_EXECUTE; - return PAGE_EXECUTE_READ; - } - - /* Mapping failed. */ - *dwDesiredAccess = 0; - return 0; -} - -BT_HIDDEN -void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, - off_t offset) -{ - struct mmap_mapping *mapping = NULL; - void *mapping_addr; - DWORD dwDesiredAccess; - DWORD flProtect; - HANDLE handle; - - /* Check for a valid fd. */ - if (fd == -1) { - _set_errno(EBADF); - goto error; - } - - /* We don't support this at the moment. */ - if (flags == MAP_FIXED) { - _set_errno(ENOTSUP); - goto error; - } - - /* Map mmap flags to those of the Windows API. */ - flProtect = map_prot_flags(prot, &dwDesiredAccess); - if (flProtect == 0) { - _set_errno(EINVAL); - goto error; - } - - /* Allocate the mapping struct. */ - mapping = mapping_create(); - if (!mapping) { - BT_LOGE_STR("Failed to allocate mmap mapping."); - _set_errno(ENOMEM); - goto error; - } - - /* Get a handle from the fd. */ - handle = (HANDLE) _get_osfhandle(fd); - - /* Duplicate the handle and store it in 'mapping.file_handle'. */ - if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), - &mapping->file_handle, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - _set_errno(ENOMEM); - goto error; - } - - /* - * Create a file mapping object with a maximum size - * of 'offset' + 'length'. - */ - mapping->map_handle = CreateFileMapping(mapping->file_handle, NULL, - flProtect, 0, offset + length, NULL); - if (mapping->map_handle == 0) { - _set_errno(EACCES); - goto error; - } - - /* Map the requested block starting at 'offset' for 'length' bytes. */ - mapping_addr = MapViewOfFile(mapping->map_handle, dwDesiredAccess, 0, - offset, length); - if (mapping_addr == 0) { - DWORD dwLastErr = GetLastError(); - if (dwLastErr == ERROR_MAPPED_ALIGNMENT) { - _set_errno(EINVAL); - } else { - _set_errno(EACCES); - } - goto error; - } - - mmap_lock(); - - /* If we have never done any mappings, allocate the hashtable. */ - if (!mmap_mappings) { - mmap_mappings = g_hash_table_new_full(g_direct_hash, - g_direct_equal, (GDestroyNotify) addr_clean, - (GDestroyNotify) mapping_clean); - if (!mmap_mappings) { - BT_LOGE_STR("Failed to allocate mmap hashtable."); - _set_errno(ENOMEM); - goto error_mutex_unlock; - } - } - - /* Add the new mapping to the hashtable. */ - g_hash_table_insert(mmap_mappings, mapping_addr, mapping); - - mmap_unlock(); - - return mapping_addr; - -error_mutex_unlock: - mmap_unlock(); -error: - mapping_clean(mapping); - return MAP_FAILED; -} - -BT_HIDDEN -int bt_munmap(void *addr, size_t length) -{ - int ret = 0; - - mmap_lock(); - - /* Check if the mapping exists in the hashtable. */ - if (g_hash_table_lookup(mmap_mappings, addr) == NULL) { - _set_errno(EINVAL); - ret = -1; - goto end; - } - - /* Remove it. */ - if (!g_hash_table_remove(mmap_mappings, addr)) { - BT_LOGF_STR("Failed to remove mapping from hashtable."); - abort(); - } - -end: - mmap_unlock(); - return ret; -} - -#endif diff --git a/compat/compat_uuid.c b/compat/compat_uuid.c deleted file mode 100644 index b367ec13..00000000 --- a/compat/compat_uuid.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * compat/compat_uuid.h - * - * Copyright (C) 2013 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMPAT-UUID" -#include "logging.h" - -#ifdef __APPLE__ -/* - * On macOS, we need a dummy symbol so that the linker won't - * complain of an empty table of contents. - */ -BT_HIDDEN -int bt_uuid_dummy_symbol; -#endif /* __APPLE__ */ - -#ifdef __MINGW32__ - -#include -#include -#include - -/* MinGW does not provide byteswap - implement our own version. */ -static -void swap(unsigned char *ptr, unsigned int i, unsigned int j) -{ - unsigned char tmp; - - tmp = ptr[i]; - ptr[i] = ptr[j]; - ptr[j] = tmp; -} - -static -void fix_uuid_endian(unsigned char * ptr) -{ - swap(ptr, 0, 3); - swap(ptr, 1, 2); - swap(ptr, 4, 5); - swap(ptr, 6, 7); -} - -int bt_uuid_generate(unsigned char *uuid_out) -{ - RPC_STATUS status; - - status = UuidCreate((UUID *) uuid_out); - if (status == RPC_S_OK) - return 0; - else - return -1; -} - -int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) -{ - RPC_STATUS status; - unsigned char *alloc_str; - int ret; - unsigned char copy_of_uuid_in[BABELTRACE_UUID_LEN]; - - /* make a modifyable copy of uuid_in */ - memcpy(copy_of_uuid_in, uuid_in, BABELTRACE_UUID_LEN); - - fix_uuid_endian(copy_of_uuid_in); - status = UuidToString((UUID *) copy_of_uuid_in, &alloc_str); - - if (status == RPC_S_OK) { - strncpy(str_out, (char *) alloc_str, BABELTRACE_UUID_STR_LEN); - str_out[BABELTRACE_UUID_STR_LEN - 1] = '\0'; - ret = 0; - } else { - ret = -1; - } - RpcStringFree(&alloc_str); - return ret; -} - -int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) -{ - RPC_STATUS status; - - status = UuidFromString((unsigned char *) str_in, - (UUID *) uuid_out); - fix_uuid_endian(uuid_out); - - if (status == RPC_S_OK) - return 0; - else - return -1; -} - -int bt_uuid_compare(const unsigned char *uuid_a, - const unsigned char *uuid_b) -{ - RPC_STATUS status; - - return !UuidCompare((UUID *) uuid_a, (UUID *) uuid_b, &status) ? 0 : -1; -} - -#endif diff --git a/compat/logging.c b/compat/logging.c deleted file mode 100644 index 1a359525..00000000 --- a/compat/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_compat_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_compat_log_level, "BABELTRACE_COMPAT_LOG_LEVEL"); diff --git a/compat/logging.h b/compat/logging.h deleted file mode 100644 index 6e4b5d26..00000000 --- a/compat/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef COMPAT_LOGGING_H -#define COMPAT_LOGGING_H - -/* - * Copyright (c) 2017 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_compat_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_compat_log_level); - -#endif /* COMPAT_LOGGING_H */ diff --git a/configure.ac b/configure.ac index b4f1e915..449df8d2 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,7 @@ m4_define([bt_lib_version], bt_lib_version_current[:]bt_lib_version_revision[:]b AC_SUBST([BABELTRACE_LIBRARY_VERSION], bt_lib_version) -AC_CONFIG_HEADERS([include/config.h]) +AC_CONFIG_HEADERS([src/common/config.h]) AC_CONFIG_AUX_DIR([config]) AC_CONFIG_MACRO_DIR([m4]) AC_REQUIRE_AUX_FILE([tap-driver.sh]) @@ -684,7 +684,7 @@ AM_CFLAGS="-Wall -Wformat -Werror=implicit-function-declaration $PTHREAD_CFLAGS AC_SUBST(AM_CFLAGS) # Set global CPPFLAGS in AM_CPPFLAGS -AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -include config.h" +AM_CPPFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/src -I\$(top_srcdir)/src -include common/config.h" AC_SUBST(AM_CPPFLAGS) # Add glib to global link libs @@ -725,64 +725,65 @@ program_transform_name="s&babeltrace2\.bin&babeltrace2&;s&babeltrace2-log\.bin&b AC_SUBST(program_transform_name) AC_CONFIG_FILES([ - Makefile - bindings/python/Makefile - bindings/python/bt2/Makefile - bindings/python/bt2/setup.py - bindings/python/bt2/bt2/__init__.py - common/Makefile - ctfser/Makefile - ctf-writer/Makefile - compat/Makefile - cli/Makefile - doc/Makefile - doc/api/Makefile doc/api/Doxyfile + doc/api/Makefile doc/bindings/Makefile doc/bindings/python/Makefile doc/contributing-images/Makefile - doc/man/Makefile - doc/man/asciidoc-attrs.conf - fd-cache/Makefile - lib/Makefile - lib/prio_heap/Makefile - lib/plugin/Makefile - lib/graph/Makefile - lib/graph/message/Makefile - lib/trace-ir/Makefile + doc/Makefile + doc/man/asciidoc-attrs.conf + doc/man/Makefile include/Makefile - logging/Makefile - bindings/Makefile - tests/Makefile + Makefile + src/babeltrace2-ctf-writer.pc + src/babeltrace2.pc + src/bindings/Makefile + src/bindings/python/bt2/bt2/__init__.py + src/bindings/python/bt2/Makefile + src/bindings/python/bt2/setup.py + src/bindings/python/Makefile + src/cli/Makefile + src/common/Makefile + src/compat/Makefile + src/ctfser/Makefile + src/ctf-writer/Makefile + src/fd-cache/Makefile + src/lib/graph/Makefile + src/lib/graph/message/Makefile + src/lib/Makefile + src/lib/plugin/Makefile + src/lib/prio-heap/Makefile + src/lib/trace-ir/Makefile + src/logging/Makefile + src/Makefile + src/plugins/ctf/common/bfcr/Makefile + src/plugins/ctf/common/Makefile + src/plugins/ctf/common/metadata/Makefile + src/plugins/ctf/common/msg-iter/Makefile + src/plugins/ctf/common/utils/Makefile + src/plugins/ctf/fs-sink/Makefile + src/plugins/ctf/fs-src/Makefile + src/plugins/ctf/lttng-live/Makefile + src/plugins/ctf/Makefile + src/plugins/lttng-utils/debug-info/Makefile + src/plugins/lttng-utils/Makefile + src/plugins/Makefile + src/plugins/text/dmesg/Makefile + src/plugins/text/Makefile + src/plugins/text/pretty/Makefile + src/plugins/utils/counter/Makefile + src/plugins/utils/dummy/Makefile + src/plugins/utils/Makefile + src/plugins/utils/muxer/Makefile + src/plugins/utils/trimmer/Makefile + src/python-plugin-provider/Makefile tests/lib/Makefile tests/lib/test-plugin-plugins/Makefile + tests/Makefile + tests/plugins/Makefile tests/utils/common.sh tests/utils/Makefile tests/utils/tap/Makefile - tests/plugins/Makefile - plugins/Makefile - plugins/ctf/Makefile - plugins/ctf/common/Makefile - plugins/ctf/common/bfcr/Makefile - plugins/ctf/common/metadata/Makefile - plugins/ctf/common/msg-iter/Makefile - plugins/ctf/common/utils/Makefile - plugins/ctf/fs-src/Makefile - plugins/ctf/fs-sink/Makefile - plugins/ctf/lttng-live/Makefile - plugins/text/Makefile - plugins/text/dmesg/Makefile - plugins/text/pretty/Makefile - plugins/utils/Makefile - plugins/utils/dummy/Makefile - plugins/utils/counter/Makefile - plugins/utils/trimmer/Makefile - plugins/utils/muxer/Makefile - python-plugin-provider/Makefile - plugins/lttng-utils/Makefile - plugins/lttng-utils/debug-info/Makefile - babeltrace2.pc - babeltrace2-ctf-writer.pc ]) AC_CONFIG_FILES([tests/cli/test_intersection], [chmod +x tests/cli/test_intersection]) diff --git a/ctf-writer/Makefile.am b/ctf-writer/Makefile.am deleted file mode 100644 index 7c5d046e..00000000 --- a/ctf-writer/Makefile.am +++ /dev/null @@ -1,36 +0,0 @@ -lib_LTLIBRARIES = libbabeltrace2-ctf-writer.la - -libbabeltrace2_ctf_writer_la_SOURCES = \ - attributes.c \ - clock.c \ - clock-class.c \ - event.c \ - event-class.c \ - field-path.c \ - fields.c \ - field-types.c \ - field-wrapper.c \ - functor.c \ - logging.c \ - logging.h \ - object.c \ - object-pool.c \ - resolve.c \ - stream.c \ - stream-class.c \ - trace.c \ - utils.c \ - validation.c \ - values.c \ - visitor.c \ - writer.c - -libbabeltrace2_ctf_writer_la_LDFLAGS = $(LT_NO_UNDEFINED) \ - -version-info $(BABELTRACE_LIBRARY_VERSION) - -libbabeltrace2_ctf_writer_la_LIBADD = \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/ctfser/libbabeltrace2-ctfser.la \ - $(top_builddir)/compat/libcompat.la \ - $(UUID_LIBS) diff --git a/ctf-writer/attributes.c b/ctf-writer/attributes.c deleted file mode 100644 index fbb81a03..00000000 --- a/ctf-writer/attributes.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * attributes.c - * - * Babeltrace CTF writer - Attributes - * - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-ATTRS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_CTF_ATTR_NAME_INDEX 0 -#define BT_CTF_ATTR_VALUE_INDEX 1 - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_create(void) -{ - struct bt_ctf_private_value *attr_obj; - - /* - * Attributes: array value object of array value objects, each one - * containing two entries: a string value object (attributes - * field name), and a value object (attributes field value). - * - * Example (JSON representation): - * - * [ - * ["hostname", "eeppdesk"], - * ["sysname", "Linux"], - * ["tracer_major", 2], - * ["tracer_minor", 5] - * ] - */ - BT_LOGD_STR("Creating attributes object."); - attr_obj = bt_ctf_private_value_array_create(); - if (!attr_obj) { - BT_LOGE_STR("Failed to create array value."); - } else { - BT_LOGD("Created attributes object: addr=%p", - attr_obj); - } - - return attr_obj; -} - -BT_HIDDEN -void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj) -{ - BT_LOGD("Destroying attributes object: addr=%p", attr_obj); - bt_ctf_object_put_ref(attr_obj); -} - -BT_HIDDEN -int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj) -{ - return bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); -} - -BT_HIDDEN -const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj, - uint64_t index) -{ - const char *ret = NULL; - struct bt_ctf_private_value *attr_field_obj = NULL; - struct bt_ctf_private_value *attr_field_name_obj = NULL; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - goto end; - } - - if (index >= bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "index=%" PRIu64 ", count=%" PRId64, - index, bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))); - goto end; - } - - attr_field_obj = bt_ctf_private_value_array_borrow_element_by_index( - attr_obj, index); - if (!attr_field_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, index); - goto end; - } - - attr_field_name_obj = - bt_ctf_private_value_array_borrow_element_by_index( - attr_field_obj, BT_CTF_ATTR_NAME_INDEX); - if (!attr_field_name_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_CTF_ATTR_NAME_INDEX); - goto end; - } - - ret = bt_ctf_value_string_get( - bt_ctf_private_value_as_value(attr_field_name_obj)); - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj, - uint64_t index) -{ - struct bt_ctf_private_value *value_obj = NULL; - struct bt_ctf_private_value *attr_field_obj = NULL; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - goto end; - } - - if (index >= bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "index=%" PRIu64 ", count=%" PRId64, - index, bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))); - goto end; - } - - attr_field_obj = bt_ctf_private_value_array_borrow_element_by_index( - attr_obj, index); - if (!attr_field_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, index); - goto end; - } - - value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_field_obj, - BT_CTF_ATTR_VALUE_INDEX); - if (!value_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_CTF_ATTR_VALUE_INDEX); - } - -end: - return value_obj; -} - -static -struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_by_name( - struct bt_ctf_private_value *attr_obj, const char *name) -{ - uint64_t i; - int64_t attr_size; - struct bt_ctf_private_value *value_obj = NULL; - struct bt_ctf_private_value *attr_field_name_obj = NULL; - - attr_size = bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); - if (attr_size < 0) { - BT_LOGE("Cannot get array value's size: value-addr=%p", - attr_obj); - goto error; - } - - for (i = 0; i < attr_size; ++i) { - const char *field_name; - - value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_obj, i); - if (!value_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, i); - goto error; - } - - attr_field_name_obj = bt_ctf_private_value_array_borrow_element_by_index(value_obj, - BT_CTF_ATTR_NAME_INDEX); - if (!attr_field_name_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, - value_obj, (int64_t) BT_CTF_ATTR_NAME_INDEX); - goto error; - } - - field_name = bt_ctf_value_string_get( - bt_ctf_private_value_as_value(attr_field_name_obj)); - - if (!strcmp(field_name, name)) { - break; - } - - value_obj = NULL; - } - - return value_obj; - -error: - value_obj = NULL; - return value_obj; -} - -BT_HIDDEN -int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj, - const char *name, struct bt_ctf_private_value *value_obj) -{ - int ret = 0; - struct bt_ctf_private_value *attr_field_obj = NULL; - - if (!attr_obj || !name || !value_obj) { - BT_LOGW("Invalid parameter: attributes object, name, or value object is NULL: " - "attr-value-addr=%p, name-addr=%p, value-addr=%p", - attr_obj, name, value_obj); - ret = -1; - goto end; - } - - attr_field_obj = bt_ctf_attributes_borrow_field_by_name(attr_obj, name); - if (attr_field_obj) { - ret = bt_ctf_private_value_array_set_element_by_index( - attr_field_obj, BT_CTF_ATTR_VALUE_INDEX, - bt_ctf_private_value_as_value(value_obj)); - attr_field_obj = NULL; - goto end; - } - - attr_field_obj = bt_ctf_private_value_array_create(); - if (!attr_field_obj) { - BT_LOGE_STR("Failed to create empty array value."); - ret = -1; - goto end; - } - - ret = bt_ctf_private_value_array_append_string_element(attr_field_obj, name); - ret |= bt_ctf_private_value_array_append_element(attr_field_obj, - bt_ctf_private_value_as_value(value_obj)); - if (ret) { - BT_LOGE("Cannot append elements to array value: addr=%p", - attr_field_obj); - goto end; - } - - ret = bt_ctf_private_value_array_append_element(attr_obj, - bt_ctf_private_value_as_value(attr_field_obj)); - if (ret) { - BT_LOGE("Cannot append element to array value: " - "array-value-addr=%p, element-value-addr=%p", - attr_obj, attr_field_obj); - } - -end: - bt_ctf_object_put_ref(attr_field_obj); - return ret; -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name( - struct bt_ctf_private_value *attr_obj, const char *name) -{ - struct bt_ctf_private_value *value_obj = NULL; - struct bt_ctf_private_value *attr_field_obj = NULL; - - if (!attr_obj || !name) { - BT_LOGW("Invalid parameter: attributes object or name is NULL: " - "value-addr=%p, name-addr=%p", attr_obj, name); - goto end; - } - - attr_field_obj = bt_ctf_attributes_borrow_field_by_name(attr_obj, name); - if (!attr_field_obj) { - BT_LOGD("Cannot find attributes object's field by name: " - "value-addr=%p, name=\"%s\"", attr_obj, name); - goto end; - } - - value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_field_obj, - BT_CTF_ATTR_VALUE_INDEX); - if (!value_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_CTF_ATTR_VALUE_INDEX); - } - -end: - return value_obj; -} - -BT_HIDDEN -int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj) -{ - uint64_t i; - int64_t count; - int ret = 0; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - ret = -1; - goto end; - } - - BT_LOGD("Freezing attributes object: value-addr=%p", attr_obj); - count = bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); - BT_ASSERT(count >= 0); - - /* - * We do not freeze the array value object itself here, since - * internal stuff could need to modify/add attributes. Each - * attribute is frozen one by one. - */ - for (i = 0; i < count; ++i) { - struct bt_ctf_private_value *obj = NULL; - - obj = bt_ctf_attributes_borrow_field_value(attr_obj, i); - if (!obj) { - BT_LOGE("Cannot get attributes object's field value by index: " - "value-addr=%p, index=%" PRIu64, - attr_obj, i); - ret = -1; - goto end; - } - - bt_ctf_value_freeze(bt_ctf_private_value_as_value(obj)); - } - -end: - return ret; -} diff --git a/ctf-writer/clock-class.c b/ctf-writer/clock-class.c deleted file mode 100644 index 1c507e37..00000000 --- a/ctf-writer/clock-class.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * clock-class.c - * - * Babeltrace CTF writer - Clock class - * - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-CLOCK-CLASS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj); - -BT_HIDDEN -bt_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class) -{ - return clock_class && clock_class->name; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, - const char *name) -{ - int ret = 0; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - if (!bt_ctf_identifier_is_valid(name)) { - BT_LOGW("Clock class's name is not a valid CTF identifier: " - "addr=%p, name=\"%s\"", - clock_class, name); - ret = -1; - goto end; - } - - if (clock_class->name) { - g_string_assign(clock_class->name, name); - } else { - clock_class->name = g_string_new(name); - if (!clock_class->name) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = -1; - goto end; - } - } - - BT_LOGV("Set clock class's name: addr=%p, name=\"%s\"", - clock_class, name); - -end: - return ret; -} - -static -bool validate_freq(struct bt_ctf_clock_class *clock_class, - const char *name, uint64_t freq) -{ - bool is_valid = true; - - if (freq == -1ULL || freq == 0) { - BT_LOGW("Invalid parameter: frequency is invalid: " - "addr=%p, name=\"%s\", freq=%" PRIu64, - clock_class, name, freq); - is_valid = false; - goto end; - } - -end: - return is_valid; -} - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name, - uint64_t freq) -{ - int ret; - struct bt_ctf_clock_class *clock_class = NULL; - - BT_LOGD("Creating default clock class object: name=\"%s\"", - name); - - if (!validate_freq(NULL, name, freq)) { - /* validate_freq() logs errors */ - goto error; - } - - clock_class = g_new0(struct bt_ctf_clock_class, 1); - if (!clock_class) { - BT_LOGE_STR("Failed to allocate one clock class."); - goto error; - } - - clock_class->precision = 1; - clock_class->frequency = freq; - bt_ctf_object_init_shared(&clock_class->base, bt_ctf_clock_class_destroy); - - if (name) { - ret = bt_ctf_clock_class_set_name(clock_class, name); - if (ret) { - /* bt_ctf_clock_class_set_name() logs errors */ - goto error; - } - } - - BT_LOGD("Created clock class object: addr=%p, name=\"%s\"", - clock_class, name); - return clock_class; -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(clock_class); - return clock_class; -} - -BT_HIDDEN -const char *bt_ctf_clock_class_get_name(struct bt_ctf_clock_class *clock_class) -{ - const char *ret = NULL; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - goto end; - } - - if (clock_class->name) { - ret = clock_class->name->str; - } - -end: - return ret; -} - -BT_HIDDEN -const char *bt_ctf_clock_class_get_description( - struct bt_ctf_clock_class *clock_class) -{ - const char *ret = NULL; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - goto end; - } - - if (clock_class->description) { - ret = clock_class->description->str; - } -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_description(struct bt_ctf_clock_class *clock_class, - const char *desc) -{ - int ret = 0; - - if (!clock_class || !desc) { - BT_LOGW("Invalid parameter: clock class or description is NULL: " - "clock-class-addr=%p, name=\"%s\", desc-addr=%p", - clock_class, bt_ctf_clock_class_get_name(clock_class), - desc); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->description = g_string_new(desc); - ret = clock_class->description ? 0 : -1; - BT_LOGV("Set clock class's description: addr=%p, " - "name=\"%s\", desc=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class), desc); -end: - return ret; -} - -BT_HIDDEN -uint64_t bt_ctf_clock_class_get_frequency( - struct bt_ctf_clock_class *clock_class) -{ - uint64_t ret = -1ULL; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - goto end; - } - - ret = clock_class->frequency; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_frequency(struct bt_ctf_clock_class *clock_class, - uint64_t freq) -{ - int ret = 0; - - if (!clock_class) { - BT_LOGW("Invalid parameter: clock class is NULL or frequency is invalid: " - "addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - if (!validate_freq(clock_class, bt_ctf_clock_class_get_name(clock_class), - freq)) { - /* validate_freq() logs errors */ - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->frequency = freq; - BT_LOGV("Set clock class's frequency: addr=%p, name=\"%s\", freq=%" PRIu64, - clock_class, bt_ctf_clock_class_get_name(clock_class), freq); -end: - return ret; -} - -BT_HIDDEN -uint64_t bt_ctf_clock_class_get_precision(struct bt_ctf_clock_class *clock_class) -{ - uint64_t ret = -1ULL; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - goto end; - } - - ret = clock_class->precision; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_precision(struct bt_ctf_clock_class *clock_class, - uint64_t precision) -{ - int ret = 0; - - if (!clock_class || precision == -1ULL) { - BT_LOGW("Invalid parameter: clock class is NULL or precision is invalid: " - "addr=%p, name=\"%s\", precision=%" PRIu64, - clock_class, bt_ctf_clock_class_get_name(clock_class), - precision); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->precision = precision; - BT_LOGV("Set clock class's precision: addr=%p, name=\"%s\", precision=%" PRIu64, - clock_class, bt_ctf_clock_class_get_name(clock_class), - precision); -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_get_offset_s(struct bt_ctf_clock_class *clock_class, - int64_t *offset_s) -{ - int ret = 0; - - if (!clock_class || !offset_s) { - BT_LOGW("Invalid parameter: clock class or offset pointer is NULL: " - "clock-class-addr=%p, name=\"%s\", offset-addr=%p", - clock_class, bt_ctf_clock_class_get_name(clock_class), - offset_s); - ret = -1; - goto end; - } - - *offset_s = clock_class->offset_s; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_offset_s(struct bt_ctf_clock_class *clock_class, - int64_t offset_s) -{ - int ret = 0; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->offset_s = offset_s; - BT_LOGV("Set clock class's offset (seconds): " - "addr=%p, name=\"%s\", offset-s=%" PRId64, - clock_class, bt_ctf_clock_class_get_name(clock_class), - offset_s); -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_get_offset_cycles(struct bt_ctf_clock_class *clock_class, - int64_t *offset) -{ - int ret = 0; - - if (!clock_class || !offset) { - BT_LOGW("Invalid parameter: clock class or offset pointer is NULL: " - "clock-class-addr=%p, name=\"%s\", offset-addr=%p", - clock_class, bt_ctf_clock_class_get_name(clock_class), - offset); - ret = -1; - goto end; - } - - *offset = clock_class->offset; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_offset_cycles(struct bt_ctf_clock_class *clock_class, - int64_t offset) -{ - int ret = 0; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->offset = offset; - BT_LOGV("Set clock class's offset (cycles): addr=%p, name=\"%s\", offset-cycles=%" PRId64, - clock_class, bt_ctf_clock_class_get_name(clock_class), offset); -end: - return ret; -} - -BT_HIDDEN -bt_bool bt_ctf_clock_class_is_absolute(struct bt_ctf_clock_class *clock_class) -{ - int ret = -1; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - goto end; - } - - ret = clock_class->absolute; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_is_absolute(struct bt_ctf_clock_class *clock_class, - bt_bool is_absolute) -{ - int ret = 0; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - clock_class->absolute = !!is_absolute; - BT_LOGV("Set clock class's absolute flag: addr=%p, name=\"%s\", is-absolute=%d", - clock_class, bt_ctf_clock_class_get_name(clock_class), - is_absolute); -end: - return ret; -} - -BT_HIDDEN -const unsigned char *bt_ctf_clock_class_get_uuid( - struct bt_ctf_clock_class *clock_class) -{ - const unsigned char *ret; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = NULL; - goto end; - } - - if (!clock_class->uuid_set) { - BT_LOGV("Clock class's UUID is not set: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = NULL; - goto end; - } - - ret = clock_class->uuid; -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class, - const unsigned char *uuid) -{ - int ret = 0; - - if (!clock_class || !uuid) { - BT_LOGW("Invalid parameter: clock class or UUID is NULL: " - "clock-class-addr=%p, name=\"%s\", uuid-addr=%p", - clock_class, bt_ctf_clock_class_get_name(clock_class), - uuid); - ret = -1; - goto end; - } - - if (clock_class->frozen) { - BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - memcpy(clock_class->uuid, uuid, BABELTRACE_UUID_LEN); - clock_class->uuid_set = 1; - BT_LOGV("Set clock class's UUID: addr=%p, name=\"%s\", " - "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - clock_class, bt_ctf_clock_class_get_name(clock_class), - (unsigned int) uuid[0], - (unsigned int) uuid[1], - (unsigned int) uuid[2], - (unsigned int) uuid[3], - (unsigned int) uuid[4], - (unsigned int) uuid[5], - (unsigned int) uuid[6], - (unsigned int) uuid[7], - (unsigned int) uuid[8], - (unsigned int) uuid[9], - (unsigned int) uuid[10], - (unsigned int) uuid[11], - (unsigned int) uuid[12], - (unsigned int) uuid[13], - (unsigned int) uuid[14], - (unsigned int) uuid[15]); -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class) -{ - if (!clock_class || clock_class->frozen) { - return; - } - - BT_LOGD("Freezing clock class: addr=%p, name=\"%s\"", - clock_class, bt_ctf_clock_class_get_name(clock_class)); - clock_class->frozen = 1; -} - -static -void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_clock_class *clock_class; - - clock_class = container_of(obj, struct bt_ctf_clock_class, base); - BT_LOGD("Destroying clock class: addr=%p, name=\"%s\"", - obj, bt_ctf_clock_class_get_name(clock_class)); - - if (clock_class->name) { - g_string_free(clock_class->name, TRUE); - } - - if (clock_class->description) { - g_string_free(clock_class->description, TRUE); - } - - g_free(clock_class); -} - -BT_HIDDEN -int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a, - struct bt_ctf_clock_class *clock_class_b) -{ - int ret = 1; - BT_ASSERT(clock_class_a); - BT_ASSERT(clock_class_b); - - /* Name */ - if (strcmp(clock_class_a->name->str, clock_class_b->name->str) != 0) { - BT_LOGV("Clock classes differ: different names: " - "cc-a-name=\"%s\", cc-b-name=\"%s\"", - clock_class_a->name->str, - clock_class_b->name->str); - goto end; - } - - /* Description */ - if (clock_class_a->description) { - if (!clock_class_b->description) { - BT_LOGV_STR("Clock classes differ: clock class A has a " - "description, but clock class B does not."); - goto end; - } - - if (strcmp(clock_class_a->name->str, clock_class_b->name->str) - != 0) { - BT_LOGV("Clock classes differ: different descriptions: " - "cc-a-descr=\"%s\", cc-b-descr=\"%s\"", - clock_class_a->description->str, - clock_class_b->description->str); - goto end; - } - } else { - if (clock_class_b->description) { - BT_LOGV_STR("Clock classes differ: clock class A has " - "no description, but clock class B has one."); - goto end; - } - } - - /* Frequency */ - if (clock_class_a->frequency != clock_class_b->frequency) { - BT_LOGV("Clock classes differ: different frequencies: " - "cc-a-freq=%" PRIu64 ", cc-b-freq=%" PRIu64, - clock_class_a->frequency, - clock_class_b->frequency); - goto end; - } - - /* Precision */ - if (clock_class_a->precision != clock_class_b->precision) { - BT_LOGV("Clock classes differ: different precisions: " - "cc-a-freq=%" PRIu64 ", cc-b-freq=%" PRIu64, - clock_class_a->precision, - clock_class_b->precision); - goto end; - } - - /* Offset (seconds) */ - if (clock_class_a->offset_s != clock_class_b->offset_s) { - BT_LOGV("Clock classes differ: different offsets (seconds): " - "cc-a-offset-s=%" PRId64 ", cc-b-offset-s=%" PRId64, - clock_class_a->offset_s, - clock_class_b->offset_s); - goto end; - } - - /* Offset (cycles) */ - if (clock_class_a->offset != clock_class_b->offset) { - BT_LOGV("Clock classes differ: different offsets (cycles): " - "cc-a-offset-s=%" PRId64 ", cc-b-offset-s=%" PRId64, - clock_class_a->offset, - clock_class_b->offset); - goto end; - } - - /* UUIDs */ - if (clock_class_a->uuid_set) { - if (!clock_class_b->uuid_set) { - BT_LOGV_STR("Clock classes differ: clock class A has a " - "UUID, but clock class B does not."); - goto end; - } - - if (memcmp(clock_class_a->uuid, clock_class_b->uuid, - BABELTRACE_UUID_LEN) != 0) { - BT_LOGV("Clock classes differ: different UUIDs: " - "cc-a-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " - "cc-b-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - (unsigned int) clock_class_a->uuid[0], - (unsigned int) clock_class_a->uuid[1], - (unsigned int) clock_class_a->uuid[2], - (unsigned int) clock_class_a->uuid[3], - (unsigned int) clock_class_a->uuid[4], - (unsigned int) clock_class_a->uuid[5], - (unsigned int) clock_class_a->uuid[6], - (unsigned int) clock_class_a->uuid[7], - (unsigned int) clock_class_a->uuid[8], - (unsigned int) clock_class_a->uuid[9], - (unsigned int) clock_class_a->uuid[10], - (unsigned int) clock_class_a->uuid[11], - (unsigned int) clock_class_a->uuid[12], - (unsigned int) clock_class_a->uuid[13], - (unsigned int) clock_class_a->uuid[14], - (unsigned int) clock_class_a->uuid[15], - (unsigned int) clock_class_b->uuid[0], - (unsigned int) clock_class_b->uuid[1], - (unsigned int) clock_class_b->uuid[2], - (unsigned int) clock_class_b->uuid[3], - (unsigned int) clock_class_b->uuid[4], - (unsigned int) clock_class_b->uuid[5], - (unsigned int) clock_class_b->uuid[6], - (unsigned int) clock_class_b->uuid[7], - (unsigned int) clock_class_b->uuid[8], - (unsigned int) clock_class_b->uuid[9], - (unsigned int) clock_class_b->uuid[10], - (unsigned int) clock_class_b->uuid[11], - (unsigned int) clock_class_b->uuid[12], - (unsigned int) clock_class_b->uuid[13], - (unsigned int) clock_class_b->uuid[14], - (unsigned int) clock_class_b->uuid[15]); - goto end; - } - } else { - if (clock_class_b->uuid_set) { - BT_LOGV_STR("Clock classes differ: clock class A has " - "no UUID, but clock class B has one."); - goto end; - } - } - - /* Absolute */ - if (!!clock_class_a->absolute != !!clock_class_b->absolute) { - BT_LOGV("Clock classes differ: one is absolute, the other " - "is not: cc-a-is-absolute=%d, cc-b-is-absolute=%d", - !!clock_class_a->absolute, - !!clock_class_b->absolute); - goto end; - } - - /* Equal */ - ret = 0; - -end: - return ret; -} diff --git a/ctf-writer/clock.c b/ctf-writer/clock.c deleted file mode 100644 index 0238c25b..00000000 --- a/ctf-writer/clock.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * clock.c - * - * Babeltrace CTF writer - Clock - * - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017 Philippe Proulx - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-CLOCK" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void bt_ctf_clock_destroy(struct bt_ctf_object *obj); - -struct bt_ctf_clock *bt_ctf_clock_create(const char *name) -{ - int ret; - struct bt_ctf_clock *clock = NULL; - unsigned char cc_uuid[BABELTRACE_UUID_LEN]; - - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - clock = g_new0(struct bt_ctf_clock, 1); - if (!clock) { - goto error; - } - - bt_ctf_object_init_shared(&clock->base, bt_ctf_clock_destroy); - clock->value = 0; - - /* Pre-2.0.0 backward compatibility: default frequency is 1 GHz */ - clock->clock_class = (void *) bt_ctf_clock_class_create(name, 1000000000); - if (!clock->clock_class) { - goto error; - } - - /* Automatically set clock class's UUID. */ - ret = bt_uuid_generate(cc_uuid); - if (ret) { - goto error; - } - - ret = bt_ctf_clock_class_set_uuid(clock->clock_class, cc_uuid); - BT_ASSERT(ret == 0); - return clock; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(clock); - return clock; -} - -const char *bt_ctf_clock_get_name(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_name(clock->clock_class); -} - -const char *bt_ctf_clock_get_description(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_description(clock->clock_class); -} - -int bt_ctf_clock_set_description(struct bt_ctf_clock *clock, const char *desc) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_description(clock->clock_class, - desc); -} - -uint64_t bt_ctf_clock_get_frequency(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_frequency(clock->clock_class); -} - -int bt_ctf_clock_set_frequency(struct bt_ctf_clock *clock, uint64_t freq) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_frequency(clock->clock_class, - freq); -} - -uint64_t bt_ctf_clock_get_precision(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_precision(clock->clock_class); -} - -int bt_ctf_clock_set_precision(struct bt_ctf_clock *clock, uint64_t precision) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_precision(clock->clock_class, - precision); -} - -int bt_ctf_clock_get_offset_s(struct bt_ctf_clock *clock, int64_t *offset_s) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_offset_s(clock->clock_class, - offset_s); -} - -int bt_ctf_clock_set_offset_s(struct bt_ctf_clock *clock, int64_t offset_s) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_offset_s(clock->clock_class, - offset_s); -} - -int bt_ctf_clock_get_offset(struct bt_ctf_clock *clock, int64_t *offset) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_offset_cycles(clock->clock_class, - offset); -} - -int bt_ctf_clock_set_offset(struct bt_ctf_clock *clock, int64_t offset) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_offset_cycles(clock->clock_class, - offset); -} - -int bt_ctf_clock_get_is_absolute(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_is_absolute(clock->clock_class); -} - -int bt_ctf_clock_set_is_absolute(struct bt_ctf_clock *clock, int is_absolute) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_is_absolute(clock->clock_class, - is_absolute); -} - -const unsigned char *bt_ctf_clock_get_uuid(struct bt_ctf_clock *clock) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_get_uuid(clock->clock_class); -} - -int bt_ctf_clock_set_uuid(struct bt_ctf_clock *clock, const unsigned char *uuid) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - return bt_ctf_clock_class_set_uuid(clock->clock_class, uuid); -} - -int bt_ctf_clock_set_time(struct bt_ctf_clock *clock, int64_t time) -{ - int64_t value; - struct bt_ctf_clock_class *cc; - - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - cc = clock->clock_class; - - /* Common case where cycles are actually nanoseconds */ - if (cc->frequency == 1000000000) { - value = time; - } else { - value = (uint64_t) (((double) time * - (double) cc->frequency) / 1e9); - } - - BT_CTF_ASSERT_PRE(clock->value <= value, - "CTF writer clock value must be updated monotonically: " - "prev-value=%" PRId64 ", new-value=%" PRId64, - clock->value, value); - clock->value = value; - return 0; -} - -BT_HIDDEN -int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value) -{ - BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - *value = clock->value; - return 0; -} - -static -void bt_ctf_clock_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_clock *clock; - - clock = container_of(obj, struct bt_ctf_clock, base); - bt_ctf_object_put_ref(clock->clock_class); - g_free(clock); -} - -BT_HIDDEN -void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class, - struct metadata_context *context) -{ - unsigned char *uuid; - - BT_LOGD("Serializing clock class's metadata: clock-class-addr=%p, " - "name=\"%s\", metadata-context-addr=%p", clock_class, - bt_ctf_clock_class_get_name(clock_class), - context); - - if (!clock_class || !context) { - BT_LOGW("Invalid parameter: clock class or metadata context is NULL: " - "clock-class-addr=%p, name=\"%s\", metadata-context-addr=%p", - clock_class, - bt_ctf_clock_class_get_name(clock_class), - context); - return; - } - - uuid = clock_class->uuid; - g_string_append(context->string, "clock {\n"); - g_string_append_printf(context->string, "\tname = %s;\n", - clock_class->name->str); - - if (clock_class->uuid_set) { - g_string_append_printf(context->string, - "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", - uuid[0], uuid[1], uuid[2], uuid[3], - uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], - uuid[12], uuid[13], uuid[14], uuid[15]); - } - - if (clock_class->description) { - g_string_append_printf(context->string, "\tdescription = \"%s\";\n", - clock_class->description->str); - } - - g_string_append_printf(context->string, "\tfreq = %" PRIu64 ";\n", - clock_class->frequency); - g_string_append_printf(context->string, "\tprecision = %" PRIu64 ";\n", - clock_class->precision); - g_string_append_printf(context->string, "\toffset_s = %" PRIu64 ";\n", - clock_class->offset_s); - g_string_append_printf(context->string, "\toffset = %" PRIu64 ";\n", - clock_class->offset); - g_string_append_printf(context->string, "\tabsolute = %s;\n", - clock_class->absolute ? "true" : "false"); - g_string_append(context->string, "};\n\n"); -} diff --git a/ctf-writer/event-class.c b/ctf-writer/event-class.c deleted file mode 100644 index 3d8c1f5c..00000000 --- a/ctf-writer/event-class.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-EVENT-CLASS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj) -{ - struct bt_ctf_event_class_common *event_class; - - event_class = container_of(obj, struct bt_ctf_event_class_common, base); - BT_LOGD("Finalizing common event class: addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - - if (event_class->name) { - g_string_free(event_class->name, TRUE); - } - - if (event_class->emf_uri) { - g_string_free(event_class->emf_uri, TRUE); - } - - BT_LOGD_STR("Putting context field type."); - bt_ctf_object_put_ref(event_class->context_field_type); - BT_LOGD_STR("Putting payload field type."); - bt_ctf_object_put_ref(event_class->payload_field_type); -} - -BT_HIDDEN -int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class, - const char *name, bt_ctf_object_release_func release_func, - bt_ctf_field_type_structure_create_func ft_struct_create_func) -{ - int ret = 0; - - BT_LOGD("Initializing common event class object: name=\"%s\"", - name); - bt_ctf_object_init_shared_with_parent(&event_class->base, release_func); - event_class->payload_field_type = ft_struct_create_func(); - if (!event_class->payload_field_type) { - BT_LOGE_STR("Cannot create event class's initial payload field type object."); - goto error; - } - - event_class->id = -1; - event_class->name = g_string_new(name); - if (!event_class->name) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - event_class->emf_uri = g_string_new(NULL); - if (!event_class->emf_uri) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - event_class->log_level = BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED; - BT_LOGD("Initialized common event class object: addr=%p, name=\"%s\"", - event_class, bt_ctf_event_class_common_get_name(event_class)); - return ret; - -error: - ret = -1; - return ret; -} - -BT_HIDDEN -void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class) -{ - BT_ASSERT(event_class); - - if (event_class->frozen) { - return; - } - - BT_LOGD("Freezing event class: addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - event_class->frozen = 1; - BT_LOGD_STR("Freezing event class's context field type."); - bt_ctf_field_type_common_freeze(event_class->context_field_type); - BT_LOGD_STR("Freezing event class's payload field type."); - bt_ctf_field_type_common_freeze(event_class->payload_field_type); -} - -BT_HIDDEN -int bt_ctf_event_class_common_validate_single_clock_class( - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_clock_class **expected_clock_class) -{ - int ret = 0; - - BT_ASSERT(event_class); - BT_ASSERT(expected_clock_class); - ret = bt_ctf_field_type_common_validate_single_clock_class( - event_class->context_field_type, - expected_clock_class); - if (ret) { - BT_LOGW("Event class's context field type " - "is not recursively mapped to the " - "expected clock class: " - "event-class-addr=%p, " - "event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", " - "ft-addr=%p", - event_class, - bt_ctf_event_class_common_get_name(event_class), - event_class->id, - event_class->context_field_type); - goto end; - } - - ret = bt_ctf_field_type_common_validate_single_clock_class( - event_class->payload_field_type, - expected_clock_class); - if (ret) { - BT_LOGW("Event class's payload field type " - "is not recursively mapped to the " - "expected clock class: " - "event-class-addr=%p, " - "event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", " - "ft-addr=%p", - event_class, - bt_ctf_event_class_common_get_name(event_class), - event_class->id, - event_class->payload_field_type); - goto end; - } - -end: - return ret; -} - -static -void bt_ctf_event_class_destroy(struct bt_ctf_object *obj) -{ - bt_ctf_event_class_common_finalize(obj); - g_free(obj); -} - -struct bt_ctf_event_class *bt_ctf_event_class_create(const char *name) -{ - struct bt_ctf_event_class *ctf_event_class = NULL; - int ret; - - if (!name) { - BT_LOGW_STR("Invalid parameter: name is NULL."); - goto error; - } - - BT_LOGD("Creating event class object: name=\"%s\"", - name); - ctf_event_class = g_new0(struct bt_ctf_event_class, 1); - if (!ctf_event_class) { - BT_LOGE_STR("Failed to allocate one event class."); - goto error; - } - - ret = bt_ctf_event_class_common_initialize(BT_CTF_TO_COMMON(ctf_event_class), - name, bt_ctf_event_class_destroy, - (bt_ctf_field_type_structure_create_func) - bt_ctf_field_type_structure_create); - if (ret) { - goto error; - } - - goto end; - -error: - bt_ctf_object_put_ref(ctf_event_class); - -end: - return ctf_event_class; -} - -const char *bt_ctf_event_class_get_name(struct bt_ctf_event_class *event_class) -{ - return bt_ctf_event_class_common_get_name(BT_CTF_TO_COMMON(event_class)); -} - -int64_t bt_ctf_event_class_get_id(struct bt_ctf_event_class *event_class) -{ - return bt_ctf_event_class_common_get_id(BT_CTF_TO_COMMON(event_class)); -} - -int bt_ctf_event_class_set_id(struct bt_ctf_event_class *event_class, - uint64_t id) -{ - return bt_ctf_event_class_common_set_id(BT_CTF_TO_COMMON(event_class), id); -} - -enum bt_ctf_event_class_log_level bt_ctf_event_class_get_log_level( - struct bt_ctf_event_class *event_class) -{ - return bt_ctf_event_class_common_get_log_level(BT_CTF_TO_COMMON(event_class)); -} - -int bt_ctf_event_class_set_log_level(struct bt_ctf_event_class *event_class, - enum bt_ctf_event_class_log_level log_level) -{ - return bt_ctf_event_class_common_set_log_level(BT_CTF_TO_COMMON(event_class), - log_level); -} - -const char *bt_ctf_event_class_get_emf_uri( - struct bt_ctf_event_class *event_class) -{ - return bt_ctf_event_class_common_get_emf_uri(BT_CTF_TO_COMMON(event_class)); -} - -int bt_ctf_event_class_set_emf_uri(struct bt_ctf_event_class *event_class, - const char *emf_uri) -{ - return bt_ctf_event_class_common_set_emf_uri(BT_CTF_TO_COMMON(event_class), - emf_uri); -} - -struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class( - struct bt_ctf_event_class *event_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_stream_class( - BT_CTF_TO_COMMON(event_class))); -} - -struct bt_ctf_field_type *bt_ctf_event_class_get_payload_field_type( - struct bt_ctf_event_class *event_class) -{ - return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_payload_field_type( - BT_CTF_TO_COMMON(event_class))); -} - -int bt_ctf_event_class_set_payload_field_type( - struct bt_ctf_event_class *event_class, - struct bt_ctf_field_type *field_type) -{ - return bt_ctf_event_class_common_set_payload_field_type( - BT_CTF_TO_COMMON(event_class), (void *) field_type); -} - -struct bt_ctf_field_type *bt_ctf_event_class_get_context_field_type( - struct bt_ctf_event_class *event_class) -{ - return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_context_field_type( - BT_CTF_TO_COMMON(event_class))); -} - -int bt_ctf_event_class_set_context_field_type( - struct bt_ctf_event_class *event_class, - struct bt_ctf_field_type *field_type) -{ - return bt_ctf_event_class_common_set_context_field_type( - BT_CTF_TO_COMMON(event_class), (void *) field_type); -} - -int bt_ctf_event_class_add_field(struct bt_ctf_event_class *event_class, - struct bt_ctf_field_type *type, - const char *name) -{ - int ret = 0; - - if (!event_class || !type) { - BT_LOGW("Invalid parameter: event class or field type is NULL: " - "event-class-addr=%p, field-type-addr=%p", - event_class, type); - ret = -1; - goto end; - } - - if (!bt_ctf_identifier_is_valid(name)) { - BT_LOGW("Invalid parameter: event class's payload field type's field name is not a valid CTF identifier: " - "addr=%p, name=\"%s\", id=%" PRId64 ", field-name=\"%s\"", - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class), - name); - ret = -1; - goto end; - } - - if (event_class->common.frozen) { - BT_LOGW("Invalid parameter: event class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - ret = -1; - goto end; - } - - if (!event_class->common.payload_field_type) { - BT_LOGW("Event class has no payload field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - ret = -1; - goto end; - } - - BT_ASSERT(bt_ctf_field_type_common_get_type_id( - event_class->common.payload_field_type) == - BT_CTF_FIELD_TYPE_ID_STRUCT); - ret = bt_ctf_field_type_structure_add_field( - (void *) event_class->common.payload_field_type, - (void *) type, name); - BT_LOGV("Added field to event class's payload field type: " - "event-class-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", field-name=\"%s\", ft-addr=%p", - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class), name, type); -end: - return ret; -} - -int64_t bt_ctf_event_class_get_payload_type_field_count( - struct bt_ctf_event_class *event_class) -{ - int64_t ret; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = (int64_t) -1; - goto end; - } - - if (!event_class->common.payload_field_type) { - BT_LOGV("Event class has no payload field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - ret = (int64_t) -1; - goto end; - } - - BT_ASSERT(bt_ctf_field_type_common_get_type_id( - event_class->common.payload_field_type) == - BT_CTF_FIELD_TYPE_ID_STRUCT); - ret = bt_ctf_field_type_common_structure_get_field_count( - event_class->common.payload_field_type); -end: - return ret; -} - -int bt_ctf_event_class_get_payload_type_field_by_index( - struct bt_ctf_event_class *event_class, - const char **field_name, struct bt_ctf_field_type **field_type, - uint64_t index) -{ - int ret; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (!event_class->common.payload_field_type) { - BT_LOGV("Event class has no payload field type: " - "addr=%p, name=\"%s\", id=%" PRId64 ", index=%" PRIu64, - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class), index); - ret = -1; - goto end; - } - - BT_ASSERT(bt_ctf_field_type_common_get_type_id( - event_class->common.payload_field_type) == - BT_CTF_FIELD_TYPE_ID_STRUCT); - ret = bt_ctf_field_type_structure_get_field_by_index( - (void *) event_class->common.payload_field_type, - field_name, (void *) field_type, index); - -end: - return ret; -} - -struct bt_ctf_field_type * -bt_ctf_event_class_get_payload_type_field_type_by_name( - struct bt_ctf_event_class *event_class, const char *name) -{ - GQuark name_quark; - struct bt_ctf_field_type *field_type = NULL; - - if (!event_class || !name) { - BT_LOGW("Invalid parameter: event class or name is NULL: " - "event-class-addr=%p, name-addr=%p", - event_class, name); - goto end; - } - - if (!event_class->common.payload_field_type) { - BT_LOGV("Event class has no payload field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - goto end; - } - - BT_ASSERT(bt_ctf_field_type_common_get_type_id( - event_class->common.payload_field_type) == - BT_CTF_FIELD_TYPE_ID_STRUCT); - name_quark = g_quark_try_string(name); - if (!name_quark) { - BT_LOGE("Cannot get GQuark: string=\"%s\"", name); - goto end; - } - - /* - * No need to increment field_type's reference count since getting it - * from the structure already does. - */ - field_type = (void *) - bt_ctf_field_type_structure_get_field_type_by_name( - (void *) event_class->common.payload_field_type, name); - -end: - return field_type; -} - -BT_HIDDEN -int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class, - struct metadata_context *context) -{ - int ret = 0; - struct bt_ctf_value *attr_value = NULL; - - BT_ASSERT(event_class); - BT_ASSERT(context); - BT_LOGD("Serializing event class's metadata: " - "event-class-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", metadata-context-addr=%p", - event_class, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class), context); - context->current_indentation_level = 1; - g_string_assign(context->field_name, ""); - g_string_append(context->string, "event {\n"); - - /* Serialize attributes */ - g_string_append_printf(context->string, "\tname = \"%s\";\n", - event_class->common.name->str); - BT_ASSERT(event_class->common.id >= 0); - g_string_append_printf(context->string, "\tid = %" PRId64 ";\n", - event_class->common.id); - g_string_append_printf(context->string, "\tstream_id = %" PRId64 ";\n", - bt_ctf_stream_class_common_get_id( - bt_ctf_event_class_common_borrow_stream_class( - BT_CTF_TO_COMMON(event_class)))); - - if (event_class->common.log_level != - BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED) { - g_string_append_printf(context->string, "\tloglevel = %d;\n", - (int) event_class->common.log_level); - } - - if (event_class->common.emf_uri->len > 0) { - g_string_append_printf(context->string, "\tmodel.emf.uri = \"%s\";\n", - event_class->common.emf_uri->str); - } - - /* Serialize context field type */ - if (event_class->common.context_field_type) { - g_string_append(context->string, "\tcontext := "); - BT_LOGD_STR("Serializing event class's context field type metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) event_class->common.context_field_type, - context); - if (ret) { - BT_LOGW("Cannot serialize event class's context field type's metadata: " - "ret=%d", ret); - goto end; - } - g_string_append(context->string, ";\n"); - } - - /* Serialize payload field type */ - if (event_class->common.payload_field_type) { - g_string_append(context->string, "\tfields := "); - BT_LOGD_STR("Serializing event class's payload field type metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) event_class->common.payload_field_type, - context); - if (ret) { - BT_LOGW("Cannot serialize event class's payload field type's metadata: " - "ret=%d", ret); - goto end; - } - g_string_append(context->string, ";\n"); - } - - g_string_append(context->string, "};\n\n"); - -end: - context->current_indentation_level = 0; - BT_CTF_OBJECT_PUT_REF_AND_RESET(attr_value); - return ret; -} - -struct bt_ctf_field_type *bt_ctf_event_class_get_field_by_name( - struct bt_ctf_event_class *event_class, const char *name) -{ - GQuark name_quark; - struct bt_ctf_field_type *field_type = NULL; - - if (!event_class || !name) { - BT_LOGW("Invalid parameter: event class or name is NULL: " - "event-class-addr=%p, name-addr=%p", - event_class, name); - goto end; - } - - if (!event_class->common.payload_field_type) { - BT_LOGV("Event class has no payload field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, - bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - goto end; - } - - BT_ASSERT(event_class->common.payload_field_type->id == - BT_CTF_FIELD_TYPE_ID_STRUCT); - name_quark = g_quark_try_string(name); - if (!name_quark) { - BT_LOGE("Cannot get GQuark: string=\"%s\"", name); - goto end; - } - - /* - * No need to increment field_type's reference count since getting it - * from the structure already does. - */ - field_type = bt_ctf_object_get_ref( - bt_ctf_field_type_common_structure_borrow_field_type_by_name( - event_class->common.payload_field_type, name)); - -end: - return field_type; -} diff --git a/ctf-writer/event.c b/ctf-writer/event.c deleted file mode 100644 index b074cdb1..00000000 --- a/ctf-writer/event.c +++ /dev/null @@ -1,900 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-EVENT" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -int bt_ctf_event_common_validate_types_for_create( - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_validation_output *validation_output, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) -{ - int ret; - enum bt_ctf_validation_flag validation_flags = - BT_CTF_VALIDATION_FLAG_STREAM | - BT_CTF_VALIDATION_FLAG_EVENT; - struct bt_ctf_trace_common *trace = NULL; - struct bt_ctf_stream_class_common *stream_class = NULL; - struct bt_ctf_field_type_common *packet_header_type = NULL; - struct bt_ctf_field_type_common *packet_context_type = NULL; - struct bt_ctf_field_type_common *event_header_type = NULL; - struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; - struct bt_ctf_field_type_common *event_context_type = NULL; - struct bt_ctf_field_type_common *event_payload_type = NULL; - int trace_valid = 0; - struct bt_ctf_private_value *environment = NULL; - - stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); - BT_ASSERT(stream_class); - trace = bt_ctf_stream_class_common_borrow_trace(stream_class); - if (trace) { - BT_LOGD_STR("Event class is part of a trace."); - packet_header_type = - bt_ctf_trace_common_borrow_packet_header_field_type(trace); - trace_valid = trace->valid; - BT_ASSERT(trace_valid); - environment = trace->environment; - } - - packet_context_type = - bt_ctf_stream_class_common_borrow_packet_context_field_type( - stream_class); - event_header_type = - bt_ctf_stream_class_common_borrow_event_header_field_type( - stream_class); - stream_event_ctx_type = - bt_ctf_stream_class_common_borrow_event_context_field_type( - stream_class); - event_context_type = - bt_ctf_event_class_common_borrow_context_field_type(event_class); - event_payload_type = - bt_ctf_event_class_common_borrow_payload_field_type(event_class); - ret = bt_ctf_validate_class_types(environment, packet_header_type, - packet_context_type, event_header_type, stream_event_ctx_type, - event_context_type, event_payload_type, trace_valid, - stream_class->valid, event_class->valid, - validation_output, validation_flags, copy_field_type_func); - if (ret) { - /* - * This means something went wrong during the validation - * process, not that the objects are invalid. - */ - BT_LOGE("Failed to validate event and parents: ret=%d", ret); - goto error; - } - - if ((validation_output->valid_flags & validation_flags) != - validation_flags) { - /* Invalid trace/stream class/event class */ - BT_LOGW("Invalid trace, stream class, or event class: " - "valid-flags=0x%x", validation_output->valid_flags); - goto error; - } - - goto end; - -error: - bt_ctf_validation_output_put_types(validation_output); - ret = -1; - -end: - return ret; -} - -static -int bt_ctf_event_common_create_fields( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_validation_output *validation_output, - create_field_func create_field_func, - release_field_func release_field_func, - create_header_field_func create_header_field_func, - release_header_field_func release_header_field_func, - struct bt_ctf_field_wrapper **header_field, - struct bt_ctf_field_common **stream_event_context_field, - struct bt_ctf_field_common **context_field, - struct bt_ctf_field_common **payload_field) -{ - int ret = 0; - - if (validation_output->event_header_type) { - BT_LOGD("Creating initial event header field: ft-addr=%p", - validation_output->event_header_type); - *header_field = - create_header_field_func(stream_class, - validation_output->event_header_type); - if (!*header_field) { - BT_LOGE_STR("Cannot create initial event header field object."); - goto error; - } - } - - if (validation_output->stream_event_ctx_type) { - BT_LOGD("Creating initial stream event context field: ft-addr=%p", - validation_output->stream_event_ctx_type); - *stream_event_context_field = create_field_func( - validation_output->stream_event_ctx_type); - if (!*stream_event_context_field) { - BT_LOGE_STR("Cannot create initial stream event context field object."); - goto error; - } - } - - if (validation_output->event_context_type) { - BT_LOGD("Creating initial event context field: ft-addr=%p", - validation_output->event_context_type); - *context_field = create_field_func( - validation_output->event_context_type); - if (!*context_field) { - BT_LOGE_STR("Cannot create initial event context field object."); - goto error; - } - } - - if (validation_output->event_payload_type) { - BT_LOGD("Creating initial event payload field: ft-addr=%p", - validation_output->event_payload_type); - *payload_field = create_field_func( - validation_output->event_payload_type); - if (!*payload_field) { - BT_LOGE_STR("Cannot create initial event payload field object."); - goto error; - } - } - - goto end; - -error: - if (*header_field) { - release_header_field_func(*header_field, stream_class); - } - - if (*stream_event_context_field) { - release_field_func(*stream_event_context_field); - } - - if (*context_field) { - release_field_func(*context_field); - } - - if (*payload_field) { - release_field_func(*payload_field); - } - - ret = -1; - -end: - return ret; -} - -BT_HIDDEN -int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event) -{ - int ret = 0; - struct bt_ctf_stream_class_common *stream_class; - - BT_ASSERT(event); - if (event->header_field) { - ret = bt_ctf_field_common_validate_recursive( - event->header_field->field); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid event's header field: " - "event-addr=%p, field-addr=%p", - event, event->header_field->field); - goto end; - } - } - - stream_class = bt_ctf_event_class_common_borrow_stream_class(event->class); - - /* - * We should not have been able to create the event without associating - * the event class to a stream class. - */ - BT_ASSERT(stream_class); - - if (stream_class->event_context_field_type) { - ret = bt_ctf_field_common_validate_recursive( - event->stream_event_context_field); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid event's stream event context field: " - "event-addr=%p, field-addr=%p", - event, event->stream_event_context_field); - goto end; - } - } - - if (event->class->context_field_type) { - ret = bt_ctf_field_common_validate_recursive(event->context_field); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid event's payload field: " - "event-addr=%p, field-addr=%p", - event, event->context_field); - goto end; - } - } - - ret = bt_ctf_field_common_validate_recursive(event->payload_field); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid event's payload field: " - "event-addr=%p, field-addr=%p", - event, event->payload_field); - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event, - bool is_frozen) -{ - BT_ASSERT(event); - BT_LOGD("Freezing event: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - - if (event->header_field) { - BT_LOGD_STR("Freezing event's header field."); - bt_ctf_field_common_set_is_frozen_recursive( - event->header_field->field, is_frozen); - } - - if (event->stream_event_context_field) { - BT_LOGD_STR("Freezing event's stream event context field."); - bt_ctf_field_common_set_is_frozen_recursive( - event->stream_event_context_field, is_frozen); - } - - if (event->context_field) { - BT_LOGD_STR("Freezing event's context field."); - bt_ctf_field_common_set_is_frozen_recursive(event->context_field, - is_frozen); - } - - if (event->payload_field) { - BT_LOGD_STR("Freezing event's payload field."); - bt_ctf_field_common_set_is_frozen_recursive(event->payload_field, - is_frozen); - } - - event->frozen = is_frozen; -} - -BT_HIDDEN -int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event, - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_clock_class *init_expected_clock_class, - bool is_shared_with_parent, bt_ctf_object_release_func release_func, - bt_ctf_validation_flag_copy_field_type_func field_type_copy_func, - bool must_be_in_trace, - int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_field_type, - struct bt_ctf_field_type_common *event_header_field_type), - create_field_func create_field_func, - release_field_func release_field_func, - create_header_field_func create_header_field_func, - release_header_field_func release_header_field_func) -{ - int ret; - struct bt_ctf_trace_common *trace = NULL; - struct bt_ctf_stream_class_common *stream_class = NULL; - struct bt_ctf_field_wrapper *event_header = NULL; - struct bt_ctf_field_common *stream_event_context = NULL; - struct bt_ctf_field_common *event_context = NULL; - struct bt_ctf_field_common *event_payload = NULL; - struct bt_ctf_validation_output validation_output = { 0 }; - struct bt_ctf_clock_class *expected_clock_class = - init_expected_clock_class ? bt_ctf_object_get_ref(init_expected_clock_class) : - NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_LOGD("Initializing common event object: event-class-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - - stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); - BT_CTF_ASSERT_PRE(stream_class, - "Event class is not part of a stream class: event-class-addr=%p", - event_class); - - /* The event class was frozen when added to its stream class */ - BT_ASSERT(event_class->frozen); - trace = bt_ctf_stream_class_common_borrow_trace(stream_class); - - if (must_be_in_trace) { - BT_CTF_ASSERT_PRE(trace, - "Event class's stream class is not part of a trace: " - "ec-addr=%p, sc-addr=%p", event_class, stream_class); - } - - /* - * This must be called before anything that can fail because on - * failure, the caller releases the reference to `event` to - * destroy it. - */ - if (is_shared_with_parent) { - bt_ctf_object_init_shared_with_parent(&event->base, release_func); - } else { - bt_ctf_object_init_unique(&event->base); - } - - if (!stream_class->frozen) { - /* - * Because this function freezes the stream class, - * validate that this stream class contains at most a - * single clock class so that we set its expected clock - * class for future checks. - */ - ret = bt_ctf_stream_class_common_validate_single_clock_class( - stream_class, &expected_clock_class); - if (ret) { - BT_LOGW("Event class's stream class or one of its event " - "classes contains a field type which is not " - "recursively mapped to the expected " - "clock class: " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "stream-class-name=\"%s\", " - "expected-clock-class-addr=%p, " - "expected-clock-class-name=\"%s\"", - stream_class, - bt_ctf_stream_class_common_get_id(stream_class), - bt_ctf_stream_class_common_get_name(stream_class), - expected_clock_class, - expected_clock_class ? - bt_ctf_clock_class_get_name(expected_clock_class) : - NULL); - goto error; - } - } - - /* Validate the trace, the stream class, and the event class */ - ret = bt_ctf_event_common_validate_types_for_create( - event_class, &validation_output, field_type_copy_func); - if (ret) { - /* bt_ctf_event_common_validate_types_for_create() logs errors */ - goto error; - } - - if (map_clock_classes_func) { - /* - * Safe to automatically map selected fields to the - * stream's clock's class here because the stream class - * is about to be frozen. - */ - if (map_clock_classes_func(stream_class, - validation_output.packet_context_type, - validation_output.event_header_type)) { - BT_LOGW_STR("Cannot automatically map selected stream class's " - "field types to stream class's clock's class."); - goto error; - } - } - - /* - * event does not share a common ancestor with the event class; it has - * to guarantee its existence by holding a reference. This reference - * shall be released once the event is associated to a stream since, - * from that point, the event and its class will share the same - * lifetime. - */ - event->class = bt_ctf_object_get_ref(event_class); - - ret = bt_ctf_event_common_create_fields(stream_class, - &validation_output, - create_field_func, release_field_func, - create_header_field_func, release_header_field_func, - &event_header, &stream_event_context, &event_context, - &event_payload); - if (ret) { - /* bt_ctf_event_common_create_fields() logs errors */ - goto error; - } - - /* - * At this point all the fields are created, potentially from - * validated copies of field types, so that the field types and - * fields can be replaced in the trace, stream class, - * event class, and created event. - */ - bt_ctf_validation_replace_types(trace, stream_class, event_class, - &validation_output, - BT_CTF_VALIDATION_FLAG_STREAM | BT_CTF_VALIDATION_FLAG_EVENT); - event->header_field = event_header; - event_header = NULL; - event->stream_event_context_field = stream_event_context; - stream_event_context = NULL; - event->context_field = event_context; - event_context = NULL; - event->payload_field = event_payload; - event_payload = NULL; - - /* - * Put what was not moved in bt_ctf_validation_replace_types(). - */ - bt_ctf_validation_output_put_types(&validation_output); - - /* - * Freeze the stream class since the event header must not be changed - * anymore. - */ - bt_ctf_stream_class_common_freeze(stream_class); - - /* - * It is safe to set the stream class's unique clock class - * now because the stream class is frozen. - */ - if (expected_clock_class) { - BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); - } - - /* - * Mark stream class, and event class as valid since - * they're all frozen now. - */ - stream_class->valid = 1; - event_class->valid = 1; - - /* Put stuff we borrowed from the event class */ - BT_LOGD("Initialized event object: addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - goto end; - -error: - bt_ctf_validation_output_put_types(&validation_output); - bt_ctf_object_put_ref(expected_clock_class); - - if (event_header) { - release_header_field_func(event_header, stream_class); - } - - if (stream_event_context) { - release_field_func(stream_event_context); - } - - if (event_context) { - release_field_func(event_context); - } - - if (event_payload) { - release_field_func(event_payload); - } - - ret = -1; - -end: - return ret; -} - -int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type) -{ - int ret = bt_ctf_stream_class_map_clock_class( - BT_CTF_FROM_COMMON(stream_class), - BT_CTF_FROM_COMMON(packet_context_type), - BT_CTF_FROM_COMMON(event_header_type)); - - if (ret) { - BT_LOGW_STR("Cannot automatically map selected stream class's field types to stream class's clock's class."); - } - - return ret; -} - -static -void destroy_event_header_field(struct bt_ctf_field_wrapper *field_wrapper) -{ - BT_ASSERT(field_wrapper); - bt_ctf_object_put_ref(field_wrapper->field); - bt_ctf_field_wrapper_destroy(field_wrapper); -} - -static -struct bt_ctf_field_wrapper *create_event_header_field( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_wrapper *field_wrapper = NULL; - struct bt_ctf_field *field = bt_ctf_field_create((void *) ft); - - if (!field) { - goto error; - } - - field_wrapper = bt_ctf_field_wrapper_new(NULL); - if (!field_wrapper) { - goto error; - } - - field_wrapper->field = (void *) field; - field = NULL; - goto end; - -error: - bt_ctf_object_put_ref(field); - - if (field_wrapper) { - destroy_event_header_field(field_wrapper); - field_wrapper = NULL; - } - -end: - return field_wrapper; -} - -static -void release_event_header_field(struct bt_ctf_field_wrapper *field_wrapper, - struct bt_ctf_event_common *event_common) -{ - BT_ASSERT(field_wrapper); - BT_CTF_OBJECT_PUT_REF_AND_RESET(field_wrapper->field); - bt_ctf_field_wrapper_destroy(field_wrapper); -} - -static -void bt_ctf_event_destroy(struct bt_ctf_object *obj) -{ - bt_ctf_event_common_finalize(obj, (void *) bt_ctf_object_put_ref, - (void *) release_event_header_field); - g_free(obj); -} - -struct bt_ctf_event *bt_ctf_event_create(struct bt_ctf_event_class *event_class) -{ - int ret; - struct bt_ctf_event *event = NULL; - struct bt_ctf_clock_class *expected_clock_class = NULL; - - event = g_new0(struct bt_ctf_event, 1); - if (!event) { - BT_LOGE_STR("Failed to allocate one CTF writer event."); - goto error; - } - - if (event_class) { - struct bt_ctf_stream_class *stream_class = - BT_CTF_FROM_COMMON(bt_ctf_event_class_common_borrow_stream_class( - BT_CTF_TO_COMMON(event_class))); - - if (stream_class && stream_class->clock) { - expected_clock_class = stream_class->clock->clock_class; - } - } - - ret = bt_ctf_event_common_initialize(BT_CTF_TO_COMMON(event), - BT_CTF_TO_COMMON(event_class), expected_clock_class, - true, bt_ctf_event_destroy, - (bt_ctf_validation_flag_copy_field_type_func) - bt_ctf_field_type_copy, - false, map_clock_classes_func, - (create_field_func) bt_ctf_field_create, - (release_field_func) bt_ctf_object_put_ref, - (create_header_field_func) create_event_header_field, - (release_header_field_func) destroy_event_header_field); - if (ret) { - /* bt_ctf_event_common_initialize() logs errors */ - goto error; - } - - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(event); - -end: - return event; -} - -struct bt_ctf_event_class *bt_ctf_event_get_class(struct bt_ctf_event *event) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))); -} - -BT_HIDDEN -struct bt_ctf_stream *bt_ctf_event_borrow_stream(struct bt_ctf_event *event) -{ - BT_ASSERT(event); - return (struct bt_ctf_stream *) - bt_ctf_object_borrow_parent(&BT_CTF_TO_COMMON(event)->base); -} - -struct bt_ctf_stream *bt_ctf_event_get_stream(struct bt_ctf_event *event) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - return bt_ctf_object_get_ref(bt_ctf_event_borrow_stream(event)); -} - -int bt_ctf_event_set_payload(struct bt_ctf_event *event, const char *name, - struct bt_ctf_field *field) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - BT_CTF_ASSERT_PRE_NON_NULL(field, "Payload field"); - BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); - return bt_ctf_field_structure_set_field_by_name( - (void *) event->common.payload_field, name, field); -} - -struct bt_ctf_field *bt_ctf_event_get_payload(struct bt_ctf_event *event, - const char *name) -{ - struct bt_ctf_field *field = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - - if (name) { - field = bt_ctf_field_structure_get_field_by_name( - BT_CTF_FROM_COMMON(event->common.payload_field), name); - } else { - field = BT_CTF_FROM_COMMON(event->common.payload_field); - bt_ctf_object_get_ref(field); - } - - return field; -} - -struct bt_ctf_field *bt_ctf_event_get_payload_field( - struct bt_ctf_event *event) -{ - return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_payload(BT_CTF_TO_COMMON(event))); -} - -struct bt_ctf_field *bt_ctf_event_get_header(struct bt_ctf_event *event) -{ - return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_header(BT_CTF_TO_COMMON(event))); -} - -struct bt_ctf_field *bt_ctf_event_get_context(struct bt_ctf_event *event) -{ - return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_context(BT_CTF_TO_COMMON(event))); -} - -struct bt_ctf_field *bt_ctf_event_get_stream_event_context( - struct bt_ctf_event *event) -{ - return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_stream_event_context( - BT_CTF_TO_COMMON(event))); -} - -BT_HIDDEN -int bt_ctf_event_serialize(struct bt_ctf_event *event, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int ret = 0; - - BT_ASSERT(event); - BT_ASSERT(ctfser); - - BT_LOGV_STR("Serializing event's context field."); - if (event->common.context_field) { - ret = bt_ctf_field_serialize_recursive( - (void *) event->common.context_field, ctfser, - native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize event's context field: " - "event-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64, - event, - bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class)); - goto end; - } - } - - BT_LOGV_STR("Serializing event's payload field."); - if (event->common.payload_field) { - ret = bt_ctf_field_serialize_recursive( - (void *) event->common.payload_field, ctfser, - native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize event's payload field: " - "event-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64, - event, - bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class)); - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -void _bt_ctf_event_freeze(struct bt_ctf_event *event) -{ - _bt_ctf_event_common_set_is_frozen(BT_CTF_TO_COMMON(event), true); -} - -int bt_ctf_event_set_header(struct bt_ctf_event *event, - struct bt_ctf_field *header) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); - - /* - * Ensure the provided header's type matches the one registered to the - * stream class. - */ - if (header) { - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( - ((struct bt_ctf_field_common *) header)->type, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type) == 0, - "Header field's type is different from the " - "expected field type: event-addr=%p, ft-addr=%p, " - "expected-ft-addr=%p", - event, ((struct bt_ctf_field_common *) header)->type, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type); - } else { - BT_CTF_ASSERT_PRE(!bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type, - "Setting no event header field, " - "but event header field type is not NULL: " - "event-addr=%p, header-ft-addr=%p", - event, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type); - } - - bt_ctf_object_put_ref(event->common.header_field->field); - event->common.header_field->field = bt_ctf_object_get_ref(header); - BT_LOGV("Set event's header field: event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "header-field-addr=%p", - event, bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class), header); - return 0; -} - -int bt_ctf_event_common_set_payload(struct bt_ctf_event *event, - struct bt_ctf_field *payload) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); - - if (payload) { - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( - ((struct bt_ctf_field_common *) payload)->type, - event->common.class->payload_field_type) == 0, - "Payload field's type is different from the " - "expected field type: event-addr=%p, ft-addr=%p, " - "expected-ft-addr=%p", - event, - ((struct bt_ctf_field_common *) payload)->type, - event->common.class->payload_field_type); - } else { - BT_CTF_ASSERT_PRE(!event->common.class->payload_field_type, - "Setting no event payload field, " - "but event payload field type is not NULL: " - "event-addr=%p, payload-ft-addr=%p", - event, event->common.class->payload_field_type); - } - - bt_ctf_object_put_ref(event->common.payload_field); - event->common.payload_field = bt_ctf_object_get_ref(payload); - BT_LOGV("Set event's payload field: event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "payload-field-addr=%p", - event, bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class), payload); - return 0; -} - -int bt_ctf_event_set_context(struct bt_ctf_event *event, - struct bt_ctf_field *context) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); - - if (context) { - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( - ((struct bt_ctf_field_common *) context)->type, - event->common.class->context_field_type) == 0, - "Context field's type is different from the " - "expected field type: event-addr=%p, ft-addr=%p, " - "expected-ft-addr=%p", - event, ((struct bt_ctf_field_common *) context)->type, - event->common.class->context_field_type); - } else { - BT_CTF_ASSERT_PRE(!event->common.class->context_field_type, - "Setting no event context field, " - "but event context field type is not NULL: " - "event-addr=%p, context-ft-addr=%p", - event, event->common.class->context_field_type); - } - - bt_ctf_object_put_ref(event->common.context_field); - event->common.context_field = bt_ctf_object_get_ref(context); - BT_LOGV("Set event's context field: event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "context-field-addr=%p", - event, bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class), context); - return 0; -} - -int bt_ctf_event_set_stream_event_context(struct bt_ctf_event *event, - struct bt_ctf_field *stream_event_context) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); - - if (stream_event_context) { - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( - ((struct bt_ctf_field_common *) stream_event_context)->type, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type) == 0, - "Stream event context field's type is different from the " - "expected field type: event-addr=%p, ft-addr=%p, " - "expected-ft-addr=%p", - event, - ((struct bt_ctf_field_common *) stream_event_context)->type, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type); - } else { - BT_CTF_ASSERT_PRE(!bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type, - "Setting no stream event context field, " - "but stream event context field type is not NULL: " - "event-addr=%p, context-ft-addr=%p", - event, - bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type); - } - - bt_ctf_object_put_ref(event->common.stream_event_context_field); - event->common.stream_event_context_field = bt_ctf_object_get_ref(stream_event_context); - BT_LOGV("Set event's stream event context field: event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "stream-event-context-field-addr=%p", - event, bt_ctf_event_class_common_get_name(event->common.class), - bt_ctf_event_class_common_get_id(event->common.class), - stream_event_context); - return 0; -} diff --git a/ctf-writer/field-path.c b/ctf-writer/field-path.c deleted file mode 100644 index 284e850d..00000000 --- a/ctf-writer/field-path.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * field-path.c - * - * Babeltrace CTF writer - Field path - * - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-FIELD-PATH" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -static -void field_path_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_path *field_path = (struct bt_ctf_field_path *) obj; - - BT_LOGD("Destroying field path: addr=%p", obj); - - if (!field_path) { - return; - } - - if (field_path->indexes) { - g_array_free(field_path->indexes, TRUE); - } - g_free(field_path); -} - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_path_create(void) -{ - struct bt_ctf_field_path *field_path = NULL; - - BT_LOGD_STR("Creating empty field path object."); - - field_path = g_new0(struct bt_ctf_field_path, 1); - if (!field_path) { - BT_LOGE_STR("Failed to allocate one field path."); - goto error; - } - - bt_ctf_object_init_shared(&field_path->base, field_path_destroy); - field_path->root = BT_CTF_SCOPE_UNKNOWN; - field_path->indexes = g_array_new(TRUE, FALSE, sizeof(int)); - if (!field_path->indexes) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - BT_LOGD("Created empty field path object: addr=%p", field_path); - return field_path; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); - return NULL; -} - -BT_HIDDEN -void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path) -{ - if (field_path->indexes->len > 0) { - g_array_remove_range(field_path->indexes, 0, - field_path->indexes->len); - } -} - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_path_copy( - struct bt_ctf_field_path *path) -{ - struct bt_ctf_field_path *new_path; - - BT_ASSERT(path); - BT_LOGD("Copying field path: addr=%p, index-count=%u", - path, path->indexes->len); - new_path = bt_ctf_field_path_create(); - if (!new_path) { - BT_LOGE_STR("Cannot create empty field path."); - goto end; - } - - new_path->root = path->root; - g_array_insert_vals(new_path->indexes, 0, - path->indexes->data, path->indexes->len); - BT_LOGD("Copied field path: original-addr=%p, copy-addr=%p", - path, new_path); -end: - return new_path; -} - -enum bt_ctf_scope bt_ctf_field_path_get_root_scope( - const struct bt_ctf_field_path *field_path) -{ - enum bt_ctf_scope scope = BT_CTF_SCOPE_UNKNOWN; - - if (!field_path) { - BT_LOGW_STR("Invalid parameter: field path is NULL."); - goto end; - } - - scope = field_path->root; - -end: - return scope; -} - -int64_t bt_ctf_field_path_get_index_count( - const struct bt_ctf_field_path *field_path) -{ - int64_t count = (int64_t) -1; - - if (!field_path) { - BT_LOGW_STR("Invalid parameter: field path is NULL."); - goto end; - } - - count = (int64_t) field_path->indexes->len; - -end: - return count; -} - -int bt_ctf_field_path_get_index(const struct bt_ctf_field_path *field_path, - uint64_t index) -{ - int ret = INT_MIN; - - if (!field_path) { - BT_LOGW_STR("Invalid parameter: field path is NULL."); - goto end; - } - - if (index >= field_path->indexes->len) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "addr=%p, index=%" PRIu64 ", count=%u", - field_path, index, field_path->indexes->len); - goto end; - } - - ret = g_array_index(field_path->indexes, int, index); - -end: - return ret; -} diff --git a/ctf-writer/field-types.c b/ctf-writer/field-types.c deleted file mode 100644 index 2d18c45c..00000000 --- a/ctf-writer/field-types.c +++ /dev/null @@ -1,5564 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-FIELD-TYPES" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_enumeration_mapping(struct bt_ctf_enumeration_mapping *mapping) -{ - g_free(mapping); -} - -BT_HIDDEN -void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft, - bool init_bo, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - BT_ASSERT(ft && (ft->id > BT_CTF_FIELD_TYPE_ID_UNKNOWN) && - (ft->id < BT_CTF_FIELD_TYPE_ID_NR)); - - bt_ctf_object_init_shared(&ft->base, release_func); - ft->methods = methods; - - if (init_bo) { - int ret; - const enum bt_ctf_byte_order bo = BT_CTF_BYTE_ORDER_NATIVE; - - BT_LOGD("Setting initial field type's byte order: bo=%s", - bt_ctf_byte_order_string(bo)); - ret = bt_ctf_field_type_common_set_byte_order(ft, bo); - BT_ASSERT(ret == 0); - } - - ft->alignment = 1; -} - -BT_HIDDEN -void bt_ctf_field_type_common_integer_initialize( - struct bt_ctf_field_type_common *ft, - unsigned int size, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(size > 0); - BT_LOGD("Initializing common integer field type object: size=%u", - size); - ft->id = BT_CTF_FIELD_TYPE_ID_INTEGER; - int_ft->size = size; - int_ft->base = BT_CTF_INTEGER_BASE_DECIMAL; - int_ft->encoding = BT_CTF_STRING_ENCODING_NONE; - bt_ctf_field_type_common_initialize(ft, true, release_func, methods); - BT_LOGD("Initialized common integer field type object: addr=%p, size=%u", - ft, size); -} - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - BT_LOGD_STR("Initializing common floating point number field type object."); - ft->id = BT_CTF_FIELD_TYPE_ID_FLOAT; - flt_ft->exp_dig = sizeof(float) * CHAR_BIT - FLT_MANT_DIG; - flt_ft->mant_dig = FLT_MANT_DIG; - bt_ctf_field_type_common_initialize(ft, true, release_func, methods); - BT_LOGD("Initialized common floating point number field type object: addr=%p, " - "exp-size=%u, mant-size=%u", ft, flt_ft->exp_dig, - flt_ft->mant_dig); -} - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *container_ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(container_ft); - BT_LOGD("Initializing common enumeration field type object: int-ft-addr=%p", - container_ft); - ft->id = BT_CTF_FIELD_TYPE_ID_ENUM; - enum_ft->container_ft = bt_ctf_object_get_ref(container_ft); - enum_ft->entries = g_ptr_array_new_with_free_func( - (GDestroyNotify) destroy_enumeration_mapping); - bt_ctf_field_type_common_initialize(ft, false, release_func, methods); - BT_LOGD("Initialized common enumeration field type object: addr=%p, " - "int-ft-addr=%p, int-ft-size=%u", ft, container_ft, - bt_ctf_field_type_common_integer_get_size(container_ft)); -} - -BT_HIDDEN -void bt_ctf_field_type_common_string_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); - - BT_LOGD_STR("Initializing common string field type object."); - ft->id = BT_CTF_FIELD_TYPE_ID_STRING; - bt_ctf_field_type_common_initialize(ft, true, release_func, methods); - string_ft->encoding = BT_CTF_STRING_ENCODING_UTF8; - ft->alignment = CHAR_BIT; - BT_LOGD("Initialized common string field type object: addr=%p", ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_structure_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - - BT_LOGD_STR("Initializing common structure field type object."); - ft->id = BT_CTF_FIELD_TYPE_ID_STRUCT; - struct_ft->fields = g_array_new(FALSE, TRUE, - sizeof(struct bt_ctf_field_type_common_structure_field)); - struct_ft->field_name_to_index = g_hash_table_new(NULL, NULL); - bt_ctf_field_type_common_initialize(ft, true, release_func, methods); - BT_LOGD("Initialized common structure field type object: addr=%p", ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_array_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft, - unsigned int length, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(element_ft); - BT_LOGD("Initializing common array field type object: element-ft-addr=%p, " - "length=%u", element_ft, length); - ft->id = BT_CTF_FIELD_TYPE_ID_ARRAY; - array_ft->element_ft = bt_ctf_object_get_ref(element_ft); - array_ft->length = length; - bt_ctf_field_type_common_initialize(ft, false, release_func, methods); - BT_LOGD("Initialized common array field type object: addr=%p, " - "element-ft-addr=%p, length=%u", ft, element_ft, length); -} - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft, - const char *length_field_name, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(element_ft); - BT_ASSERT(length_field_name); - BT_ASSERT(bt_ctf_identifier_is_valid(length_field_name)); - BT_LOGD("Initializing common sequence field type object: element-ft-addr=%p, " - "length-field-name=\"%s\"", element_ft, length_field_name); - ft->id = BT_CTF_FIELD_TYPE_ID_SEQUENCE; - seq_ft->element_ft = bt_ctf_object_get_ref(element_ft); - seq_ft->length_field_name = g_string_new(length_field_name); - bt_ctf_field_type_common_initialize(ft, false, release_func, methods); - BT_LOGD("Initialized common sequence field type object: addr=%p, " - "element-ft-addr=%p, length-field-name=\"%s\"", - ft, element_ft, length_field_name); -} - -BT_HIDDEN -void bt_ctf_field_type_common_variant_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *tag_ft, - const char *tag_name, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(!tag_name || bt_ctf_identifier_is_valid(tag_name)); - BT_LOGD("Initializing common variant field type object: " - "tag-ft-addr=%p, tag-field-name=\"%s\"", - tag_ft, tag_name); - ft->id = BT_CTF_FIELD_TYPE_ID_VARIANT; - var_ft->tag_name = g_string_new(tag_name); - var_ft->choice_name_to_index = g_hash_table_new(NULL, NULL); - var_ft->choices = g_array_new(FALSE, TRUE, - sizeof(struct bt_ctf_field_type_common_variant_choice)); - - if (tag_ft) { - var_ft->tag_ft = bt_ctf_object_get_ref(tag_ft); - } - - bt_ctf_field_type_common_initialize(ft, true, release_func, methods); - /* A variant's alignment is undefined */ - ft->alignment = 0; - BT_LOGD("Initialized common variant field type object: addr=%p, " - "tag-ft-addr=%p, tag-field-name=\"%s\"", - ft, tag_ft, tag_name); -} - -BT_HIDDEN -void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_integer *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying integer field type object: addr=%p", ft); - BT_LOGD_STR("Putting mapped clock class."); - bt_ctf_object_put_ref(ft->mapped_clock_class); - g_free(ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_floating_point *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying floating point number field type object: addr=%p", ft); - g_free(ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_enumeration *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying enumeration field type object: addr=%p", ft); - g_ptr_array_free(ft->entries, TRUE); - BT_LOGD_STR("Putting container field type."); - bt_ctf_object_put_ref(ft->container_ft); - g_free(ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_string *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying string field type object: addr=%p", ft); - g_free(ft); -} - -static -void bt_ctf_field_type_common_structure_field_finalize( - struct bt_ctf_field_type_common_structure_field *field) -{ - if (!field) { - return; - } - - BT_LOGD("Finalizing structure field type's field: " - "addr=%p, field-ft-addr=%p, field-name=\"%s\"", - field, field->type, g_quark_to_string(field->name)); - BT_LOGD_STR("Putting field type."); - bt_ctf_object_put_ref(field->type); -} - -BT_HIDDEN -void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_structure *ft = (void *) obj; - uint64_t i; - - if (!ft) { - return; - } - - BT_LOGD("Destroying structure field type object: addr=%p", ft); - - if (ft->fields) { - for (i = 0; i < ft->fields->len; i++) { - bt_ctf_field_type_common_structure_field_finalize( - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - ft, i)); - } - - g_array_free(ft->fields, TRUE); - } - - if (ft->field_name_to_index) { - g_hash_table_destroy(ft->field_name_to_index); - } - - g_free(ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_array *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying array field type object: addr=%p", ft); - BT_LOGD_STR("Putting element field type."); - bt_ctf_object_put_ref(ft->element_ft); - g_free(ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_sequence *ft = (void *) obj; - - if (!ft) { - return; - } - - BT_LOGD("Destroying sequence field type object: addr=%p", ft); - BT_LOGD_STR("Putting element field type."); - bt_ctf_object_put_ref(ft->element_ft); - g_string_free(ft->length_field_name, TRUE); - BT_LOGD_STR("Putting length field path."); - bt_ctf_object_put_ref(ft->length_field_path); - g_free(ft); -} - -static -void bt_ctf_field_type_common_variant_choice_finalize( - struct bt_ctf_field_type_common_variant_choice *choice) -{ - if (!choice) { - return; - } - - BT_LOGD("Finalizing variant field type's choice: " - "addr=%p, field-ft-addr=%p, field-name=\"%s\"", - choice, choice->type, g_quark_to_string(choice->name)); - BT_LOGD_STR("Putting field type."); - bt_ctf_object_put_ref(choice->type); - - if (choice->ranges) { - g_array_free(choice->ranges, TRUE); - } -} - -BT_HIDDEN -void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_common_variant *ft = (void *) obj; - uint64_t i; - - if (!ft) { - return; - } - - BT_LOGD("Destroying variant field type object: addr=%p", ft); - - if (ft->choices) { - for (i = 0; i < ft->choices->len; i++) { - bt_ctf_field_type_common_variant_choice_finalize( - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - ft, i)); - } - - g_array_free(ft->choices, TRUE); - } - - if (ft->choice_name_to_index) { - g_hash_table_destroy(ft->choice_name_to_index); - } - - if (ft->tag_name) { - g_string_free(ft->tag_name, TRUE); - } - - BT_LOGD_STR("Putting tag field type."); - bt_ctf_object_put_ref(ft->tag_ft); - BT_LOGD_STR("Putting tag field path."); - bt_ctf_object_put_ref(ft->tag_field_path); - g_free(ft); -} - -struct range_overlap_query { - union { - uint64_t _unsigned; - int64_t _signed; - } range_start; - - union { - uint64_t _unsigned; - int64_t _signed; - } range_end; - int overlaps; - GQuark mapping_name; -}; - -static -void check_ranges_overlap(gpointer element, gpointer query) -{ - struct bt_ctf_enumeration_mapping *mapping = element; - struct range_overlap_query *overlap_query = query; - - if (mapping->range_start._signed <= overlap_query->range_end._signed - && overlap_query->range_start._signed <= - mapping->range_end._signed) { - overlap_query->overlaps = 1; - overlap_query->mapping_name = mapping->string; - } - - overlap_query->overlaps |= - mapping->string == overlap_query->mapping_name; - - if (overlap_query->overlaps) { - BT_LOGV("Overlapping enumeration field type mappings: " - "mapping-name=\"%s\", " - "mapping-a-range-start=%" PRId64 ", " - "mapping-a-range-end=%" PRId64 ", " - "mapping-b-range-start=%" PRId64 ", " - "mapping-b-range-end=%" PRId64, - g_quark_to_string(mapping->string), - mapping->range_start._signed, - mapping->range_end._signed, - overlap_query->range_start._signed, - overlap_query->range_end._signed); - } -} - -static -void check_ranges_overlap_unsigned(gpointer element, gpointer query) -{ - struct bt_ctf_enumeration_mapping *mapping = element; - struct range_overlap_query *overlap_query = query; - - if (mapping->range_start._unsigned <= overlap_query->range_end._unsigned - && overlap_query->range_start._unsigned <= - mapping->range_end._unsigned) { - overlap_query->overlaps = 1; - overlap_query->mapping_name = mapping->string; - } - - overlap_query->overlaps |= - mapping->string == overlap_query->mapping_name; - - if (overlap_query->overlaps) { - BT_LOGW("Overlapping enumeration field type mappings: " - "mapping-name=\"%s\", " - "mapping-a-range-start=%" PRIu64 ", " - "mapping-a-range-end=%" PRIu64 ", " - "mapping-b-range-start=%" PRIu64 ", " - "mapping-b-range-end=%" PRIu64, - g_quark_to_string(mapping->string), - mapping->range_start._unsigned, - mapping->range_end._unsigned, - overlap_query->range_start._unsigned, - overlap_query->range_end._unsigned); - } -} - -static -gint compare_enumeration_mappings_signed(struct bt_ctf_enumeration_mapping **a, - struct bt_ctf_enumeration_mapping **b) -{ - return ((*a)->range_start._signed < (*b)->range_start._signed) ? -1 : 1; -} - -static -gint compare_enumeration_mappings_unsigned(struct bt_ctf_enumeration_mapping **a, - struct bt_ctf_enumeration_mapping **b) -{ - return ((*a)->range_start._unsigned < (*b)->range_start._unsigned) ? -1 : 1; -} - -static -int add_structure_variant_member(GArray *members, - GHashTable *field_name_to_index, - struct bt_ctf_field_type_common *field_type, const char *field_name, - bool is_variant) -{ - int ret = 0; - GQuark name_quark = g_quark_from_string(field_name); - struct bt_ctf_field_type_common **member_ft; - GQuark *member_name; - - /* Make sure structure does not contain a field of the same name */ - if (g_hash_table_lookup_extended(field_name_to_index, - GUINT_TO_POINTER(name_quark), NULL, NULL)) { - BT_LOGW("Structure or variant field type already contains a field type with this name: " - "field-name=\"%s\"", field_name); - ret = -1; - goto end; - } - - g_array_set_size(members, members->len + 1); - - if (is_variant) { - struct bt_ctf_field_type_common_variant_choice *choice = - &g_array_index(members, - struct bt_ctf_field_type_common_variant_choice, - members->len - 1); - - member_ft = &choice->type; - member_name = &choice->name; - BT_ASSERT(!choice->ranges); - choice->ranges = g_array_new(FALSE, TRUE, - sizeof(struct bt_ctf_field_type_common_variant_choice_range)); - BT_ASSERT(choice->ranges); - } else { - struct bt_ctf_field_type_common_structure_field *field = - &g_array_index(members, - struct bt_ctf_field_type_common_structure_field, - members->len - 1); - - member_ft = &field->type; - member_name = &field->name; - } - - *member_name = name_quark; - *member_ft = bt_ctf_object_get_ref(field_type); - g_hash_table_insert(field_name_to_index, - GUINT_TO_POINTER(name_quark), - GUINT_TO_POINTER(members->len - 1)); - BT_LOGV("Added structure/variant field type member: member-ft-addr=%p, " - "member-name=\"%s\"", field_type, field_name); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - if (int_ft->mapped_clock_class && int_ft->is_signed) { - BT_LOGW("Invalid integer field type: cannot be signed and have a mapped clock class: " - "ft-addr=%p, clock-class-addr=%p, clock-class-name=\"%s\"", - ft, int_ft->mapped_clock_class, - bt_ctf_clock_class_get_name(int_ft->mapped_clock_class)); - ret = -1; - goto end; - } - -end: - return ret; -} - -static -void bt_ctf_field_type_enum_iter_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_field_type_enumeration_mapping_iterator *iter = - container_of(obj, - struct bt_ctf_field_type_enumeration_mapping_iterator, - base); - - BT_LOGD("Destroying enumeration field type mapping iterator: addr=%p", - obj); - BT_LOGD_STR("Putting parent enumeration field type."); - bt_ctf_object_put_ref(iter->enumeration_ft); - g_free(iter); -} - -static -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_find_mappings_type( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_field_type_enumeration_mapping_iterator_type iterator_type) -{ - struct bt_ctf_field_type_enumeration_mapping_iterator *iter = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, - "Field type"); - iter = g_new0(struct bt_ctf_field_type_enumeration_mapping_iterator, 1); - if (!iter) { - BT_LOGE_STR("Failed to allocate one enumeration field type mapping."); - goto end; - } - - bt_ctf_object_init_shared(&iter->base, bt_ctf_field_type_enum_iter_destroy); - iter->enumeration_ft = bt_ctf_object_get_ref(ft); - iter->index = -1; - iter->type = iterator_type; - -end: - return iter; -} - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_find_mappings_by_name( - struct bt_ctf_field_type_common *ft, const char *name) -{ - struct bt_ctf_field_type_enumeration_mapping_iterator *iter; - - iter = bt_ctf_field_type_common_enumeration_find_mappings_type( - ft, CTF_ITERATOR_BY_NAME); - if (!iter) { - BT_LOGW("Cannot create enumeration field type mapping iterator: " - "ft-addr=%p, mapping-name=\"%s\"", ft, name); - goto error; - } - - iter->u.name_quark = g_quark_try_string(name); - if (!iter->u.name_quark) { - /* - * No results are possible, set the iterator's position at the - * end. - */ - iter->index = iter->enumeration_ft->entries->len; - } - - return iter; - -error: - bt_ctf_object_put_ref(iter); - return NULL; -} - -static -struct bt_ctf_enumeration_mapping *bt_ctf_field_type_common_enumeration_get_mapping_by_index( - struct bt_ctf_field_type_common *ft, uint64_t index) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_enumeration_mapping *mapping = NULL; - - if (index >= enum_ft->entries->len) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "addr=%p, index=%" PRIu64 ", count=%u", - ft, index, enum_ft->entries->len); - goto end; - } - - mapping = g_ptr_array_index(enum_ft->entries, index); - -end: - return mapping; -} - -BT_HIDDEN -int bt_ctf_field_type_enumeration_mapping_iterator_next( - struct bt_ctf_field_type_enumeration_mapping_iterator *iter) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = iter->enumeration_ft; - int i, ret = 0, len; - - BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); - len = enum_ft->entries->len; - for (i = iter->index + 1; i < len; i++) { - struct bt_ctf_enumeration_mapping *mapping = - bt_ctf_field_type_common_enumeration_get_mapping_by_index( - BT_CTF_TO_COMMON(enum_ft), i); - - switch (iter->type) { - case CTF_ITERATOR_BY_NAME: - if (mapping->string == iter->u.name_quark) { - iter->index = i; - goto end; - } - break; - case CTF_ITERATOR_BY_SIGNED_VALUE: - { - int64_t value = iter->u.signed_value; - - if (value >= mapping->range_start._signed && - value <= mapping->range_end._signed) { - iter->index = i; - goto end; - } - break; - } - case CTF_ITERATOR_BY_UNSIGNED_VALUE: - { - uint64_t value = iter->u.unsigned_value; - - if (value >= mapping->range_start._unsigned && - value <= mapping->range_end._unsigned) { - iter->index = i; - goto end; - } - break; - } - default: - BT_LOGF("Invalid enumeration field type mapping iterator type: " - "type=%d", iter->type); - abort(); - } - } - - ret = -1; - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value( - struct bt_ctf_field_type_common *ft, int64_t value) -{ - struct bt_ctf_field_type_enumeration_mapping_iterator *iter; - - iter = bt_ctf_field_type_common_enumeration_find_mappings_type( - ft, CTF_ITERATOR_BY_SIGNED_VALUE); - if (!iter) { - BT_LOGW("Cannot create enumeration field type mapping iterator: " - "ft-addr=%p, value=%" PRId64, ft, value); - goto error; - } - - if (bt_ctf_field_type_common_integer_is_signed( - BT_CTF_TO_COMMON(iter->enumeration_ft->container_ft)) != 1) { - BT_LOGW("Invalid parameter: enumeration field type is unsigned: " - "enum-ft-addr=%p, int-ft-addr=%p", - ft, iter->enumeration_ft->container_ft); - goto error; - } - - iter->u.signed_value = value; - return iter; - -error: - bt_ctf_object_put_ref(iter); - return NULL; -} - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value( - struct bt_ctf_field_type_common *ft, uint64_t value) -{ - struct bt_ctf_field_type_enumeration_mapping_iterator *iter; - - iter = bt_ctf_field_type_common_enumeration_find_mappings_type( - ft, CTF_ITERATOR_BY_UNSIGNED_VALUE); - if (!iter) { - BT_LOGW("Cannot create enumeration field type mapping iterator: " - "ft-addr=%p, value=%" PRIu64, ft, value); - goto error; - } - - if (bt_ctf_field_type_common_integer_is_signed( - BT_CTF_TO_COMMON(iter->enumeration_ft->container_ft)) != 0) { - BT_LOGW("Invalid parameter: enumeration field type is signed: " - "enum-ft-addr=%p, int-ft-addr=%p", - ft, iter->enumeration_ft->container_ft); - goto error; - } - - iter->u.unsigned_value = value; - return iter; - -error: - bt_ctf_object_put_ref(iter); - return NULL; -} - -BT_HIDDEN -int bt_ctf_field_type_enumeration_mapping_iterator_signed_get( - struct bt_ctf_field_type_enumeration_mapping_iterator *iter, - const char **mapping_name, int64_t *range_begin, - int64_t *range_end) -{ - BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); - BT_CTF_ASSERT_PRE(iter->index != -1, - "Invalid enumeration field type mapping iterator access: " - "addr=%p, position=-1", iter); - return bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( - (void *) iter->enumeration_ft, iter->index, - mapping_name, range_begin, range_end); -} - -BT_HIDDEN -int bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get( - struct bt_ctf_field_type_enumeration_mapping_iterator *iter, - const char **mapping_name, uint64_t *range_begin, - uint64_t *range_end) -{ - BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); - BT_CTF_ASSERT_PRE(iter->index != -1, - "Invalid enumeration field type mapping iterator access: " - "addr=%p, position=-1", iter); - return bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( - (void *) iter->enumeration_ft, iter->index, - mapping_name, range_begin, range_end); -} - -/* - * Note: This algorithm is O(n^2) vs number of enumeration mappings. - * Only used when freezing an enumeration. - */ -static -void bt_ctf_field_type_common_enumeration_set_range_overlap( - struct bt_ctf_field_type_common_enumeration *ft) -{ - int64_t i, j, len; - int is_signed; - - BT_LOGV("Setting enumeration field type's overlap flag: addr=%p", - ft); - len = ft->entries->len; - is_signed = bt_ctf_field_type_common_integer_is_signed( - BT_CTF_TO_COMMON(ft->container_ft)); - - for (i = 0; i < len; i++) { - for (j = i + 1; j < len; j++) { - struct bt_ctf_enumeration_mapping *mapping[2]; - - mapping[0] = bt_ctf_field_type_common_enumeration_get_mapping_by_index( - BT_CTF_TO_COMMON(ft), i); - mapping[1] = bt_ctf_field_type_common_enumeration_get_mapping_by_index( - BT_CTF_TO_COMMON(ft), j); - if (is_signed) { - if (mapping[0]->range_start._signed - <= mapping[1]->range_end._signed - && mapping[0]->range_end._signed - >= mapping[1]->range_start._signed) { - ft->has_overlapping_ranges = BT_TRUE; - goto end; - } - } else { - if (mapping[0]->range_start._unsigned - <= mapping[1]->range_end._unsigned - && mapping[0]->range_end._unsigned - >= mapping[1]->range_start._unsigned) { - ft->has_overlapping_ranges = BT_TRUE; - goto end; - } - } - } - } - -end: - if (ft->has_overlapping_ranges) { - BT_LOGV_STR("Enumeration field type has overlapping ranges."); - } else { - BT_LOGV_STR("Enumeration field type has no overlapping ranges."); - } -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_validate_recursive( - struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - ret = bt_ctf_field_type_common_integer_validate( - BT_CTF_TO_COMMON(enum_ft->container_ft)); - if (ret) { - BT_LOGW("Invalid enumeration field type: container type is invalid: " - "enum-ft-addr=%p, int-ft-addr=%p", - ft, enum_ft->container_ft); - goto end; - } - - /* Ensure enum has entries */ - if (enum_ft->entries->len == 0) { - BT_LOGW("Invalid enumeration field type: no entries: " - "addr=%p", ft); - ret = -1; - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_validate_recursive( - struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - /* Length field name should be set at this point */ - if (seq_ft->length_field_name->len == 0) { - BT_LOGW("Invalid sequence field type: no length field name: " - "addr=%p", ft); - ret = -1; - goto end; - } - - ret = bt_ctf_field_type_common_validate(seq_ft->element_ft); - if (ret) { - BT_LOGW("Invalid sequence field type: invalid element field type: " - "seq-ft-addr=%p, element-ft-add=%p", - ft, seq_ft->element_ft); - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_array_validate_recursive( - struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - ret = bt_ctf_field_type_common_validate(array_ft->element_ft); - if (ret) { - BT_LOGW("Invalid array field type: invalid element field type: " - "array-ft-addr=%p, element-ft-add=%p", - ft, array_ft->element_ft); - } - - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_validate_recursive( - struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - struct bt_ctf_field_type_common *child_ft = NULL; - int64_t field_count = - bt_ctf_field_type_common_structure_get_field_count(ft); - int64_t i; - - BT_ASSERT(field_count >= 0); - - for (i = 0; i < field_count; ++i) { - const char *field_name; - - ret = bt_ctf_field_type_common_structure_borrow_field_by_index(ft, - &field_name, &child_ft, i); - BT_ASSERT(ret == 0); - ret = bt_ctf_field_type_common_validate(child_ft); - if (ret) { - BT_LOGW("Invalid structure field type: " - "a contained field type is invalid: " - "struct-ft-addr=%p, field-ft-addr=%p, " - "field-name=\"%s\", field-index=%" PRId64, - ft, child_ft, field_name, i); - goto end; - } - } - -end: - return ret; -} - -static -bt_bool bt_ctf_field_type_common_enumeration_has_overlapping_ranges( - struct bt_ctf_field_type_common_enumeration *enum_ft) -{ - if (!enum_ft->common.frozen) { - bt_ctf_field_type_common_enumeration_set_range_overlap(enum_ft); - } - - return enum_ft->has_overlapping_ranges; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_validate_recursive( - struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - int64_t field_count; - struct bt_ctf_field_type_common *child_ft = NULL; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - int64_t i; - - if (var_ft->tag_name->len == 0) { - BT_LOGW("Invalid variant field type: no tag field name: " - "addr=%p", ft); - ret = -1; - goto end; - } - - if (!var_ft->tag_ft) { - BT_LOGW("Invalid variant field type: no tag field type: " - "addr=%p, tag-field-name=\"%s\"", var_ft, - var_ft->tag_name->str); - ret = -1; - goto end; - } - - if (bt_ctf_field_type_common_enumeration_has_overlapping_ranges(var_ft->tag_ft)) { - BT_LOGW("Invalid variant field type: enumeration tag field type has overlapping ranges: " - "variant-ft-addr=%p, tag-field-name=\"%s\", " - "enum-ft-addr=%p", ft, var_ft->tag_name->str, - var_ft->tag_ft); - ret = -1; - goto end; - } - - /* - * It is valid to have a variant field type which does not have - * the fields corresponding to each label in the associated - * enumeration. - * - * It is also valid to have variant field type fields which - * cannot be selected because the variant field type tag has no - * mapping named as such. This scenario, while not ideal, cannot - * cause any error. - * - * If a non-existing field happens to be selected by an - * enumeration while reading a variant field, an error will be - * generated at that point (while reading the stream). - */ - field_count = bt_ctf_field_type_common_variant_get_field_count(ft); - if (field_count < 0) { - BT_LOGW("Invalid variant field type: no fields: " - "addr=%p, tag-field-name=\"%s\"", - ft, var_ft->tag_name->str); - ret = -1; - goto end; - } - - for (i = 0; i < field_count; ++i) { - const char *field_name; - - ret = bt_ctf_field_type_common_variant_borrow_field_by_index(ft, - &field_name, &child_ft, i); - BT_ASSERT(ret == 0); - ret = bt_ctf_field_type_common_validate(child_ft); - if (ret) { - BT_LOGW("Invalid variant field type: " - "a contained field type is invalid: " - "variant-ft-addr=%p, tag-field-name=\"%s\", " - "field-ft-addr=%p, field-name=\"%s\", " - "field-index=%" PRId64, - ft, var_ft->tag_name->str, child_ft, - field_name, i); - goto end; - } - } - -end: - return ret; -} - -/* - * This function validates a given field type without considering - * where this field type is located. It only validates the properties - * of the given field type and the properties of its children if - * applicable. - */ -BT_HIDDEN -int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft) -{ - int ret = 0; - - BT_ASSERT(ft); - - if (ft->valid) { - /* Already marked as valid */ - goto end; - } - - if (ft->methods->validate) { - ret = ft->methods->validate(ft); - } - - if (ret == 0 && ft->frozen) { - /* Field type is valid */ - BT_LOGV("Marking field type as valid: addr=%p", ft); - ft->valid = 1; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, - "Field type"); - return (int) int_ft->size; -} - -BT_HIDDEN -bt_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, - "Field type"); - return int_ft->is_signed; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft, - bt_bool is_signed) -{ - int ret = 0; - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: field type is not an integer field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - int_ft->is_signed = !!is_signed; - BT_LOGV("Set integer field type's signedness: addr=%p, is-signed=%d", - ft, is_signed); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft, - unsigned int size) -{ - int ret = 0; - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: field type is not an integer field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (size == 0 || size > 64) { - BT_LOGW("Invalid parameter: size must be between 1 and 64: " - "addr=%p, size=%u", ft, size); - ret = -1; - goto end; - } - - int_ft->size = size; - BT_LOGV("Set integer field type's size: addr=%p, size=%u", - ft, size); - -end: - return ret; -} - -BT_HIDDEN -enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, - "Field type"); - return int_ft->base; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft, - enum bt_ctf_integer_base base) -{ - int ret = 0; - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: field type is not an integer field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - switch (base) { - case BT_CTF_INTEGER_BASE_UNSPECIFIED: - case BT_CTF_INTEGER_BASE_BINARY: - case BT_CTF_INTEGER_BASE_OCTAL: - case BT_CTF_INTEGER_BASE_DECIMAL: - case BT_CTF_INTEGER_BASE_HEXADECIMAL: - { - int_ft->base = base; - break; - } - default: - BT_LOGW("Invalid parameter: unknown integer field type base: " - "addr=%p, base=%d", ft, base); - ret = -1; - } - - BT_LOGV("Set integer field type's base: addr=%p, base=%s", - ft, bt_ctf_integer_base_string(base)); - -end: - return ret; -} - -BT_HIDDEN -enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, - "Field type"); - return int_ft->encoding; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft, - enum bt_ctf_string_encoding encoding) -{ - int ret = 0; - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: field type is not an integer field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (encoding != BT_CTF_STRING_ENCODING_UTF8 && - encoding != BT_CTF_STRING_ENCODING_ASCII && - encoding != BT_CTF_STRING_ENCODING_NONE) { - BT_LOGW("Invalid parameter: unknown string encoding: " - "addr=%p, encoding=%d", ft, encoding); - ret = -1; - goto end; - } - - int_ft->encoding = encoding; - BT_LOGV("Set integer field type's encoding: addr=%p, encoding=%s", - ft, bt_ctf_string_encoding_string(encoding)); - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, - "Field type"); - return int_ft->mapped_clock_class; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class *clock_class) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - int ret = 0; - - if (!clock_class) { - BT_LOGW_STR("Invalid parameter: clock class is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: field type is not an integer field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - goto end; - } - - if (!bt_ctf_clock_class_is_valid(clock_class)) { - BT_LOGW("Invalid parameter: clock class is invalid: ft-addr=%p" - "clock-class-addr=%p, clock-class-name=\"%s\"", - ft, clock_class, - bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(int_ft->mapped_clock_class); - int_ft->mapped_clock_class = bt_ctf_object_get_ref(clock_class); - BT_LOGV("Set integer field type's mapped clock class: ft-addr=%p, " - "clock-class-addr=%p, clock-class-name=\"%s\"", - ft, clock_class, bt_ctf_clock_class_get_name(clock_class)); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_mapped_clock_class( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class *clock_class) -{ - int ret = 0; - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - ret = bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( - ft, clock_class); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( - struct bt_ctf_field_type_common *ft, uint64_t index, - const char **mapping_name, int64_t *range_begin, - int64_t *range_end) -{ - int ret = 0; - struct bt_ctf_enumeration_mapping *mapping; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, - BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); - mapping = bt_ctf_field_type_common_enumeration_get_mapping_by_index(ft, - index); - if (!mapping) { - /* bt_ctf_field_type_common_enumeration_get_mapping_by_index() logs errors */ - ret = -1; - goto end; - } - - if (mapping_name) { - *mapping_name = g_quark_to_string(mapping->string); - BT_ASSERT(*mapping_name); - } - - if (range_begin) { - *range_begin = mapping->range_start._signed; - } - - if (range_end) { - *range_end = mapping->range_end._signed; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( - struct bt_ctf_field_type_common *ft, uint64_t index, - const char **mapping_name, uint64_t *range_begin, - uint64_t *range_end) -{ - int ret = 0; - struct bt_ctf_enumeration_mapping *mapping; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); - mapping = bt_ctf_field_type_common_enumeration_get_mapping_by_index( - ft, index); - if (!mapping) { - /* bt_ctf_field_type_common_enumeration_get_mapping_by_index() reports any error */ - ret = -1; - goto end; - } - - if (mapping_name) { - *mapping_name = g_quark_to_string(mapping->string); - BT_ASSERT(*mapping_name); - } - - if (range_begin) { - *range_begin = mapping->range_start._unsigned; - } - - if (range_end) { - *range_end = mapping->range_end._unsigned; - } - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_enumeration_borrow_container_field_type( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); - return BT_CTF_TO_COMMON(enum_ft->container_ft); -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_signed_add_mapping( - struct bt_ctf_field_type_common *ft, const char *string, - int64_t range_start, int64_t range_end) -{ - int ret = 0; - GQuark mapping_name; - struct bt_ctf_enumeration_mapping *mapping; - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - char *escaped_string; - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (!string) { - BT_LOGW_STR("Invalid parameter: string is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { - BT_LOGW("Invalid parameter: field type is not an enumeration field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (range_end < range_start) { - BT_LOGW("Invalid parameter: range's end is lesser than range's start: " - "addr=%p, range-start=%" PRId64 ", range-end=%" PRId64, - ft, range_start, range_end); - ret = -1; - goto end; - } - - if (strlen(string) == 0) { - BT_LOGW("Invalid parameter: mapping name is an empty string: " - "enum-ft-addr=%p, mapping-name-addr=%p", ft, - string); - ret = -1; - goto end; - } - - escaped_string = g_strescape(string, NULL); - if (!escaped_string) { - BT_LOGE("Cannot escape mapping name: enum-ft-addr=%p, " - "mapping-name-addr=%p, mapping-name=\"%s\"", - ft, string, string); - ret = -1; - goto end; - } - - mapping = g_new(struct bt_ctf_enumeration_mapping, 1); - if (!mapping) { - BT_LOGE_STR("Failed to allocate one enumeration mapping."); - ret = -1; - goto error_free; - } - mapping_name = g_quark_from_string(escaped_string); - *mapping = (struct bt_ctf_enumeration_mapping) { - .range_start._signed = range_start, - .range_end._signed = range_end, - .string = mapping_name, - }; - g_ptr_array_add(enum_ft->entries, mapping); - g_ptr_array_sort(enum_ft->entries, - (GCompareFunc) compare_enumeration_mappings_signed); - BT_LOGV("Added mapping to signed enumeration field type: addr=%p, " - "name=\"%s\", range-start=%" PRId64 ", " - "range-end=%" PRId64, - ft, string, range_start, range_end); - -error_free: - free(escaped_string); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_unsigned_add_mapping( - struct bt_ctf_field_type_common *ft, const char *string, - uint64_t range_start, uint64_t range_end) -{ - int ret = 0; - GQuark mapping_name; - struct bt_ctf_enumeration_mapping *mapping; - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - char *escaped_string; - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (!string) { - BT_LOGW_STR("Invalid parameter: string is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { - BT_LOGW("Invalid parameter: field type is not an enumeration field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (range_end < range_start) { - BT_LOGW("Invalid parameter: range's end is lesser than range's start: " - "addr=%p, range-start=%" PRIu64 ", range-end=%" PRIu64, - ft, range_start, range_end); - ret = -1; - goto end; - } - - if (strlen(string) == 0) { - BT_LOGW("Invalid parameter: mapping name is an empty string: " - "enum-ft-addr=%p, mapping-name-addr=%p", ft, - string); - ret = -1; - goto end; - } - - escaped_string = g_strescape(string, NULL); - if (!escaped_string) { - BT_LOGE("Cannot escape mapping name: enum-ft-addr=%p, " - "mapping-name-addr=%p, mapping-name=\"%s\"", - ft, string, string); - ret = -1; - goto end; - } - - mapping = g_new(struct bt_ctf_enumeration_mapping, 1); - if (!mapping) { - BT_LOGE_STR("Failed to allocate one enumeration mapping."); - ret = -1; - goto error_free; - } - mapping_name = g_quark_from_string(escaped_string); - *mapping = (struct bt_ctf_enumeration_mapping) { - .range_start._unsigned = range_start, - .range_end._unsigned = range_end, - .string = mapping_name, - }; - g_ptr_array_add(enum_ft->entries, mapping); - g_ptr_array_sort(enum_ft->entries, - (GCompareFunc) compare_enumeration_mappings_unsigned); - BT_LOGV("Added mapping to unsigned enumeration field type: addr=%p, " - "name=\"%s\", range-start=%" PRIu64 ", " - "range-end=%" PRIu64, - ft, string, range_start, range_end); - -error_free: - free(escaped_string); - -end: - return ret; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_enumeration_get_mapping_count( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); - return (int64_t) enum_ft->entries->len; -} - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_get_exponent_digits( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_FLOAT, - "Field type"); - return (int) flt_ft->exp_dig; -} - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_set_exponent_digits( - struct bt_ctf_field_type_common *ft, - unsigned int exponent_digits) -{ - int ret = 0; - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_FLOAT) { - BT_LOGW("Invalid parameter: field type is not a floating point number field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if ((exponent_digits != sizeof(float) * CHAR_BIT - FLT_MANT_DIG) && - (exponent_digits != sizeof(double) * CHAR_BIT - DBL_MANT_DIG) && - (exponent_digits != - sizeof(long double) * CHAR_BIT - LDBL_MANT_DIG)) { - BT_LOGW("Invalid parameter: invalid exponent size: " - "addr=%p, exp-size=%u", ft, exponent_digits); - ret = -1; - goto end; - } - - flt_ft->exp_dig = exponent_digits; - BT_LOGV("Set floating point number field type's exponent size: addr=%p, " - "exp-size=%u", ft, exponent_digits); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_get_mantissa_digits( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_FLOAT, - "Field type"); - return (int) flt_ft->mant_dig; -} - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_set_mantissa_digits( - struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits) -{ - int ret = 0; - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_FLOAT) { - BT_LOGW("Invalid parameter: field type is not a floating point number field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if ((mantissa_digits != FLT_MANT_DIG) && - (mantissa_digits != DBL_MANT_DIG) && - (mantissa_digits != LDBL_MANT_DIG)) { - BT_LOGW("Invalid parameter: invalid mantissa size: " - "addr=%p, mant-size=%u", ft, mantissa_digits); - ret = -1; - goto end; - } - - flt_ft->mant_dig = mantissa_digits; - BT_LOGV("Set floating point number field type's mantissa size: addr=%p, " - "mant-size=%u", ft, mantissa_digits); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_replace_field( - struct bt_ctf_field_type_common *ft, - const char *field_name, - struct bt_ctf_field_type_common *field_type) -{ - int ret = 0; - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - GQuark name_quark; - uint64_t i; - - BT_ASSERT(ft); - BT_ASSERT(field_name); - BT_ASSERT(field_type); - BT_ASSERT(ft->id == BT_CTF_FIELD_TYPE_ID_STRUCT); - name_quark = g_quark_from_string(field_name); - - for (i = 0; i < struct_ft->fields->len; i++) { - struct bt_ctf_field_type_common_structure_field *field = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, i); - - if (field->name == name_quark) { - bt_ctf_object_put_ref(field->type); - field->type = bt_ctf_object_get_ref(field_type); - } - } - - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *field_type, - const char *field_name) -{ - int ret = 0; - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - - /* - * TODO: check that `field_type` does not contain `type`, - * recursively. - */ - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (!field_name) { - BT_LOGW_STR("Invalid parameter: field name is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid parameter: field type is not a structure field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (ft == field_type) { - BT_LOGW("Invalid parameter: structure field type and field type to add are the same: " - "addr=%p", ft); - ret = -1; - goto end; - } - - if (add_structure_variant_member(struct_ft->fields, - struct_ft->field_name_to_index, field_type, field_name, - false)) { - BT_LOGW("Cannot add field to structure field type: " - "struct-ft-addr=%p, field-ft-addr=%p, field-name=\"%s\"", - ft, field_type, field_name); - ret = -1; - goto end; - } - - BT_LOGV("Added structure field type field: struct-ft-addr=%p, " - "field-ft-addr=%p, field-name=\"%s\"", ft, - field_type, field_name); - -end: - return ret; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_structure_get_field_count( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, - "Field type"); - return (int64_t) struct_ft->fields->len; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_borrow_field_by_index( - struct bt_ctf_field_type_common *ft, - const char **field_name, - struct bt_ctf_field_type_common **field_type, uint64_t index) -{ - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common_structure_field *field; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, - "Field type"); - BT_CTF_ASSERT_PRE(index < struct_ft->fields->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u, ft-addr=%p", - index, struct_ft->fields->len, ft); - field = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(struct_ft, index); - - if (field_type) { - *field_type = field->type; - } - - if (field_name) { - *field_name = g_quark_to_string(field->name); - BT_ASSERT(*field_name); - } - - return 0; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_structure_borrow_field_type_by_name( - struct bt_ctf_field_type_common *ft, const char *name) -{ - size_t index; - GQuark name_quark; - struct bt_ctf_field_type_common_structure_field *field; - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common *field_type = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, - "Field type"); - name_quark = g_quark_try_string(name); - if (!name_quark) { - BT_LOGV("No such structure field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - goto end; - } - - if (!g_hash_table_lookup_extended(struct_ft->field_name_to_index, - GUINT_TO_POINTER(name_quark), NULL, (gpointer *) &index)) { - BT_LOGV("No such structure field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - goto end; - } - - field = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, index); - field_type = field->type; - -end: - return field_type; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_tag_field_type( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common *tag_ft = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - - if (!var_ft->tag_ft) { - BT_LOGV("Variant field type has no tag field type: " - "addr=%p", ft); - goto end; - } - - tag_ft = BT_CTF_TO_COMMON(var_ft->tag_ft); - -end: - return tag_ft; -} - -BT_HIDDEN -const char *bt_ctf_field_type_common_variant_get_tag_name( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - const char *tag_name = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - - if (var_ft->tag_name->len == 0) { - BT_LOGV("Variant field type has no tag field name: " - "addr=%p", ft); - goto end; - } - - tag_name = var_ft->tag_name->str; - -end: - return tag_name; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_name( - struct bt_ctf_field_type_common *ft, const char *name) -{ - int ret = 0; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { - BT_LOGW("Invalid parameter: field type is not a variant field type: " - "addr=%p, ft-id=%s", ft, bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (!bt_ctf_identifier_is_valid(name)) { - BT_LOGW("Invalid parameter: tag field name is not a valid CTF identifier: " - "variant-ft-addr=%p, tag-field-name=\"%s\"", - ft, name); - ret = -1; - goto end; - } - - g_string_assign(var_ft->tag_name, name); - BT_LOGV("Set variant field type's tag field name: addr=%p, " - "tag-field-name=\"%s\"", ft, name); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *field_type, - const char *field_name) -{ - size_t i; - int ret = 0; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - GQuark field_name_quark = g_quark_from_string(field_name); - - /* - * TODO: check that `field_type` does not contain `type`, - * recursively. - */ - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { - BT_LOGW("Invalid parameter: field type is not a variant field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (ft == field_type) { - BT_LOGW("Invalid parameter: variant field type and field type to add are the same: " - "addr=%p", ft); - ret = -1; - goto end; - } - - /* The user has explicitly provided a tag; validate against it. */ - if (var_ft->tag_ft) { - int name_found = 0; - - /* Make sure this name is present in the enum tag */ - for (i = 0; i < var_ft->tag_ft->entries->len; i++) { - struct bt_ctf_enumeration_mapping *mapping = - g_ptr_array_index(var_ft->tag_ft->entries, i); - - if (mapping->string == field_name_quark) { - name_found = 1; - break; - } - } - - if (!name_found) { - /* Validation failed */ - BT_LOGW("Invalid parameter: field name does not name a tag field type's mapping: " - "variant-ft-addr=%p, tag-ft-addr=%p, " - "tag-field-name=\"%s\"" - "field-ft-addr=%p, field-name=\"%s\"", - ft, var_ft->tag_ft, var_ft->tag_name->str, - field_type, field_name); - ret = -1; - goto end; - } - } - - if (add_structure_variant_member(var_ft->choices, - var_ft->choice_name_to_index, field_type, - field_name, true)) { - BT_LOGW("Cannot add field to variant field type: " - "variant-ft-addr=%p, field-ft-addr=%p, field-name=\"%s\"", - ft, field_type, field_name); - ret = -1; - goto end; - } - - BT_LOGV("Added variant field type field: variant-ft-addr=%p, " - "field-ft-addr=%p, field-name=\"%s\"", ft, - field_type, field_name); - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_by_name( - struct bt_ctf_field_type_common *ft, - const char *field_name) -{ - size_t index; - GQuark name_quark; - struct bt_ctf_field_type_common_variant_choice *choice; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common *field_type = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_NON_NULL(field_name, "Name"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - name_quark = g_quark_try_string(field_name); - if (!name_quark) { - BT_LOGV("No such variant field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, field_name); - goto end; - } - - if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, - GUINT_TO_POINTER(name_quark), NULL, (gpointer *) &index)) { - BT_LOGV("No such variant field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, field_name); - goto end; - } - - choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, index); - field_type = choice->type; - -end: - return field_type; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_variant_get_field_count( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Variant field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - return (int64_t) var_ft->choices->len; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_borrow_field_by_index( - struct bt_ctf_field_type_common *ft, - const char **field_name, - struct bt_ctf_field_type_common **field_type, uint64_t index) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common_variant_choice *choice; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - BT_CTF_ASSERT_PRE(index < var_ft->choices->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u, ft-addr=%p", - index, var_ft->choices->len, ft); - choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, index); - - if (field_type) { - *field_type = choice->type; - } - - if (field_name) { - *field_name = g_quark_to_string(choice->name); - BT_ASSERT(*field_name); - } - - return 0; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_variant_find_choice_index( - struct bt_ctf_field_type_common *ft, uint64_t uval, - bool is_signed) -{ - int64_t ret; - uint64_t i; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - BT_ASSERT(ft); - BT_ASSERT(ft->id == BT_CTF_FIELD_TYPE_ID_VARIANT); - - if (bt_ctf_field_type_common_variant_update_choices(ft)) { - ret = INT64_C(-1); - goto end; - } - - for (i = 0; i < var_ft->choices->len; i++) { - uint64_t range_i; - struct bt_ctf_field_type_common_variant_choice *choice = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - var_ft, i); - - for (range_i = 0; range_i < choice->ranges->len; range_i++) { - struct bt_ctf_field_type_common_variant_choice_range *range = - &g_array_index( - choice->ranges, - struct bt_ctf_field_type_common_variant_choice_range, - range_i); - - if (is_signed) { - int64_t tag_ival = (int64_t) uval; - - if (tag_ival >= range->lower.i && - tag_ival <= range->upper.i) { - goto found; - } - } else { - if (uval >= range->lower.u && - uval <= range->upper.u) { - goto found; - } - } - } - } - - /* Range not found */ - ret = INT64_C(-1); - goto end; - -found: - ret = (int64_t) i; - -end: - return ret; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_array_borrow_element_field_type( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ARRAY, - "Field type"); - BT_ASSERT(array_ft && array_ft->element_ft); - return array_ft->element_ft; -} - -BT_HIDDEN -int bt_ctf_field_type_common_array_set_element_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: array field type is NULL."); - ret = -1; - goto end; - } - - if (!element_ft) { - BT_LOGW_STR("Invalid parameter: element field type is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_ARRAY) { - BT_LOGW("Invalid parameter: field type is not an array field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (array_ft->element_ft) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(array_ft->element_ft); - } - - array_ft->element_ft = bt_ctf_object_get_ref(element_ft); - BT_LOGV("Set array field type's element field type: array-ft-addr=%p, " - "element-ft-addr=%p", ft, element_ft); - -end: - return ret; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ARRAY, - "Field type"); - return (int64_t) array_ft->length; -} - -BT_HIDDEN -struct bt_ctf_field_type_common *bt_ctf_field_type_common_sequence_borrow_element_field_type( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, - "Field type"); - return seq_ft->element_ft; -} - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_set_element_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: sequence field type is NULL."); - ret = -1; - goto end; - } - - if (!element_ft) { - BT_LOGW_STR("Invalid parameter: element field type is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_SEQUENCE) { - BT_LOGW("Invalid parameter: field type is not a sequence field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (seq_ft->element_ft) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(seq_ft->element_ft); - } - - seq_ft->element_ft = element_ft; - bt_ctf_object_get_ref(seq_ft->element_ft); - BT_LOGV("Set sequence field type's element field type: sequence-ft-addr=%p, " - "element-ft-addr=%p", ft, element_ft); - -end: - return ret; -} - -BT_HIDDEN -const char *bt_ctf_field_type_common_sequence_get_length_field_name( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, - "Field type"); - return seq_ft->length_field_name ? - seq_ft->length_field_name->str : NULL; -} - -BT_HIDDEN -enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRING, - "Field type"); - return string_ft->encoding; -} - -BT_HIDDEN -int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft, - enum bt_ctf_string_encoding encoding) -{ - int ret = 0; - struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_STRING) { - BT_LOGW("Invalid parameter: field type is not a string field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - if (encoding != BT_CTF_STRING_ENCODING_UTF8 && - encoding != BT_CTF_STRING_ENCODING_ASCII) { - BT_LOGW("Invalid parameter: unknown string encoding: " - "addr=%p, encoding=%d", ft, encoding); - ret = -1; - goto end; - } - - string_ft->encoding = encoding; - BT_LOGV("Set string field type's encoding: addr=%p, encoding=%s", - ft, bt_ctf_string_encoding_string(encoding)); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *ft) -{ - int ret; - enum bt_ctf_field_type_id type_id; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - - if (ft->frozen) { - ret = (int) ft->alignment; - goto end; - } - - type_id = bt_ctf_field_type_common_get_type_id(ft); - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - { - struct bt_ctf_field_type_common *element_ft = - bt_ctf_field_type_common_sequence_borrow_element_field_type(ft); - - BT_ASSERT(element_ft); - ret = bt_ctf_field_type_common_get_alignment(element_ft); - break; - } - case BT_CTF_FIELD_TYPE_ID_ARRAY: - { - struct bt_ctf_field_type_common *element_ft = - bt_ctf_field_type_common_array_borrow_element_field_type(ft); - - BT_ASSERT(element_ft); - ret = bt_ctf_field_type_common_get_alignment(element_ft); - break; - } - case BT_CTF_FIELD_TYPE_ID_STRUCT: - { - int64_t i, element_count; - - element_count = bt_ctf_field_type_common_structure_get_field_count( - ft); - BT_ASSERT(element_count >= 0); - - for (i = 0; i < element_count; i++) { - struct bt_ctf_field_type_common *field = NULL; - int field_alignment; - - ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - ft, NULL, &field, i); - BT_ASSERT(ret == 0); - BT_ASSERT(field); - field_alignment = bt_ctf_field_type_common_get_alignment( - field); - if (field_alignment < 0) { - ret = field_alignment; - goto end; - } - - ft->alignment = MAX(field_alignment, ft->alignment); - } - ret = (int) ft->alignment; - break; - } - case BT_CTF_FIELD_TYPE_ID_UNKNOWN: - BT_LOGW("Invalid parameter: unknown field type ID: " - "addr=%p, ft-id=%d", ft, type_id); - ret = -1; - break; - default: - ret = (int) ft->alignment; - break; - } - -end: - return ret; -} - -static inline -int is_power_of_two(unsigned int value) -{ - return ((value & (value - 1)) == 0) && value > 0; -} - -BT_HIDDEN -int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft, - unsigned int alignment) -{ - int ret = 0; - enum bt_ctf_field_type_id type_id; - - /* Alignment must be a power of two */ - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (!is_power_of_two(alignment)) { - BT_LOGW("Invalid parameter: alignment is not a power of two: " - "addr=%p, align=%u", ft, alignment); - ret = -1; - goto end; - } - - type_id = bt_ctf_field_type_common_get_type_id(ft); - if (type_id == BT_CTF_FIELD_TYPE_ID_UNKNOWN) { - BT_LOGW("Invalid parameter: unknown field type ID: " - "addr=%p, ft-id=%d", ft, type_id); - ret = -1; - goto end; - } - - if (ft->id == BT_CTF_FIELD_TYPE_ID_STRING && alignment != CHAR_BIT) { - BT_LOGW("Invalid parameter: alignment must be %u for a string field type: " - "addr=%p, align=%u", CHAR_BIT, ft, alignment); - ret = -1; - goto end; - } - - if (type_id == BT_CTF_FIELD_TYPE_ID_VARIANT || - type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE || - type_id == BT_CTF_FIELD_TYPE_ID_ARRAY) { - /* Setting an alignment on these types makes no sense */ - BT_LOGW("Invalid parameter: cannot set the alignment of this field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - ft->alignment = alignment; - ret = 0; - BT_LOGV("Set field type's alignment: addr=%p, align=%u", - ft, alignment); - -end: - return ret; -} - -BT_HIDDEN -enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order( - struct bt_ctf_field_type_common *ft) -{ - enum bt_ctf_byte_order ret = BT_CTF_BYTE_ORDER_UNKNOWN; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_INTEGER: - { - struct bt_ctf_field_type_common_integer *integer = - BT_CTF_FROM_COMMON(ft); - - ret = integer->user_byte_order; - break; - } - case BT_CTF_FIELD_TYPE_ID_ENUM: - { - struct bt_ctf_field_type_common_enumeration *enum_ft = - BT_CTF_FROM_COMMON(ft); - - ret = bt_ctf_field_type_common_get_byte_order( - BT_CTF_TO_COMMON(enum_ft->container_ft)); - break; - } - case BT_CTF_FIELD_TYPE_ID_FLOAT: - { - struct bt_ctf_field_type_common_floating_point *floating_point = - BT_CTF_FROM_COMMON(ft); - ret = floating_point->user_byte_order; - break; - } - default: - BT_LOGW("Invalid parameter: cannot get the byte order of this field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - goto end; - } - - BT_ASSERT(ret == BT_CTF_BYTE_ORDER_NATIVE || - ret == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN || - ret == BT_CTF_BYTE_ORDER_BIG_ENDIAN || - ret == BT_CTF_BYTE_ORDER_NETWORK); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order) -{ - int ret = 0; - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->frozen) { - BT_LOGW("Invalid parameter: field type is frozen: addr=%p", - ft); - ret = -1; - goto end; - } - - if (byte_order != BT_CTF_BYTE_ORDER_NATIVE && - byte_order != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN && - byte_order != BT_CTF_BYTE_ORDER_BIG_ENDIAN && - byte_order != BT_CTF_BYTE_ORDER_NETWORK) { - BT_LOGW("Invalid parameter: invalid byte order: " - "addr=%p, bo=%s", ft, - bt_ctf_byte_order_string(byte_order)); - ret = -1; - goto end; - } - - if (ft->methods->set_byte_order) { - ft->methods->set_byte_order(ft, byte_order); - } - - BT_LOGV("Set field type's byte order: addr=%p, bo=%s", - ft, bt_ctf_byte_order_string(byte_order)); - -end: - return ret; -} - -BT_HIDDEN -enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id( - struct bt_ctf_field_type_common *ft) -{ - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - return ft->id; -} - -BT_HIDDEN -void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft) -{ - if (!ft || ft->frozen) { - return; - } - - BT_ASSERT(ft->methods->freeze); - ft->methods->freeze(ft); -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_signed( - struct bt_ctf_field_type_common_variant *var_ft, - int64_t tag_value) -{ - struct bt_ctf_field_type_common *field_type = NULL; - GQuark field_name_quark; - gpointer index; - struct bt_ctf_field_type_common_variant_choice *choice; - struct range_overlap_query query = { - .range_start._signed = tag_value, - .range_end._signed = tag_value, - .mapping_name = 0, - .overlaps = 0, - }; - - g_ptr_array_foreach(var_ft->tag_ft->entries, check_ranges_overlap, - &query); - if (!query.overlaps) { - goto end; - } - - field_name_quark = query.mapping_name; - if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, - GUINT_TO_POINTER(field_name_quark), NULL, &index)) { - goto end; - } - - choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, - (size_t) index); - field_type = choice->type; - -end: - return field_type; -} - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_unsigned( - struct bt_ctf_field_type_common_variant *var_ft, - uint64_t tag_value) -{ - struct bt_ctf_field_type_common *field_type = NULL; - GQuark field_name_quark; - gpointer index; - struct bt_ctf_field_type_common_variant_choice *choice; - struct range_overlap_query query = { - .range_start._unsigned = tag_value, - .range_end._unsigned = tag_value, - .mapping_name = 0, - .overlaps = 0, - }; - - g_ptr_array_foreach(var_ft->tag_ft->entries, - check_ranges_overlap_unsigned, &query); - if (!query.overlaps) { - goto end; - } - - field_name_quark = query.mapping_name; - if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, - GUINT_TO_POINTER(field_name_quark), NULL, &index)) { - goto end; - } - - choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, - (size_t) index); - field_type = choice->type; - -end: - return field_type; -} - -BT_HIDDEN -struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common *ft_copy = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_ASSERT(ft->methods->copy); - ft_copy = ft->methods->copy(ft); - if (!ft_copy) { - BT_LOGE_STR("Cannot copy field type."); - goto end; - } - - ft_copy->alignment = ft->alignment; - -end: - return ft_copy; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_get_field_name_index( - struct bt_ctf_field_type_common *ft, const char *name) -{ - int ret; - size_t index; - GQuark name_quark; - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, - "Field type"); - - name_quark = g_quark_try_string(name); - if (!name_quark) { - BT_LOGV("No such structure field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - ret = -1; - goto end; - } - - if (!g_hash_table_lookup_extended(struct_ft->field_name_to_index, - GUINT_TO_POINTER(name_quark), - NULL, (gpointer *) &index)) { - BT_LOGV("No such structure field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - ret = -1; - goto end; - } - - ret = (int) index; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_get_field_name_index( - struct bt_ctf_field_type_common *ft, const char *name) -{ - int ret; - size_t index; - GQuark name_quark; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - name_quark = g_quark_try_string(name); - if (!name_quark) { - BT_LOGV("No such variant field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - ret = -1; - goto end; - } - - if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, - GUINT_TO_POINTER(name_quark), - NULL, (gpointer *) &index)) { - BT_LOGV("No such variant field type field name: " - "ft-addr=%p, field-name=\"%s\"", - ft, name); - ret = -1; - goto end; - } - - ret = (int) index; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_set_length_field_path( - struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path) -{ - int ret = 0; - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_SEQUENCE) { - BT_LOGW("Invalid parameter: field type is not a sequence field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - bt_ctf_object_get_ref(path); - BT_CTF_OBJECT_MOVE_REF(seq_ft->length_field_path, path); - BT_LOGV("Set sequence field type's length field path: ft-addr=%p, " - "field-path-addr=%p", ft, path); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_field_path( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_path *path) -{ - int ret = 0; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - ret = -1; - goto end; - } - - if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { - BT_LOGW("Invalid parameter: field type is not a variant field type: " - "addr=%p, ft-id=%s", ft, - bt_ctf_field_type_id_string(ft->id)); - ret = -1; - goto end; - } - - bt_ctf_object_get_ref(path); - BT_CTF_OBJECT_MOVE_REF(var_ft->tag_field_path, path); - BT_LOGV("Set variant field type's tag field path: ft-addr=%p, " - "field-path-addr=%p", ft, path); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *tag_ft) -{ - int ret = 0; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - if (!ft) { - BT_LOGW_STR("Invalid parameter: variant field type is NULL."); - ret = -1; - goto end; - } - - if (!tag_ft) { - BT_LOGW_STR("Invalid parameter: tag field type is NULL."); - ret = -1; - goto end; - } - - if (tag_ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { - BT_LOGW("Invalid parameter: tag field type is not an enumeration field type: " - "addr=%p, ft-id=%s", tag_ft, - bt_ctf_field_type_id_string(tag_ft->id)); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(var_ft->tag_ft); - var_ft->tag_ft = bt_ctf_object_get_ref(tag_ft); - BT_LOGV("Set variant field type's tag field type: variant-ft-addr=%p, " - "tag-ft-addr=%p", ft, tag_ft); - -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft) -{ - ft->frozen = 1; -} - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_freeze_recursive( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - BT_LOGD("Freezing enumeration field type object: addr=%p", ft); - bt_ctf_field_type_common_enumeration_set_range_overlap(enum_ft); - bt_ctf_field_type_common_generic_freeze(ft); - BT_LOGD("Freezing enumeration field type object's container field type: int-ft-addr=%p", - enum_ft->container_ft); - bt_ctf_field_type_common_freeze(BT_CTF_TO_COMMON(enum_ft->container_ft)); -} - -BT_HIDDEN -void bt_ctf_field_type_common_structure_freeze_recursive( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - uint64_t i; - - /* Cache the alignment */ - BT_LOGD("Freezing structure field type object: addr=%p", ft); - ft->alignment = bt_ctf_field_type_common_get_alignment(ft); - bt_ctf_field_type_common_generic_freeze(ft); - - for (i = 0; i < struct_ft->fields->len; i++) { - struct bt_ctf_field_type_common_structure_field *field = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, i); - - BT_LOGD("Freezing structure field type field: " - "ft-addr=%p, name=\"%s\"", - field->type, g_quark_to_string(field->name)); - bt_ctf_field_type_common_freeze(field->type); - } -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_update_choices(struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - uint64_t i; - int ret = 0; - bool is_signed; - - if (ft->frozen && var_ft->choices_up_to_date) { - goto end; - } - - BT_ASSERT(var_ft->tag_ft); - is_signed = !!var_ft->tag_ft->container_ft->is_signed; - - for (i = 0; i < var_ft->choices->len; i++) { - struct bt_ctf_field_type_common_variant_choice *choice = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, i); - const char *choice_name = g_quark_to_string(choice->name); - struct bt_ctf_field_type_enumeration_mapping_iterator *iter = - bt_ctf_field_type_common_enumeration_find_mappings_by_name( - BT_CTF_TO_COMMON(var_ft->tag_ft), choice_name); - - if (!iter) { - ret = -1; - goto end; - } - - BT_ASSERT(choice->ranges); - g_array_set_size(choice->ranges, 0); - - while (bt_ctf_field_type_enumeration_mapping_iterator_next(iter) == 0) { - struct bt_ctf_field_type_common_variant_choice_range range; - - if (is_signed) { - ret = bt_ctf_field_type_enumeration_mapping_iterator_signed_get( - iter, NULL, - &range.lower.i, &range.upper.i); - } else { - ret = bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get( - iter, NULL, - &range.lower.u, &range.upper.u); - } - - BT_ASSERT(ret == 0); - g_array_append_val(choice->ranges, range); - } - - bt_ctf_object_put_ref(iter); - } - - var_ft->choices_up_to_date = true; - -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_field_type_common_variant_freeze_recursive( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - uint64_t i; - - BT_LOGD("Freezing variant field type object: addr=%p", ft); - bt_ctf_field_type_common_generic_freeze(ft); - - for (i = 0; i < var_ft->choices->len; i++) { - struct bt_ctf_field_type_common_variant_choice *choice = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, i); - - BT_LOGD("Freezing variant field type member: " - "ft-addr=%p, name=\"%s\"", - choice->type, g_quark_to_string(choice->name)); - bt_ctf_field_type_common_freeze(choice->type); - } -} - -BT_HIDDEN -void bt_ctf_field_type_common_array_freeze_recursive( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - /* Cache the alignment */ - BT_LOGD("Freezing array field type object: addr=%p", ft); - ft->alignment = bt_ctf_field_type_common_get_alignment(ft); - bt_ctf_field_type_common_generic_freeze(ft); - BT_LOGD("Freezing array field type object's element field type: element-ft-addr=%p", - array_ft->element_ft); - bt_ctf_field_type_common_freeze(array_ft->element_ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_freeze_recursive( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - /* Cache the alignment */ - BT_LOGD("Freezing sequence field type object: addr=%p", ft); - ft->alignment = bt_ctf_field_type_common_get_alignment(ft); - bt_ctf_field_type_common_generic_freeze(ft); - BT_LOGD("Freezing sequence field type object's element field type: element-ft-addr=%p", - seq_ft->element_ft); - bt_ctf_field_type_common_freeze(seq_ft->element_ft); -} - -BT_HIDDEN -void bt_ctf_field_type_common_integer_set_byte_order( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) -{ - struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); - - int_ft->user_byte_order = byte_order; -} - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); - - bt_ctf_field_type_common_set_byte_order(BT_CTF_TO_COMMON(enum_ft->container_ft), - byte_order); -} - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_set_byte_order( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) -{ - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - - flt_ft->user_byte_order = byte_order; -} - -BT_HIDDEN -void bt_ctf_field_type_common_structure_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order) -{ - int i; - struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); - - for (i = 0; i < struct_ft->fields->len; i++) { - struct bt_ctf_field_type_common_structure_field *field = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - struct_ft, i); - struct bt_ctf_field_type_common *field_type = field->type; - - bt_ctf_field_type_common_set_byte_order(field_type, byte_order); - } -} - -BT_HIDDEN -void bt_ctf_field_type_common_variant_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order) -{ - int i; - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - for (i = 0; i < var_ft->choices->len; i++) { - struct bt_ctf_field_type_common_variant_choice *choice = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - var_ft, i); - struct bt_ctf_field_type_common *field_type = choice->type; - - bt_ctf_field_type_common_set_byte_order(field_type, byte_order); - } -} - -BT_HIDDEN -void bt_ctf_field_type_common_array_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order) -{ - struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); - - bt_ctf_field_type_common_set_byte_order(array_ft->element_ft, byte_order); -} - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - bt_ctf_field_type_common_set_byte_order(seq_ft->element_ft, byte_order); -} - - -BT_HIDDEN -int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - struct bt_ctf_field_type_common_integer *int_ft_a = BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_integer *int_ft_b = BT_CTF_FROM_COMMON(ft_b); - - /* Length */ - if (int_ft_a->size != int_ft_b->size) { - BT_LOGV("Integer field types differ: different sizes: " - "ft-a-size=%u, ft-b-size=%u", - int_ft_a->size, int_ft_b->size); - goto end; - } - - /* Byte order */ - if (int_ft_a->user_byte_order != int_ft_b->user_byte_order) { - BT_LOGV("Integer field types differ: different byte orders: " - "ft-a-bo=%s, ft-b-bo=%s", - bt_ctf_byte_order_string(int_ft_a->user_byte_order), - bt_ctf_byte_order_string(int_ft_b->user_byte_order)); - goto end; - } - - /* Signedness */ - if (int_ft_a->is_signed != int_ft_b->is_signed) { - BT_LOGV("Integer field types differ: different signedness: " - "ft-a-is-signed=%d, ft-b-is-signed=%d", - int_ft_a->is_signed, - int_ft_b->is_signed); - goto end; - } - - /* Base */ - if (int_ft_a->base != int_ft_b->base) { - BT_LOGV("Integer field types differ: different bases: " - "ft-a-base=%s, ft-b-base=%s", - bt_ctf_integer_base_string(int_ft_a->base), - bt_ctf_integer_base_string(int_ft_b->base)); - goto end; - } - - /* Encoding */ - if (int_ft_a->encoding != int_ft_b->encoding) { - BT_LOGV("Integer field types differ: different encodings: " - "ft-a-encoding=%s, ft-b-encoding=%s", - bt_ctf_string_encoding_string(int_ft_a->encoding), - bt_ctf_string_encoding_string(int_ft_b->encoding)); - goto end; - } - - /* Mapped clock class */ - if (int_ft_a->mapped_clock_class) { - if (!int_ft_b->mapped_clock_class) { - BT_LOGV_STR("Integer field types differ: field type A " - "has a mapped clock class, but field type B " - "does not."); - goto end; - } - - if (bt_ctf_clock_class_compare(int_ft_a->mapped_clock_class, - int_ft_b->mapped_clock_class) != 0) { - BT_LOGV_STR("Integer field types differ: different " - "mapped clock classes."); - } - } else { - if (int_ft_b->mapped_clock_class) { - BT_LOGV_STR("Integer field types differ: field type A " - "has no description, but field type B has one."); - goto end; - } - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_compare( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - struct bt_ctf_field_type_common_floating_point *flt_ft_a = - BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_floating_point *flt_ft_b = - BT_CTF_FROM_COMMON(ft_b); - - /* Byte order */ - if (flt_ft_a->user_byte_order != flt_ft_b->user_byte_order) { - BT_LOGV("Floating point number field types differ: different byte orders: " - "ft-a-bo=%s, ft-b-bo=%s", - bt_ctf_byte_order_string(flt_ft_a->user_byte_order), - bt_ctf_byte_order_string(flt_ft_b->user_byte_order)); - goto end; - } - - /* Exponent length */ - if (flt_ft_a->exp_dig != flt_ft_b->exp_dig) { - BT_LOGV("Floating point number field types differ: different exponent sizes: " - "ft-a-exp-size=%u, ft-b-exp-size=%u", - flt_ft_a->exp_dig, flt_ft_b->exp_dig); - goto end; - } - - /* Mantissa length */ - if (flt_ft_a->mant_dig != flt_ft_b->mant_dig) { - BT_LOGV("Floating point number field types differ: different mantissa sizes: " - "ft-a-mant-size=%u, ft-b-mant-size=%u", - flt_ft_a->mant_dig, flt_ft_b->mant_dig); - goto end; - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -static -int compare_enumeration_mappings(struct bt_ctf_enumeration_mapping *mapping_a, - struct bt_ctf_enumeration_mapping *mapping_b) -{ - int ret = 1; - - /* Label */ - if (mapping_a->string != mapping_b->string) { - BT_LOGV("Enumeration field type mappings differ: different names: " - "mapping-a-name=\"%s\", mapping-b-name=\"%s\"", - g_quark_to_string(mapping_a->string), - g_quark_to_string(mapping_b->string)); - goto end; - } - - /* Range start */ - if (mapping_a->range_start._unsigned != - mapping_b->range_start._unsigned) { - BT_LOGV("Enumeration field type mappings differ: different starts of range: " - "mapping-a-range-start-unsigned=%" PRIu64 ", " - "mapping-b-range-start-unsigned=%" PRIu64, - mapping_a->range_start._unsigned, - mapping_b->range_start._unsigned); - goto end; - } - - /* Range end */ - if (mapping_a->range_end._unsigned != - mapping_b->range_end._unsigned) { - BT_LOGV("Enumeration field type mappings differ: different ends of range: " - "mapping-a-range-end-unsigned=%" PRIu64 ", " - "mapping-b-range-end-unsigned=%" PRIu64, - mapping_a->range_end._unsigned, - mapping_b->range_end._unsigned); - goto end; - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - int i; - struct bt_ctf_field_type_common_enumeration *enum_ft_a = - BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_enumeration *enum_ft_b = - BT_CTF_FROM_COMMON(ft_b); - - /* Container field type */ - ret = bt_ctf_field_type_common_compare( - BT_CTF_TO_COMMON(enum_ft_a->container_ft), - BT_CTF_TO_COMMON(enum_ft_b->container_ft)); - if (ret) { - BT_LOGV("Enumeration field types differ: different container field types: " - "ft-a-container-ft-addr=%p, ft-b-container-ft-addr=%p", - enum_ft_a->container_ft, enum_ft_b->container_ft); - goto end; - } - - ret = 1; - - /* Entries */ - if (enum_ft_a->entries->len != enum_ft_b->entries->len) { - goto end; - } - - for (i = 0; i < enum_ft_a->entries->len; ++i) { - struct bt_ctf_enumeration_mapping *mapping_a = - g_ptr_array_index(enum_ft_a->entries, i); - struct bt_ctf_enumeration_mapping *mapping_b = - g_ptr_array_index(enum_ft_b->entries, i); - - if (compare_enumeration_mappings(mapping_a, mapping_b)) { - BT_LOGV("Enumeration field types differ: different mappings: " - "ft-a-mapping-addr=%p, ft-b-mapping-addr=%p, " - "ft-a-mapping-name=\"%s\", ft-b-mapping-name=\"%s\"", - mapping_a, mapping_b, - g_quark_to_string(mapping_a->string), - g_quark_to_string(mapping_b->string)); - goto end; - } - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - struct bt_ctf_field_type_common_string *string_ft_a = BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_string *string_ft_b = BT_CTF_FROM_COMMON(ft_b); - - /* Encoding */ - if (string_ft_a->encoding != string_ft_b->encoding) { - BT_LOGV("String field types differ: different encodings: " - "ft-a-encoding=%s, ft-b-encoding=%s", - bt_ctf_string_encoding_string(string_ft_a->encoding), - bt_ctf_string_encoding_string(string_ft_b->encoding)); - goto end; - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -static -int compare_structure_variant_members( - struct bt_ctf_field_type_common *member_a_ft, - struct bt_ctf_field_type_common *member_b_ft, - GQuark member_a_name, GQuark member_b_name) -{ - int ret = 1; - - /* Label */ - if (member_a_name != member_b_name) { - BT_LOGV("Structure/variant field type fields differ: different names: " - "field-a-name=%s, field-b-name=%s", - g_quark_to_string(member_a_name), - g_quark_to_string(member_b_name)); - goto end; - } - - /* Type */ - ret = bt_ctf_field_type_common_compare(member_a_ft, member_b_ft); - if (ret == 1) { - BT_LOGV("Structure/variant field type fields differ: different field types: " - "field-name=\"%s\", field-a-ft-addr=%p, field-b-ft-addr=%p", - g_quark_to_string(member_a_name), - member_a_ft, member_b_ft); - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_structure_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - int i; - struct bt_ctf_field_type_common_structure *struct_ft_a = - BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_structure *struct_ft_b = - BT_CTF_FROM_COMMON(ft_b); - - /* Alignment */ - if (bt_ctf_field_type_common_get_alignment(ft_a) != - bt_ctf_field_type_common_get_alignment(ft_b)) { - BT_LOGV("Structure field types differ: different alignments: " - "ft-a-align=%u, ft-b-align=%u", - bt_ctf_field_type_common_get_alignment(ft_a), - bt_ctf_field_type_common_get_alignment(ft_b)); - goto end; - } - - /* Fields */ - if (struct_ft_a->fields->len != struct_ft_b->fields->len) { - BT_LOGV("Structure field types differ: different field counts: " - "ft-a-field-count=%u, ft-b-field-count=%u", - struct_ft_a->fields->len, struct_ft_b->fields->len); - goto end; - } - - for (i = 0; i < struct_ft_a->fields->len; ++i) { - struct bt_ctf_field_type_common_structure_field *field_a = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - struct_ft_a, i); - struct bt_ctf_field_type_common_structure_field *field_b = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - struct_ft_b, i); - - ret = compare_structure_variant_members(field_a->type, - field_b->type, field_a->name, field_b->name); - if (ret) { - /* compare_structure_variant_members() logs what differs */ - BT_LOGV_STR("Structure field types differ: different fields."); - goto end; - } - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_variant_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - int i; - struct bt_ctf_field_type_common_variant *var_ft_a = BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_variant *var_ft_b = BT_CTF_FROM_COMMON(ft_b); - - /* Tag name */ - if (strcmp(var_ft_a->tag_name->str, var_ft_b->tag_name->str)) { - BT_LOGV("Variant field types differ: different tag field names: " - "ft-a-tag-field-name=\"%s\", ft-b-tag-field-name=\"%s\"", - var_ft_a->tag_name->str, var_ft_b->tag_name->str); - goto end; - } - - /* Tag type */ - ret = bt_ctf_field_type_common_compare(BT_CTF_TO_COMMON(var_ft_a->tag_ft), - BT_CTF_TO_COMMON(var_ft_b->tag_ft)); - if (ret) { - BT_LOGV("Variant field types differ: different tag field types: " - "ft-a-tag-ft-addr=%p, ft-b-tag-ft-addr=%p", - var_ft_a->tag_ft, var_ft_b->tag_ft); - goto end; - } - - ret = 1; - - /* Fields */ - if (var_ft_a->choices->len != var_ft_b->choices->len) { - BT_LOGV("Variant field types differ: different field counts: " - "ft-a-field-count=%u, ft-b-field-count=%u", - var_ft_a->choices->len, var_ft_b->choices->len); - goto end; - } - - for (i = 0; i < var_ft_a->choices->len; ++i) { - struct bt_ctf_field_type_common_variant_choice *choice_a = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - var_ft_a, i); - struct bt_ctf_field_type_common_variant_choice *choice_b = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - var_ft_b, i); - - ret = compare_structure_variant_members(choice_a->type, - choice_b->type, choice_a->name, choice_b->name); - if (ret) { - /* compare_structure_variant_members() logs what differs */ - BT_LOGV_STR("Variant field types differ: different fields."); - goto end; - } - } - - /* Equal */ - ret = 0; - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_array_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - struct bt_ctf_field_type_common_array *array_ft_a = BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_array *array_ft_b = BT_CTF_FROM_COMMON(ft_b); - - /* Length */ - if (array_ft_a->length != array_ft_b->length) { - BT_LOGV("Structure field types differ: different lengths: " - "ft-a-length=%u, ft-b-length=%u", - array_ft_a->length, array_ft_b->length); - goto end; - } - - /* Element type */ - ret = bt_ctf_field_type_common_compare(array_ft_a->element_ft, - array_ft_b->element_ft); - if (ret == 1) { - BT_LOGV("Array field types differ: different element field types: " - "ft-a-element-ft-addr=%p, ft-b-element-ft-addr=%p", - array_ft_a->element_ft, array_ft_b->element_ft); - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = -1; - struct bt_ctf_field_type_common_sequence *seq_ft_a = BT_CTF_FROM_COMMON(ft_a); - struct bt_ctf_field_type_common_sequence *seq_ft_b = BT_CTF_FROM_COMMON(ft_b); - - /* Length name */ - if (strcmp(seq_ft_a->length_field_name->str, - seq_ft_b->length_field_name->str)) { - BT_LOGV("Sequence field types differ: different length field names: " - "ft-a-length-field-name=\"%s\", " - "ft-b-length-field-name=\"%s\"", - seq_ft_a->length_field_name->str, - seq_ft_b->length_field_name->str); - goto end; - } - - /* Element type */ - ret = bt_ctf_field_type_common_compare(seq_ft_a->element_ft, - seq_ft_b->element_ft); - if (ret == 1) { - BT_LOGV("Sequence field types differ: different element field types: " - "ft-a-element-ft-addr=%p, ft-b-element-ft-addr=%p", - seq_ft_a->element_ft, seq_ft_b->element_ft); - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b) -{ - int ret = 1; - - BT_CTF_ASSERT_PRE_NON_NULL(ft_a, "Field type A"); - BT_CTF_ASSERT_PRE_NON_NULL(ft_b, "Field type B"); - - if (ft_a == ft_b) { - /* Same reference: equal (even if both are NULL) */ - ret = 0; - goto end; - } - - if (!ft_a) { - BT_LOGW_STR("Invalid parameter: field type A is NULL."); - ret = -1; - goto end; - } - - if (!ft_b) { - BT_LOGW_STR("Invalid parameter: field type B is NULL."); - ret = -1; - goto end; - } - - if (ft_a->id != ft_b->id) { - /* Different type IDs */ - BT_LOGV("Field types differ: different IDs: " - "ft-a-addr=%p, ft-b-addr=%p, " - "ft-a-id=%s, ft-b-id=%s", - ft_a, ft_b, - bt_ctf_field_type_id_string(ft_a->id), - bt_ctf_field_type_id_string(ft_b->id)); - goto end; - } - - if (ft_a->id == BT_CTF_FIELD_TYPE_ID_UNKNOWN) { - /* Both have unknown type IDs */ - BT_LOGW_STR("Invalid parameter: field type IDs are unknown."); - goto end; - } - - BT_ASSERT(ft_a->methods->compare); - ret = ft_a->methods->compare(ft_a, ft_b); - if (ret == 1) { - BT_LOGV("Field types differ: ft-a-addr=%p, ft-b-addr=%p", - ft_a, ft_b); - } - -end: - return ret; -} - -BT_HIDDEN -int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft) -{ - int64_t field_count = -1; - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_STRUCT: - field_count = - bt_ctf_field_type_common_structure_get_field_count(ft); - break; - case BT_CTF_FIELD_TYPE_ID_VARIANT: - field_count = - bt_ctf_field_type_common_variant_get_field_count(ft); - break; - case BT_CTF_FIELD_TYPE_ID_ARRAY: - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - /* - * Array and sequence types always contain a single member - * (the element type). - */ - field_count = 1; - break; - default: - break; - } - - return field_count; -} - -BT_HIDDEN -struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index( - struct bt_ctf_field_type_common *ft, int index) -{ - struct bt_ctf_field_type_common *field_type = NULL; - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_STRUCT: - { - int ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - ft, NULL, &field_type, index); - if (ret) { - field_type = NULL; - goto end; - } - break; - } - case BT_CTF_FIELD_TYPE_ID_VARIANT: - { - int ret = bt_ctf_field_type_common_variant_borrow_field_by_index( - ft, NULL, &field_type, index); - if (ret) { - field_type = NULL; - goto end; - } - break; - } - case BT_CTF_FIELD_TYPE_ID_ARRAY: - field_type = - bt_ctf_field_type_common_array_borrow_element_field_type(ft); - break; - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - field_type = - bt_ctf_field_type_common_sequence_borrow_element_field_type(ft); - break; - default: - break; - } - -end: - return field_type; -} - -BT_HIDDEN -int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft, - const char *name) -{ - int field_index = -1; - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_STRUCT: - field_index = bt_ctf_field_type_common_structure_get_field_name_index( - ft, name); - break; - case BT_CTF_FIELD_TYPE_ID_VARIANT: - field_index = bt_ctf_field_type_common_variant_get_field_name_index( - ft, name); - break; - default: - break; - } - - return field_index; -} - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - return var_ft->tag_field_path; -} - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path( - struct bt_ctf_field_type_common *ft) -{ - struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, - "Field type"); - return seq_ft->length_field_path; -} - -BT_HIDDEN -int bt_ctf_field_type_common_validate_single_clock_class( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class **expected_clock_class) -{ - int ret = 0; - - if (!ft) { - goto end; - } - - BT_ASSERT(expected_clock_class); - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_INTEGER: - { - struct bt_ctf_clock_class *mapped_clock_class = - bt_ctf_field_type_common_integer_borrow_mapped_clock_class(ft); - - if (!mapped_clock_class) { - goto end; - } - - if (!*expected_clock_class) { - /* Move reference to output parameter */ - *expected_clock_class = bt_ctf_object_get_ref(mapped_clock_class); - mapped_clock_class = NULL; - BT_LOGV("Setting expected clock class: " - "expected-clock-class-addr=%p", - *expected_clock_class); - } else { - if (mapped_clock_class != *expected_clock_class) { - BT_LOGW("Integer field type is not mapped to " - "the expected clock class: " - "mapped-clock-class-addr=%p, " - "mapped-clock-class-name=\"%s\", " - "expected-clock-class-addr=%p, " - "expected-clock-class-name=\"%s\"", - mapped_clock_class, - bt_ctf_clock_class_get_name(mapped_clock_class), - *expected_clock_class, - bt_ctf_clock_class_get_name(*expected_clock_class)); - bt_ctf_object_put_ref(mapped_clock_class); - ret = -1; - goto end; - } - } - - break; - } - case BT_CTF_FIELD_TYPE_ID_ENUM: - case BT_CTF_FIELD_TYPE_ID_ARRAY: - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - { - struct bt_ctf_field_type_common *sub_ft = NULL; - - switch (ft->id) { - case BT_CTF_FIELD_TYPE_ID_ENUM: - sub_ft = bt_ctf_field_type_common_enumeration_borrow_container_field_type( - ft); - break; - case BT_CTF_FIELD_TYPE_ID_ARRAY: - sub_ft = bt_ctf_field_type_common_array_borrow_element_field_type( - ft); - break; - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - sub_ft = bt_ctf_field_type_common_sequence_borrow_element_field_type( - ft); - break; - default: - BT_LOGF("Unexpected field type ID: id=%d", ft->id); - abort(); - } - - BT_ASSERT(sub_ft); - ret = bt_ctf_field_type_common_validate_single_clock_class(sub_ft, - expected_clock_class); - break; - } - case BT_CTF_FIELD_TYPE_ID_STRUCT: - { - uint64_t i; - int64_t count = bt_ctf_field_type_common_structure_get_field_count( - ft); - - for (i = 0; i < count; i++) { - const char *name; - struct bt_ctf_field_type_common *member_type; - - ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - ft, &name, &member_type, i); - BT_ASSERT(ret == 0); - ret = bt_ctf_field_type_common_validate_single_clock_class( - member_type, expected_clock_class); - if (ret) { - BT_LOGW("Structure field type's field's type " - "is not recursively mapped to the " - "expected clock class: " - "field-ft-addr=%p, field-name=\"%s\"", - member_type, name); - goto end; - } - } - break; - } - case BT_CTF_FIELD_TYPE_ID_VARIANT: - { - uint64_t i; - int64_t count = bt_ctf_field_type_common_variant_get_field_count( - ft); - - for (i = 0; i < count; i++) { - const char *name; - struct bt_ctf_field_type_common *member_type; - - ret = bt_ctf_field_type_common_variant_borrow_field_by_index( - ft, &name, &member_type, i); - BT_ASSERT(ret == 0); - ret = bt_ctf_field_type_common_validate_single_clock_class( - member_type, expected_clock_class); - if (ret) { - BT_LOGW("Variant field type's field's type " - "is not recursively mapped to the " - "expected clock class: " - "field-ft-addr=%p, field-name=\"%s\"", - member_type, name); - goto end; - } - } - break; - } - default: - break; - } - -end: - return ret; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_integer_copy( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_enumeration_copy_recursive( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_floating_point_copy( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_structure_copy_recursive( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_variant_copy_recursive( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_array_copy_recursive( - struct bt_ctf_field_type *ft); - -static -struct bt_ctf_field_type *bt_ctf_field_type_sequence_copy_recursive( - struct bt_ctf_field_type *type); - -static -struct bt_ctf_field_type *bt_ctf_field_type_string_copy( - struct bt_ctf_field_type *type); - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_integer_methods = { - .freeze = bt_ctf_field_type_common_generic_freeze, - .validate = bt_ctf_field_type_common_integer_validate, - .set_byte_order = bt_ctf_field_type_common_integer_set_byte_order, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_integer_copy, - .compare = bt_ctf_field_type_common_integer_compare, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_floating_point_methods = { - .freeze = bt_ctf_field_type_common_generic_freeze, - .validate = NULL, - .set_byte_order = bt_ctf_field_type_common_floating_point_set_byte_order, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_floating_point_copy, - .compare = bt_ctf_field_type_common_floating_point_compare, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_enumeration_methods = { - .freeze = bt_ctf_field_type_common_enumeration_freeze_recursive, - .validate = bt_ctf_field_type_common_enumeration_validate_recursive, - .set_byte_order = bt_ctf_field_type_common_enumeration_set_byte_order_recursive, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_enumeration_copy_recursive, - .compare = bt_ctf_field_type_common_enumeration_compare_recursive, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_string_methods = { - .freeze = bt_ctf_field_type_common_generic_freeze, - .validate = NULL, - .set_byte_order = NULL, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_string_copy, - .compare = bt_ctf_field_type_common_string_compare, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_array_methods = { - .freeze = bt_ctf_field_type_common_array_freeze_recursive, - .validate = bt_ctf_field_type_common_array_validate_recursive, - .set_byte_order = bt_ctf_field_type_common_array_set_byte_order_recursive, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_array_copy_recursive, - .compare = bt_ctf_field_type_common_array_compare_recursive, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_sequence_methods = { - .freeze = bt_ctf_field_type_common_sequence_freeze_recursive, - .validate = bt_ctf_field_type_common_sequence_validate_recursive, - .set_byte_order = bt_ctf_field_type_common_sequence_set_byte_order_recursive, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_sequence_copy_recursive, - .compare = bt_ctf_field_type_common_sequence_compare_recursive, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_structure_methods = { - .freeze = bt_ctf_field_type_common_structure_freeze_recursive, - .validate = bt_ctf_field_type_common_structure_validate_recursive, - .set_byte_order = bt_ctf_field_type_common_structure_set_byte_order_recursive, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_structure_copy_recursive, - .compare = bt_ctf_field_type_common_structure_compare_recursive, -}; - -static struct bt_ctf_field_type_common_methods bt_ctf_field_type_variant_methods = { - .freeze = bt_ctf_field_type_common_variant_freeze_recursive, - .validate = bt_ctf_field_type_common_variant_validate_recursive, - .set_byte_order = bt_ctf_field_type_common_variant_set_byte_order_recursive, - .copy = (bt_ctf_field_type_common_method_copy) - bt_ctf_field_type_variant_copy_recursive, - .compare = bt_ctf_field_type_common_variant_compare_recursive, -}; - -typedef int (*bt_ctf_field_type_serialize_func)(struct bt_ctf_field_type_common *, - struct metadata_context *); - -BT_HIDDEN -int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type, - struct metadata_context *context) -{ - int ret; - struct bt_ctf_field_type_common *type_common = (void *) type; - bt_ctf_field_type_serialize_func serialize_func; - - BT_ASSERT(type); - BT_ASSERT(context); - - /* Make sure field type is valid before serializing it */ - ret = bt_ctf_field_type_common_validate((void *) type); - if (ret) { - BT_LOGW("Cannot serialize field type's metadata: field type is invalid: " - "addr=%p", type); - goto end; - } - - serialize_func = type_common->spec.writer.serialize_func; - ret = serialize_func((void *) type, context); - -end: - return ret; -} - -static -const char *get_encoding_string(enum bt_ctf_string_encoding encoding) -{ - const char *encoding_string; - - switch (encoding) { - case BT_CTF_STRING_ENCODING_NONE: - encoding_string = "none"; - break; - case BT_CTF_STRING_ENCODING_ASCII: - encoding_string = "ASCII"; - break; - case BT_CTF_STRING_ENCODING_UTF8: - encoding_string = "UTF8"; - break; - default: - encoding_string = "unknown"; - break; - } - - return encoding_string; -} - -static -const char *get_integer_base_string(enum bt_ctf_integer_base base) -{ - const char *base_string; - - switch (base) { - case BT_CTF_INTEGER_BASE_DECIMAL: - case BT_CTF_INTEGER_BASE_UNSPECIFIED: - base_string = "decimal"; - break; - case BT_CTF_INTEGER_BASE_HEXADECIMAL: - base_string = "hexadecimal"; - break; - case BT_CTF_INTEGER_BASE_OCTAL: - base_string = "octal"; - break; - case BT_CTF_INTEGER_BASE_BINARY: - base_string = "binary"; - break; - default: - base_string = "unknown"; - break; - } - - return base_string; -} - -static -void append_field_name(struct metadata_context *context, - const char *name) -{ - g_string_append_c(context->string, ' '); - - if (!bt_ctf_identifier_is_valid(name) || *name == '_') { - g_string_append_c(context->string, '_'); - } - - g_string_append(context->string, name); -} - -static -int bt_ctf_field_type_integer_serialize(struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - struct bt_ctf_field_type_common_integer *integer = BT_CTF_FROM_COMMON(type); - int ret = 0; - - BT_LOGD("Serializing CTF writer integer field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - g_string_append_printf(context->string, - "integer { size = %u; align = %u; signed = %s; encoding = %s; base = %s; byte_order = %s", - integer->size, type->alignment, - (integer->is_signed ? "true" : "false"), - get_encoding_string(integer->encoding), - get_integer_base_string(integer->base), - bt_ctf_get_byte_order_string(integer->user_byte_order)); - if (integer->mapped_clock_class) { - const char *clock_name = bt_ctf_clock_class_get_name( - integer->mapped_clock_class); - - BT_ASSERT(clock_name); - g_string_append_printf(context->string, - "; map = clock.%s.value", clock_name); - } - - g_string_append(context->string, "; }"); - return ret; -} - -static -int bt_ctf_field_type_enumeration_serialize_recursive( - struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - size_t entry; - int ret; - struct bt_ctf_field_type_common_enumeration *enumeration = - BT_CTF_FROM_COMMON(type); - struct bt_ctf_field_type_common *container_type; - int container_signed; - - BT_LOGD("Serializing CTF writer enumeration field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - container_type = - bt_ctf_field_type_common_enumeration_borrow_container_field_type(type); - BT_ASSERT(container_type); - container_signed = bt_ctf_field_type_common_integer_is_signed( - container_type); - BT_ASSERT(container_signed >= 0); - g_string_append(context->string, "enum : "); - BT_LOGD_STR("Serializing CTF writer enumeration field type's container field type's metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) enumeration->container_ft, context); - if (ret) { - BT_LOGW("Cannot serialize CTF writer enumeration field type's container field type's metadata: " - "container-ft-addr=%p", enumeration->container_ft); - goto end; - } - - g_string_append(context->string, " { "); - for (entry = 0; entry < enumeration->entries->len; entry++) { - struct bt_ctf_enumeration_mapping *mapping = - enumeration->entries->pdata[entry]; - const char *label = g_quark_to_string(mapping->string); - - g_string_append(context->string, "\""); - - if (!bt_ctf_identifier_is_valid(label) || label[0] == '_') { - g_string_append(context->string, "_"); - } - - g_string_append_printf(context->string, "%s\" = ", label); - - if (container_signed) { - if (mapping->range_start._signed == - mapping->range_end._signed) { - g_string_append_printf(context->string, - "%" PRId64, - mapping->range_start._signed); - } else { - g_string_append_printf(context->string, - "%" PRId64 " ... %" PRId64, - mapping->range_start._signed, - mapping->range_end._signed); - } - } else { - if (mapping->range_start._unsigned == - mapping->range_end._unsigned) { - g_string_append_printf(context->string, - "%" PRIu64, - mapping->range_start._unsigned); - } else { - g_string_append_printf(context->string, - "%" PRIu64 " ... %" PRIu64, - mapping->range_start._unsigned, - mapping->range_end._unsigned); - } - } - - g_string_append(context->string, - ((entry != (enumeration->entries->len - 1)) ? - ", " : " }")); - } - - if (context->field_name->len) { - append_field_name(context, - context->field_name->str); - g_string_assign(context->field_name, ""); - } - -end: - return ret; -} - -static -int bt_ctf_field_type_floating_point_serialize(struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - struct bt_ctf_field_type_common_floating_point *floating_point = - BT_CTF_FROM_COMMON(type); - - BT_LOGD("Serializing CTF writer floating point number field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - g_string_append_printf(context->string, - "floating_point { exp_dig = %u; mant_dig = %u; byte_order = %s; align = %u; }", - floating_point->exp_dig, - floating_point->mant_dig, - bt_ctf_get_byte_order_string(floating_point->user_byte_order), - type->alignment); - return 0; -} - -static -int bt_ctf_field_type_structure_serialize_recursive( - struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - size_t i; - unsigned int indent; - int ret = 0; - struct bt_ctf_field_type_common_structure *structure = BT_CTF_FROM_COMMON(type); - GString *structure_field_name = context->field_name; - - BT_LOGD("Serializing CTF writer structure field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - context->field_name = g_string_new(""); - - context->current_indentation_level++; - g_string_append(context->string, "struct {\n"); - - for (i = 0; i < structure->fields->len; i++) { - struct bt_ctf_field_type_common_structure_field *field = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - structure, i); - - BT_LOGD("Serializing CTF writer structure field type's field metadata: " - "index=%zu, " - "field-ft-addr=%p, field-name=\"%s\"", - i, field, g_quark_to_string(field->name)); - - for (indent = 0; indent < context->current_indentation_level; - indent++) { - g_string_append_c(context->string, '\t'); - } - - g_string_assign(context->field_name, - g_quark_to_string(field->name)); - ret = bt_ctf_field_type_serialize_recursive( - (void *) field->type, context); - if (ret) { - BT_LOGW("Cannot serialize CTF writer structure field type's field's metadata: " - "index=%zu, " - "field-ft-addr=%p, field-name=\"%s\"", - i, field->type, - g_quark_to_string(field->name)); - goto end; - } - - if (context->field_name->len) { - append_field_name(context, - context->field_name->str); - } - g_string_append(context->string, ";\n"); - } - - context->current_indentation_level--; - for (indent = 0; indent < context->current_indentation_level; - indent++) { - g_string_append_c(context->string, '\t'); - } - - g_string_append_printf(context->string, "} align(%u)", - type->alignment); - -end: - g_string_free(context->field_name, TRUE); - context->field_name = structure_field_name; - return ret; -} - -static -int bt_ctf_field_type_variant_serialize_recursive( - struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - size_t i; - unsigned int indent; - int ret = 0; - struct bt_ctf_field_type_common_variant *variant = BT_CTF_FROM_COMMON(type); - GString *variant_field_name = context->field_name; - - BT_LOGD("Serializing CTF writer variant field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - context->field_name = g_string_new(""); - if (variant->tag_name->len > 0) { - g_string_append(context->string, "variant <"); - append_field_name(context, variant->tag_name->str); - g_string_append(context->string, "> {\n"); - } else { - g_string_append(context->string, "variant {\n"); - } - - context->current_indentation_level++; - for (i = 0; i < variant->choices->len; i++) { - struct bt_ctf_field_type_common_variant_choice *field = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - variant, i); - - BT_LOGD("Serializing CTF writer variant field type's field metadata: " - "index=%zu, " - "field-ft-addr=%p, field-name=\"%s\"", - i, field, g_quark_to_string(field->name)); - - g_string_assign(context->field_name, - g_quark_to_string(field->name)); - for (indent = 0; indent < context->current_indentation_level; - indent++) { - g_string_append_c(context->string, '\t'); - } - - g_string_assign(context->field_name, - g_quark_to_string(field->name)); - ret = bt_ctf_field_type_serialize_recursive( - (void *) field->type, context); - if (ret) { - BT_LOGW("Cannot serialize CTF writer variant field type's field's metadata: " - "index=%zu, " - "field-ft-addr=%p, field-name=\"%s\"", - i, field->type, - g_quark_to_string(field->name)); - goto end; - } - - if (context->field_name->len) { - append_field_name(context, - context->field_name->str); - g_string_append_c(context->string, ';'); - } - - g_string_append_c(context->string, '\n'); - } - - context->current_indentation_level--; - for (indent = 0; indent < context->current_indentation_level; - indent++) { - g_string_append_c(context->string, '\t'); - } - - g_string_append(context->string, "}"); - -end: - g_string_free(context->field_name, TRUE); - context->field_name = variant_field_name; - return ret; -} - -static -int bt_ctf_field_type_array_serialize_recursive( - struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - int ret = 0; - struct bt_ctf_field_type_common_array *array = BT_CTF_FROM_COMMON(type); - - BT_LOGD("Serializing CTF writer array field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - BT_LOGD_STR("Serializing CTF writer array field type's element field type's metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) array->element_ft, context); - if (ret) { - BT_LOGW("Cannot serialize CTF writer array field type's element field type's metadata: " - "element-ft-addr=%p", array->element_ft); - goto end; - } - - if (context->field_name->len) { - append_field_name(context, - context->field_name->str); - - g_string_append_printf(context->string, "[%u]", array->length); - g_string_assign(context->field_name, ""); - } else { - g_string_append_printf(context->string, "[%u]", array->length); - } - -end: - return ret; -} - -static -int bt_ctf_field_type_sequence_serialize_recursive( - struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - int ret = 0; - struct bt_ctf_field_type_common_sequence *sequence = BT_CTF_FROM_COMMON(type); - - BT_LOGD("Serializing CTF writer sequence field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - BT_LOGD_STR("Serializing CTF writer sequence field type's element field type's metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) sequence->element_ft, context); - if (ret) { - BT_LOGW("Cannot serialize CTF writer sequence field type's element field type's metadata: " - "element-ft-addr=%p", sequence->element_ft); - goto end; - } - - if (context->field_name->len) { - append_field_name(context, context->field_name->str); - g_string_assign(context->field_name, ""); - } - g_string_append(context->string, "["); - append_field_name(context, sequence->length_field_name->str); - g_string_append(context->string, "]"); - -end: - return ret; -} - -static -int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type_common *type, - struct metadata_context *context) -{ - struct bt_ctf_field_type_common_string *string = BT_CTF_FROM_COMMON(type); - - BT_LOGD("Serializing CTF writer string field type's metadata: " - "ft-addr=%p, metadata-context-addr=%p", type, context); - g_string_append_printf(context->string, - "string { encoding = %s; }", - get_encoding_string(string->encoding)); - return 0; -} - -struct bt_ctf_field_type *bt_ctf_field_type_integer_create(unsigned int size) -{ - struct bt_ctf_field_type_common_integer *integer = NULL; - - BT_LOGD("Creating CTF writer integer field type object: size=%u", size); - - if (size == 0 || size > 64) { - BT_LOGW("Invalid parameter: size must be between 1 and 64: " - "size=%u", size); - goto error; - } - - integer = g_new0(struct bt_ctf_field_type_common_integer, 1); - if (!integer) { - BT_LOGE_STR("Failed to allocate one integer field type."); - goto error; - } - - bt_ctf_field_type_common_integer_initialize(BT_CTF_TO_COMMON(integer), - size, bt_ctf_field_type_common_integer_destroy, - &bt_ctf_field_type_integer_methods); - integer->common.spec.writer.serialize_func = - bt_ctf_field_type_integer_serialize; - BT_LOGD("Created CTF writer integer field type object: addr=%p, size=%u", - integer, size); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(integer); - -end: - return (void *) integer; -} - -int bt_ctf_field_type_integer_get_size(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_integer_get_size((void *) ft); -} - -bt_bool bt_ctf_field_type_integer_is_signed(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_integer_is_signed((void *) ft); -} - -int bt_ctf_field_type_integer_set_is_signed(struct bt_ctf_field_type *ft, - bt_bool is_signed) -{ - return bt_ctf_field_type_common_integer_set_is_signed((void *) ft, - is_signed); -} - -int bt_ctf_field_type_integer_set_size(struct bt_ctf_field_type *ft, - unsigned int size) -{ - return bt_ctf_field_type_common_integer_set_size((void *) ft, size); -} - -enum bt_ctf_integer_base bt_ctf_field_type_integer_get_base( - struct bt_ctf_field_type *ft) -{ - return (int) bt_ctf_field_type_common_integer_get_base((void *) ft); -} - -int bt_ctf_field_type_integer_set_base(struct bt_ctf_field_type *ft, - enum bt_ctf_integer_base base) -{ - return bt_ctf_field_type_common_integer_set_base((void *) ft, - (int) base); -} - -enum bt_ctf_string_encoding bt_ctf_field_type_integer_get_encoding( - struct bt_ctf_field_type *ft) -{ - return (int) bt_ctf_field_type_common_integer_get_encoding((void *) ft); -} - -int bt_ctf_field_type_integer_set_encoding(struct bt_ctf_field_type *ft, - enum bt_ctf_string_encoding encoding) -{ - return bt_ctf_field_type_common_integer_set_encoding((void *) ft, - (int) encoding); -} - -struct bt_ctf_clock_class *bt_ctf_field_type_integer_get_mapped_clock_class( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_integer_borrow_mapped_clock_class( - (void *) ft)); -} - -int bt_ctf_field_type_integer_set_mapped_clock_class( - struct bt_ctf_field_type *ft, - struct bt_ctf_clock_class *clock_class) -{ - return bt_ctf_field_type_common_integer_set_mapped_clock_class((void *) ft, - clock_class); -} - -int bt_ctf_field_type_enumeration_signed_get_mapping_by_index( - struct bt_ctf_field_type *ft, uint64_t index, - const char **mapping_name, int64_t *range_begin, - int64_t *range_end) -{ - return bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( - (void *) ft, index, mapping_name, range_begin, range_end); -} - -int bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index( - struct bt_ctf_field_type *ft, uint64_t index, - const char **mapping_name, uint64_t *range_begin, - uint64_t *range_end) -{ - return bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( - (void *) ft, index, mapping_name, range_begin, range_end); -} - -struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create( - struct bt_ctf_field_type *container_ft) -{ - struct bt_ctf_field_type_common_enumeration *enumeration = NULL; - struct bt_ctf_field_type_common *int_ft = (void *) container_ft; - - BT_LOGD("Creating CTF writer enumeration field type object: int-ft-addr=%p", - container_ft); - - if (!container_ft) { - BT_LOGW_STR("Invalid parameter: field type is NULL."); - goto error; - } - - if (int_ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid parameter: container field type is not an integer field type: " - "container-ft-addr=%p, container-ft-id=%s", - container_ft, bt_ctf_field_type_id_string(int_ft->id)); - goto error; - } - - enumeration = g_new0(struct bt_ctf_field_type_common_enumeration, 1); - if (!enumeration) { - BT_LOGE_STR("Failed to allocate one enumeration field type."); - goto error; - } - - bt_ctf_field_type_common_enumeration_initialize(BT_CTF_TO_COMMON(enumeration), - int_ft, bt_ctf_field_type_common_enumeration_destroy_recursive, - &bt_ctf_field_type_enumeration_methods); - enumeration->common.spec.writer.serialize_func = - bt_ctf_field_type_enumeration_serialize_recursive; - BT_LOGD("Created CTF writer enumeration field type object: addr=%p, " - "int-ft-addr=%p, int-ft-size=%u", - enumeration, container_ft, - bt_ctf_field_type_integer_get_size(container_ft)); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(enumeration); - -end: - return (void *) enumeration; -} - -struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_field_type( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_object_get_ref( - bt_ctf_field_type_common_enumeration_borrow_container_field_type( - (void *) ft)); -} - -int bt_ctf_field_type_enumeration_signed_add_mapping( - struct bt_ctf_field_type *ft, const char *string, - int64_t range_start, int64_t range_end) -{ - return bt_ctf_field_type_common_enumeration_signed_add_mapping( - (void *) ft, string, range_start, range_end); -} - -int bt_ctf_field_type_enumeration_unsigned_add_mapping( - struct bt_ctf_field_type *ft, const char *string, - uint64_t range_start, uint64_t range_end) -{ - return bt_ctf_field_type_common_enumeration_unsigned_add_mapping( - (void *) ft, string, range_start, range_end); -} - -int64_t bt_ctf_field_type_enumeration_get_mapping_count( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_enumeration_get_mapping_count((void *) ft); -} - -struct bt_ctf_field_type *bt_ctf_field_type_floating_point_create(void) -{ - struct bt_ctf_field_type_common_floating_point *floating_point = - g_new0(struct bt_ctf_field_type_common_floating_point, 1); - - BT_LOGD_STR("Creating CTF writer floating point number field type object."); - - if (!floating_point) { - BT_LOGE_STR("Failed to allocate one floating point number field type."); - goto end; - } - - bt_ctf_field_type_common_floating_point_initialize( - BT_CTF_TO_COMMON(floating_point), - bt_ctf_field_type_common_floating_point_destroy, - &bt_ctf_field_type_floating_point_methods); - floating_point->common.spec.writer.serialize_func = - bt_ctf_field_type_floating_point_serialize; - BT_LOGD("Created CTF writer floating point number field type object: addr=%p, " - "exp-size=%u, mant-size=%u", floating_point, - floating_point->exp_dig, floating_point->mant_dig); - -end: - return (void *) floating_point; -} - -int bt_ctf_field_type_floating_point_get_exponent_digits( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_floating_point_get_exponent_digits( - (void *) ft); -} - -int bt_ctf_field_type_floating_point_set_exponent_digits( - struct bt_ctf_field_type *ft, unsigned int exponent_digits) -{ - return bt_ctf_field_type_common_floating_point_set_exponent_digits( - (void *) ft, exponent_digits); -} - -int bt_ctf_field_type_floating_point_get_mantissa_digits( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_floating_point_get_mantissa_digits( - (void *) ft); -} - -int bt_ctf_field_type_floating_point_set_mantissa_digits( - struct bt_ctf_field_type *ft, unsigned int mantissa_digits) -{ - return bt_ctf_field_type_common_floating_point_set_mantissa_digits( - (void *) ft, mantissa_digits); -} - -struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void) -{ - struct bt_ctf_field_type_common_structure *structure = - g_new0(struct bt_ctf_field_type_common_structure, 1); - - BT_LOGD_STR("Creating CTF writer structure field type object."); - - if (!structure) { - BT_LOGE_STR("Failed to allocate one structure field type."); - goto error; - } - - bt_ctf_field_type_common_structure_initialize(BT_CTF_TO_COMMON(structure), - bt_ctf_field_type_common_structure_destroy_recursive, - &bt_ctf_field_type_structure_methods); - structure->common.spec.writer.serialize_func = - bt_ctf_field_type_structure_serialize_recursive; - BT_LOGD("Created CTF writer structure field type object: addr=%p", - structure); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(structure); - -end: - return (void *) structure; -} - -int bt_ctf_field_type_structure_add_field(struct bt_ctf_field_type *ft, - struct bt_ctf_field_type *field_type, - const char *field_name) -{ - return bt_ctf_field_type_common_structure_add_field((void *) ft, - (void *) field_type, field_name); -} - -int64_t bt_ctf_field_type_structure_get_field_count(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_structure_get_field_count((void *) ft); -} - -int bt_ctf_field_type_structure_get_field_by_index( - struct bt_ctf_field_type *ft, - const char **field_name, - struct bt_ctf_field_type **field_type, uint64_t index) -{ - int ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - (void *) ft, field_name, (void *) field_type, index); - - if (ret == 0 && field_type) { - bt_ctf_object_get_ref(*field_type); - } - - return ret; -} - -struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name( - struct bt_ctf_field_type *ft, const char *name) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_structure_borrow_field_type_by_name( - (void *) ft, name)); -} - -struct bt_ctf_field_type *bt_ctf_field_type_variant_create( - struct bt_ctf_field_type *tag_ft, const char *tag_name) -{ - struct bt_ctf_field_type_common_variant *var_ft = NULL; - - BT_LOGD("Creating CTF writer variant field type object: " - "tag-ft-addr=%p, tag-field-name=\"%s\"", - tag_ft, tag_name); - - if (tag_name && !bt_ctf_identifier_is_valid(tag_name)) { - BT_LOGW("Invalid parameter: tag field name is not a valid CTF identifier: " - "tag-ft-addr=%p, tag-field-name=\"%s\"", - tag_ft, tag_name); - goto error; - } - - var_ft = g_new0(struct bt_ctf_field_type_common_variant, 1); - if (!var_ft) { - BT_LOGE_STR("Failed to allocate one variant field type."); - goto error; - } - - bt_ctf_field_type_common_variant_initialize(BT_CTF_TO_COMMON(var_ft), - (void *) tag_ft, tag_name, - bt_ctf_field_type_common_variant_destroy_recursive, - &bt_ctf_field_type_variant_methods); - var_ft->common.spec.writer.serialize_func = - bt_ctf_field_type_variant_serialize_recursive; - BT_LOGD("Created CTF writer variant field type object: addr=%p, " - "tag-ft-addr=%p, tag-field-name=\"%s\"", - var_ft, tag_ft, tag_name); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(var_ft); - -end: - return (void *) var_ft; -} - -struct bt_ctf_field_type *bt_ctf_field_type_variant_get_tag_field_type( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_variant_borrow_tag_field_type( - (void *) ft)); -} - -const char *bt_ctf_field_type_variant_get_tag_name(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_variant_get_tag_name((void *) ft); -} - -int bt_ctf_field_type_variant_set_tag_name( - struct bt_ctf_field_type *ft, const char *name) -{ - return bt_ctf_field_type_common_variant_set_tag_name((void *) ft, name); -} - -int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *ft, - struct bt_ctf_field_type *field_type, - const char *field_name) -{ - return bt_ctf_field_type_common_variant_add_field((void *) ft, - (void *) field_type, field_name); -} - -struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name( - struct bt_ctf_field_type *ft, - const char *field_name) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_variant_borrow_field_type_by_name( - (void *) ft, field_name)); -} - -struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_from_tag( - struct bt_ctf_field_type *ft, - struct bt_ctf_field *tag_field) -{ - int ret; - int64_t choice_index; - struct bt_ctf_field *container; - struct bt_ctf_field_type_common_variant *var_ft = (void *) ft; - struct bt_ctf_field_type *ret_ft = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); - BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, - "Field type"); - BT_CTF_ASSERT_PRE_NON_NULL(tag_field, "Tag field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( - (struct bt_ctf_field_common *) tag_field, - BT_CTF_FIELD_TYPE_ID_ENUM, "Tag field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((struct bt_ctf_field_common *) tag_field, - "Tag field"); - - container = bt_ctf_field_enumeration_borrow_container(tag_field); - BT_ASSERT(container); - - if (var_ft->tag_ft->container_ft->is_signed) { - int64_t val; - - ret = bt_ctf_field_integer_signed_get_value(container, - &val); - BT_ASSERT(ret == 0); - choice_index = bt_ctf_field_type_common_variant_find_choice_index( - (void *) ft, (uint64_t) val, true); - } else { - uint64_t val; - - ret = bt_ctf_field_integer_unsigned_get_value(container, - &val); - BT_ASSERT(ret == 0); - choice_index = bt_ctf_field_type_common_variant_find_choice_index( - (void *) ft, val, false); - } - - if (choice_index < 0) { - BT_LOGW("Cannot find variant field type's field: " - "var-ft-addr=%p, tag-field-addr=%p", ft, tag_field); - goto end; - } - - ret = bt_ctf_field_type_variant_get_field_by_index(ft, NULL, - &ret_ft, choice_index); - BT_ASSERT(ret == 0); - -end: - return ret_ft; -} - -int64_t bt_ctf_field_type_variant_get_field_count(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_variant_get_field_count((void *) ft); -} - -int bt_ctf_field_type_variant_get_field_by_index(struct bt_ctf_field_type *ft, - const char **field_name, struct bt_ctf_field_type **field_type, - uint64_t index) -{ - int ret = bt_ctf_field_type_common_variant_borrow_field_by_index( - (void *) ft, field_name, (void *) field_type, index); - - if (ret == 0 && field_type) { - bt_ctf_object_get_ref(*field_type); - } - - return ret; -} - -struct bt_ctf_field_type *bt_ctf_field_type_array_create( - struct bt_ctf_field_type *element_ft, unsigned int length) -{ - struct bt_ctf_field_type_common_array *array = NULL; - - BT_LOGD("Creating CTF writer array field type object: element-ft-addr=%p, " - "length=%u", element_ft, length); - - if (!element_ft) { - BT_LOGW_STR("Invalid parameter: element field type is NULL."); - goto error; - } - - if (length == 0) { - BT_LOGW_STR("Invalid parameter: length is zero."); - goto error; - } - - array = g_new0(struct bt_ctf_field_type_common_array, 1); - if (!array) { - BT_LOGE_STR("Failed to allocate one array field type."); - goto error; - } - - bt_ctf_field_type_common_array_initialize(BT_CTF_TO_COMMON(array), - (void *) element_ft, length, - bt_ctf_field_type_common_array_destroy_recursive, - &bt_ctf_field_type_array_methods); - array->common.spec.writer.serialize_func = - bt_ctf_field_type_array_serialize_recursive; - BT_LOGD("Created CTF writer array field type object: addr=%p, " - "element-ft-addr=%p, length=%u", - array, element_ft, length); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(array); - -end: - return (void *) array; -} - -struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_field_type( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_array_borrow_element_field_type( - (void *) ft)); -} - -int64_t bt_ctf_field_type_array_get_length(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_array_get_length((void *) ft); -} - -struct bt_ctf_field_type *bt_ctf_field_type_sequence_create( - struct bt_ctf_field_type *element_ft, - const char *length_field_name) -{ - struct bt_ctf_field_type_common_sequence *sequence = NULL; - - BT_LOGD("Creating CTF writer sequence field type object: element-ft-addr=%p, " - "length-field-name=\"%s\"", element_ft, length_field_name); - - if (!element_ft) { - BT_LOGW_STR("Invalid parameter: element field type is NULL."); - goto error; - } - - if (!bt_ctf_identifier_is_valid(length_field_name)) { - BT_LOGW("Invalid parameter: length field name is not a valid CTF identifier: " - "length-field-name=\"%s\"", length_field_name); - goto error; - } - - sequence = g_new0(struct bt_ctf_field_type_common_sequence, 1); - if (!sequence) { - BT_LOGE_STR("Failed to allocate one sequence field type."); - goto error; - } - - bt_ctf_field_type_common_sequence_initialize(BT_CTF_TO_COMMON(sequence), - (void *) element_ft, length_field_name, - bt_ctf_field_type_common_sequence_destroy_recursive, - &bt_ctf_field_type_sequence_methods); - sequence->common.spec.writer.serialize_func = - bt_ctf_field_type_sequence_serialize_recursive; - BT_LOGD("Created CTF writer sequence field type object: addr=%p, " - "element-ft-addr=%p, length-field-name=\"%s\"", - sequence, element_ft, length_field_name); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(sequence); - -end: - return (void *) sequence; -} - -struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_field_type( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_object_get_ref(bt_ctf_field_type_common_sequence_borrow_element_field_type( - (void *) ft)); -} - -const char *bt_ctf_field_type_sequence_get_length_field_name( - struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_sequence_get_length_field_name((void *) ft); -} - -struct bt_ctf_field_type *bt_ctf_field_type_string_create(void) -{ - struct bt_ctf_field_type_common_string *string = - g_new0(struct bt_ctf_field_type_common_string, 1); - - BT_LOGD_STR("Creating CTF writer string field type object."); - - if (!string) { - BT_LOGE_STR("Failed to allocate one string field type."); - return NULL; - } - - bt_ctf_field_type_common_string_initialize(BT_CTF_TO_COMMON(string), - bt_ctf_field_type_common_string_destroy, - &bt_ctf_field_type_string_methods); - string->common.spec.writer.serialize_func = - bt_ctf_field_type_string_serialize; - BT_LOGD("Created CTF writer string field type object: addr=%p", string); - return (void *) string; -} - -enum bt_ctf_string_encoding bt_ctf_field_type_string_get_encoding( - struct bt_ctf_field_type *ft) -{ - return (int) bt_ctf_field_type_common_string_get_encoding((void *) ft); -} - -int bt_ctf_field_type_string_set_encoding(struct bt_ctf_field_type *ft, - enum bt_ctf_string_encoding encoding) -{ - return bt_ctf_field_type_common_string_set_encoding((void *) ft, - (int) encoding); -} - -int bt_ctf_field_type_get_alignment(struct bt_ctf_field_type *ft) -{ - return bt_ctf_field_type_common_get_alignment((void *) ft); -} - -int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *ft, - unsigned int alignment) -{ - return bt_ctf_field_type_common_set_alignment((void *) ft, alignment); -} - -enum bt_ctf_byte_order bt_ctf_field_type_get_byte_order( - struct bt_ctf_field_type *ft) -{ - return (int) bt_ctf_field_type_common_get_byte_order((void *) ft); -} - -int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *ft, - enum bt_ctf_byte_order byte_order) -{ - return bt_ctf_field_type_common_set_byte_order((void *) ft, - (int) byte_order); -} - -enum bt_ctf_field_type_id bt_ctf_field_type_get_type_id( - struct bt_ctf_field_type *ft) -{ - return (int) bt_ctf_field_type_common_get_type_id((void *) ft); -} - -BT_HIDDEN -struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft) -{ - return (void *) bt_ctf_field_type_common_copy((void *) ft); -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_integer_copy( - struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_type_common_integer *int_ft = (void *) ft; - struct bt_ctf_field_type_common_integer *copy_ft; - - BT_LOGD("Copying CTF writer integer field type's: addr=%p", ft); - copy_ft = (void *) bt_ctf_field_type_integer_create(int_ft->size); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer integer field type."); - goto end; - } - - copy_ft->mapped_clock_class = bt_ctf_object_get_ref(int_ft->mapped_clock_class); - copy_ft->user_byte_order = int_ft->user_byte_order; - copy_ft->is_signed = int_ft->is_signed; - copy_ft->size = int_ft->size; - copy_ft->base = int_ft->base; - copy_ft->encoding = int_ft->encoding; - BT_LOGD("Copied CTF writer integer field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - return (void *) copy_ft; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_enumeration_copy_recursive( - struct bt_ctf_field_type *ft) -{ - size_t i; - struct bt_ctf_field_type_common_enumeration *enum_ft = (void *) ft; - struct bt_ctf_field_type_common_enumeration *copy_ft = NULL; - struct bt_ctf_field_type_common_enumeration *container_copy_ft; - - BT_LOGD("Copying CTF writer enumeration field type's: addr=%p", ft); - - /* Copy the source enumeration's container */ - BT_LOGD_STR("Copying CTF writer enumeration field type's container field type."); - container_copy_ft = BT_CTF_FROM_COMMON(bt_ctf_field_type_common_copy( - BT_CTF_TO_COMMON(enum_ft->container_ft))); - if (!container_copy_ft) { - BT_LOGE_STR("Cannot copy CTF writer enumeration field type's container field type."); - goto end; - } - - copy_ft = (void *) bt_ctf_field_type_enumeration_create( - (void *) container_copy_ft); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer enumeration field type."); - goto end; - } - - /* Copy all enumaration entries */ - for (i = 0; i < enum_ft->entries->len; i++) { - struct bt_ctf_enumeration_mapping *mapping = g_ptr_array_index( - enum_ft->entries, i); - struct bt_ctf_enumeration_mapping *copy_mapping = g_new0( - struct bt_ctf_enumeration_mapping, 1); - - if (!copy_mapping) { - BT_LOGE_STR("Failed to allocate one enumeration mapping."); - goto error; - } - - *copy_mapping = *mapping; - g_ptr_array_add(copy_ft->entries, copy_mapping); - } - - BT_LOGD("Copied CTF writer enumeration field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - bt_ctf_object_put_ref(container_copy_ft); - return (void *) copy_ft; - -error: - bt_ctf_object_put_ref(container_copy_ft); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); - return (void *) copy_ft; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_floating_point_copy( - struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); - struct bt_ctf_field_type_common_floating_point *copy_ft; - - BT_LOGD("Copying CTF writer floating point number field type's: addr=%p", ft); - copy_ft = (void *) bt_ctf_field_type_floating_point_create(); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer floating point number field type."); - goto end; - } - - copy_ft->user_byte_order = flt_ft->user_byte_order; - copy_ft->exp_dig = flt_ft->exp_dig; - copy_ft->mant_dig = flt_ft->mant_dig; - BT_LOGD("Copied CTF writer floating point number field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - return (void *) copy_ft; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_structure_copy_recursive( - struct bt_ctf_field_type *ft) -{ - int64_t i; - GHashTableIter iter; - gpointer key, value; - struct bt_ctf_field_type_common_structure *struct_ft = (void *) ft; - struct bt_ctf_field_type_common_structure *copy_ft; - - BT_LOGD("Copying CTF writer structure field type's: addr=%p", ft); - copy_ft = (void *) bt_ctf_field_type_structure_create(); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer structure field type."); - goto end; - } - - /* Copy field_name_to_index */ - g_hash_table_iter_init(&iter, struct_ft->field_name_to_index); - while (g_hash_table_iter_next(&iter, &key, &value)) { - g_hash_table_insert(copy_ft->field_name_to_index, - key, value); - } - - g_array_set_size(copy_ft->fields, struct_ft->fields->len); - - for (i = 0; i < struct_ft->fields->len; i++) { - struct bt_ctf_field_type_common_structure_field *entry, *copy_entry; - struct bt_ctf_field_type_common *field_ft_copy; - - entry = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - struct_ft, i); - copy_entry = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - copy_ft, i); - BT_LOGD("Copying CTF writer structure field type's field: " - "index=%" PRId64 ", " - "field-ft-addr=%p, field-name=\"%s\"", - i, entry, g_quark_to_string(entry->name)); - - field_ft_copy = (void *) bt_ctf_field_type_copy( - (void *) entry->type); - if (!field_ft_copy) { - BT_LOGE("Cannot copy CTF writer structure field type's field: " - "index=%" PRId64 ", " - "field-ft-addr=%p, field-name=\"%s\"", - i, entry, g_quark_to_string(entry->name)); - goto error; - } - - copy_entry->name = entry->name; - copy_entry->type = field_ft_copy; - } - - BT_LOGD("Copied CTF writer structure field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - return (void *) copy_ft; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); - return NULL; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_variant_copy_recursive( - struct bt_ctf_field_type *ft) -{ - int64_t i; - GHashTableIter iter; - gpointer key, value; - struct bt_ctf_field_type_common *tag_ft_copy = NULL; - struct bt_ctf_field_type_common_variant *var_ft = (void *) ft; - struct bt_ctf_field_type_common_variant *copy_ft = NULL; - - BT_LOGD("Copying CTF writer variant field type's: addr=%p", ft); - if (var_ft->tag_ft) { - BT_LOGD_STR("Copying CTF writer variant field type's tag field type."); - tag_ft_copy = bt_ctf_field_type_common_copy( - BT_CTF_TO_COMMON(var_ft->tag_ft)); - if (!tag_ft_copy) { - BT_LOGE_STR("Cannot copy CTF writer variant field type's tag field type."); - goto end; - } - } - - copy_ft = (void *) bt_ctf_field_type_variant_create( - (void *) tag_ft_copy, - var_ft->tag_name->len ? var_ft->tag_name->str : NULL); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer variant field type."); - goto end; - } - - /* Copy field_name_to_index */ - g_hash_table_iter_init(&iter, var_ft->choice_name_to_index); - while (g_hash_table_iter_next(&iter, &key, &value)) { - g_hash_table_insert(copy_ft->choice_name_to_index, - key, value); - } - - g_array_set_size(copy_ft->choices, var_ft->choices->len); - - for (i = 0; i < var_ft->choices->len; i++) { - struct bt_ctf_field_type_common_variant_choice *entry, *copy_entry; - struct bt_ctf_field_type_common *field_ft_copy; - uint64_t range_i; - - entry = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, i); - copy_entry = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - copy_ft, i); - BT_LOGD("Copying CTF writer variant field type's field: " - "index=%" PRId64 ", " - "field-ft-addr=%p, field-name=\"%s\"", - i, entry, g_quark_to_string(entry->name)); - - field_ft_copy = (void *) bt_ctf_field_type_copy( - (void *) entry->type); - if (!field_ft_copy) { - BT_LOGE("Cannot copy CTF writer variant field type's field: " - "index=%" PRId64 ", " - "field-ft-addr=%p, field-name=\"%s\"", - i, entry, g_quark_to_string(entry->name)); - g_free(copy_entry); - goto error; - } - - copy_entry->name = entry->name; - copy_entry->type = field_ft_copy; - - /* Copy ranges */ - copy_entry->ranges = g_array_new(FALSE, TRUE, - sizeof(struct bt_ctf_field_type_common_variant_choice_range)); - BT_ASSERT(copy_entry->ranges); - g_array_set_size(copy_entry->ranges, entry->ranges->len); - - for (range_i = 0; range_i < entry->ranges->len; range_i++) { - copy_entry->ranges[range_i] = entry->ranges[range_i]; - } - } - - if (var_ft->tag_field_path) { - BT_LOGD_STR("Copying CTF writer variant field type's tag field path."); - copy_ft->tag_field_path = bt_ctf_field_path_copy( - var_ft->tag_field_path); - if (!copy_ft->tag_field_path) { - BT_LOGE_STR("Cannot copy CTF writer variant field type's tag field path."); - goto error; - } - } - - copy_ft->choices_up_to_date = var_ft->choices_up_to_date; - BT_LOGD("Copied CTF writer variant field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - bt_ctf_object_put_ref(tag_ft_copy); - return (void *) copy_ft; - -error: - bt_ctf_object_put_ref(tag_ft_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); - return NULL; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_array_copy_recursive( - struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_type_common *container_ft_copy = NULL; - struct bt_ctf_field_type_common_array *array_ft = (void *) ft; - struct bt_ctf_field_type_common_array *copy_ft = NULL; - - BT_LOGD("Copying CTF writer array field type's: addr=%p", ft); - BT_LOGD_STR("Copying CTF writer array field type's element field type."); - container_ft_copy = bt_ctf_field_type_common_copy(array_ft->element_ft); - if (!container_ft_copy) { - BT_LOGE_STR("Cannot copy CTF writer array field type's element field type."); - goto end; - } - - copy_ft = (void *) bt_ctf_field_type_array_create( - (void *) container_ft_copy, array_ft->length); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer array field type."); - goto end; - } - - BT_LOGD("Copied CTF writer array field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - bt_ctf_object_put_ref(container_ft_copy); - return (void *) copy_ft; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_sequence_copy_recursive( - struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_type_common *container_ft_copy = NULL; - struct bt_ctf_field_type_common_sequence *seq_ft = (void *) ft; - struct bt_ctf_field_type_common_sequence *copy_ft = NULL; - - BT_LOGD("Copying CTF writer sequence field type's: addr=%p", ft); - BT_LOGD_STR("Copying CTF writer sequence field type's element field type."); - container_ft_copy = bt_ctf_field_type_common_copy(seq_ft->element_ft); - if (!container_ft_copy) { - BT_LOGE_STR("Cannot copy CTF writer sequence field type's element field type."); - goto end; - } - - copy_ft = (void *) bt_ctf_field_type_sequence_create( - (void *) container_ft_copy, - seq_ft->length_field_name->len ? - seq_ft->length_field_name->str : NULL); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer sequence field type."); - goto end; - } - - if (seq_ft->length_field_path) { - BT_LOGD_STR("Copying CTF writer sequence field type's length field path."); - copy_ft->length_field_path = bt_ctf_field_path_copy( - seq_ft->length_field_path); - if (!copy_ft->length_field_path) { - BT_LOGE_STR("Cannot copy CTF writer sequence field type's length field path."); - goto error; - } - } - - BT_LOGD("Copied CTF writer sequence field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - bt_ctf_object_put_ref(container_ft_copy); - return (void *) copy_ft; -error: - bt_ctf_object_put_ref(container_ft_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); - return NULL; -} - -static -struct bt_ctf_field_type *bt_ctf_field_type_string_copy(struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_type_common_string *string_ft = (void *) ft; - struct bt_ctf_field_type_common_string *copy_ft = NULL; - - BT_LOGD("Copying CTF writer string field type's: addr=%p", ft); - copy_ft = (void *) bt_ctf_field_type_string_create(); - if (!copy_ft) { - BT_LOGE_STR("Cannot create CTF writer string field type."); - goto end; - } - - copy_ft->encoding = string_ft->encoding; - BT_LOGD("Copied CTF writer string field type: original-ft-addr=%p, copy-ft-addr=%p", - ft, copy_ft); - -end: - return (void *) copy_ft; -} diff --git a/ctf-writer/field-wrapper.c b/ctf-writer/field-wrapper.c deleted file mode 100644 index a5a3b46f..00000000 --- a/ctf-writer/field-wrapper.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-FIELD-WRAPPER" -#include "logging.h" - -#include -#include -#include -#include -#include - -BT_HIDDEN -struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data) -{ - struct bt_ctf_field_wrapper *field_wrapper = - g_new0(struct bt_ctf_field_wrapper, 1); - - BT_LOGD_STR("Creating empty field wrapper object."); - - if (!field_wrapper) { - BT_LOGE("Failed to allocate one field wrapper."); - goto end; - } - - bt_ctf_object_init_unique(&field_wrapper->base); - BT_LOGD("Created empty field wrapper object: addr=%p", - field_wrapper); - -end: - return field_wrapper; -} - -BT_HIDDEN -void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field_wrapper) -{ - BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper); - BT_ASSERT(!field_wrapper->field); - BT_LOGD_STR("Putting stream class."); - g_free(field_wrapper); -} - -BT_HIDDEN -struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create( - struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft) -{ - struct bt_ctf_field_wrapper *field_wrapper = NULL; - - BT_ASSERT(pool); - BT_ASSERT(ft); - field_wrapper = bt_ctf_object_pool_create_object(pool); - if (!field_wrapper) { - BT_LOGE("Cannot allocate one field wrapper"); - goto error; - } - - BT_ASSERT(field_wrapper->field); - goto end; - -error: - if (field_wrapper) { - bt_ctf_field_wrapper_destroy(field_wrapper); - field_wrapper = NULL; - } - -end: - return field_wrapper; -} diff --git a/ctf-writer/fields.c b/ctf-writer/fields.c deleted file mode 100644 index 327f62e2..00000000 --- a/ctf-writer/fields.c +++ /dev/null @@ -1,1860 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-FIELDS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_CTF_ASSERT_PRE_CTF_FIELD_IS_INT_OR_ENUM(_field, _name) \ - BT_CTF_ASSERT_PRE((_field)->type->id == BT_CTF_FIELD_TYPE_ID_INTEGER || \ - (_field)->type->id == BT_CTF_FIELD_TYPE_ID_ENUM, \ - _name " is not an integer or an enumeration field: " \ - "field-addr=%p", (_field)) - -BT_HIDDEN -struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common *copy = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT(field_type_common_has_known_id(field->type)); - BT_ASSERT(field->methods->copy); - copy = field->methods->copy(field); - if (!copy) { - BT_LOGW("Cannot create field: ft-addr=%p", field->type); - goto end; - } - - bt_ctf_field_common_set(copy, field->payload_set); - -end: - return copy; -} - -BT_HIDDEN -int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_release_func) -{ - int ret = 0; - struct bt_ctf_field_type_common_structure *structure_type = - BT_CTF_FROM_COMMON(type); - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - size_t i; - - BT_LOGD("Initializing common structure field object: ft-addr=%p", type); - bt_ctf_field_common_initialize(field, type, is_shared, - release_func, methods); - structure->fields = g_ptr_array_new_with_free_func(field_release_func); - g_ptr_array_set_size(structure->fields, structure_type->fields->len); - - /* Create all fields contained in the structure field. */ - for (i = 0; i < structure_type->fields->len; i++) { - struct bt_ctf_field_common *field; - struct bt_ctf_field_type_common_structure_field *struct_field = - BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( - structure_type, i); - field = field_create_func(struct_field->type); - if (!field) { - BT_LOGE("Failed to create structure field's member: name=\"%s\", index=%zu", - g_quark_to_string(struct_field->name), i); - ret = -1; - goto end; - } - - g_ptr_array_index(structure->fields, i) = field; - } - - BT_LOGD("Initialized common structure field object: addr=%p, ft-addr=%p", - field, type); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_release_func) -{ - int ret = 0; - struct bt_ctf_field_type_common_variant *variant_type = - BT_CTF_FROM_COMMON(type); - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - size_t i; - - BT_LOGD("Initializing common variant field object: ft-addr=%p", type); - bt_ctf_field_common_initialize(field, type, is_shared, - release_func, methods); - ret = bt_ctf_field_type_common_variant_update_choices(type); - if (ret) { - BT_LOGE("Cannot update common variant field type choices: " - "ret=%d", ret); - goto end; - } - - variant->fields = g_ptr_array_new_with_free_func(field_release_func); - g_ptr_array_set_size(variant->fields, variant_type->choices->len); - - /* Create all fields contained in the variant field. */ - for (i = 0; i < variant_type->choices->len; i++) { - struct bt_ctf_field_common *field; - struct bt_ctf_field_type_common_variant_choice *var_choice = - BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( - variant_type, i); - - field = field_create_func(var_choice->type); - if (!field) { - BT_LOGE("Failed to create variant field's member: name=\"%s\", index=%zu", - g_quark_to_string(var_choice->name), i); - ret = -1; - goto end; - } - - g_ptr_array_index(variant->fields, i) = field; - } - - BT_LOGD("Initialized common variant field object: addr=%p, ft-addr=%p", - field, type); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods) -{ - int ret = 0; - struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); - - BT_LOGD("Initializing common string field object: ft-addr=%p", type); - bt_ctf_field_common_initialize(field, type, is_shared, - release_func, methods); - string->buf = g_array_sized_new(FALSE, FALSE, sizeof(char), 1); - if (!string->buf) { - ret = -1; - goto end; - } - - g_array_index(string->buf, char, 0) = '\0'; - BT_LOGD("Initialized common string field object: addr=%p, ft-addr=%p", - field, type); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_destroy_func) -{ - struct bt_ctf_field_type_common_array *array_type = BT_CTF_FROM_COMMON(type); - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - unsigned int array_length; - int ret = 0; - uint64_t i; - - BT_LOGD("Initializing common array field object: ft-addr=%p", type); - BT_ASSERT(type); - bt_ctf_field_common_initialize(field, type, is_shared, - release_func, methods); - array_length = array_type->length; - array->elements = g_ptr_array_sized_new(array_length); - if (!array->elements) { - ret = -1; - goto end; - } - - g_ptr_array_set_free_func(array->elements, field_destroy_func); - g_ptr_array_set_size(array->elements, array_length); - - for (i = 0; i < array_length; i++) { - array->elements->pdata[i] = field_create_func( - array_type->element_ft); - if (!array->elements->pdata[i]) { - ret = -1; - goto end; - } - } - - BT_LOGD("Initialized common array field object: addr=%p, ft-addr=%p", - field, type); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - GDestroyNotify field_destroy_func) -{ - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - int ret = 0; - - BT_LOGD("Initializing common sequence field object: ft-addr=%p", type); - BT_ASSERT(type); - bt_ctf_field_common_initialize(field, type, is_shared, - release_func, methods); - sequence->elements = g_ptr_array_new(); - if (!sequence->elements) { - ret = -1; - goto end; - } - - g_ptr_array_set_free_func(sequence->elements, field_destroy_func); - BT_LOGD("Initialized common sequence field object: addr=%p, ft-addr=%p", - field, type); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field) -{ - return (field && field->payload_set) ? 0 : -1; -} - -BT_HIDDEN -int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field) -{ - int64_t i; - int ret = 0; - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < structure->fields->len; i++) { - ret = bt_ctf_field_common_validate_recursive( - (void *) structure->fields->pdata[i]); - - if (ret) { - int this_ret; - const char *name; - - this_ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - field->type, &name, NULL, i); - BT_ASSERT(this_ret == 0); - BT_CTF_ASSERT_PRE_MSG("Invalid structure field's field: " - "struct-field-addr=%p, field-name=\"%s\", " - "index=%" PRId64 ", field-addr=%p", - field, name, i, structure->fields->pdata[i]); - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field) -{ - int ret = 0; - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - if (!variant->current_field) { - ret = -1; - goto end; - } - - ret = bt_ctf_field_common_validate_recursive(variant->current_field); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field) -{ - int64_t i; - int ret = 0; - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < array->elements->len; i++) { - ret = bt_ctf_field_common_validate_recursive((void *) array->elements->pdata[i]); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid array field's element field: " - "array-field-addr=%p, %" PRId64 ", " - "elem-field-addr=%p", - field, i, array->elements->pdata[i]); - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field) -{ - int64_t i; - int ret = 0; - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < sequence->elements->len; i++) { - ret = bt_ctf_field_common_validate_recursive( - (void *) sequence->elements->pdata[i]); - if (ret) { - BT_CTF_ASSERT_PRE_MSG("Invalid sequence field's element field: " - "seq-field-addr=%p, %" PRId64 ", " - "elem-field-addr=%p", - field, i, sequence->elements->pdata[i]); - goto end; - } - } -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field) -{ - BT_ASSERT(field); - field->payload_set = false; -} - -BT_HIDDEN -void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field) -{ - int64_t i; - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < structure->fields->len; i++) { - struct bt_ctf_field_common *member = structure->fields->pdata[i]; - - if (!member) { - /* - * Structure members are lazily initialized; - * skip if this member has not been allocated - * yet. - */ - continue; - } - - bt_ctf_field_common_reset_recursive(member); - } -} - -BT_HIDDEN -void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - variant->current_field = NULL; -} - -BT_HIDDEN -void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field) -{ - size_t i; - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < array->elements->len; i++) { - struct bt_ctf_field_common *member = array->elements->pdata[i]; - - if (!member) { - /* - * Array elements are lazily initialized; skip - * if this member has not been allocated yet. - */ - continue; - } - - bt_ctf_field_common_reset_recursive(member); - } -} - -BT_HIDDEN -void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - uint64_t i; - - BT_ASSERT(field); - - for (i = 0; i < sequence->elements->len; i++) { - if (sequence->elements->pdata[i]) { - bt_ctf_field_common_reset_recursive( - sequence->elements->pdata[i]); - } - } - - sequence->length = 0; -} - -BT_HIDDEN -void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field, - bool is_frozen) -{ - field->frozen = is_frozen; -} - -BT_HIDDEN -void bt_ctf_field_common_structure_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - uint64_t i; - struct bt_ctf_field_common_structure *structure_field = - BT_CTF_FROM_COMMON(field); - - BT_LOGD("Freezing structure field object: addr=%p", field); - - for (i = 0; i < structure_field->fields->len; i++) { - struct bt_ctf_field_common *struct_field = - g_ptr_array_index(structure_field->fields, i); - - BT_LOGD("Freezing structure field's field: field-addr=%p, index=%" PRId64, - struct_field, i); - bt_ctf_field_common_set_is_frozen_recursive(struct_field, - is_frozen); - } - - bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); -} - -BT_HIDDEN -void bt_ctf_field_common_variant_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - uint64_t i; - struct bt_ctf_field_common_variant *variant_field = BT_CTF_FROM_COMMON(field); - - BT_LOGD("Freezing variant field object: addr=%p", field); - - for (i = 0; i < variant_field->fields->len; i++) { - struct bt_ctf_field_common *var_field = - g_ptr_array_index(variant_field->fields, i); - - BT_LOGD("Freezing variant field's field: field-addr=%p, index=%" PRId64, - var_field, i); - bt_ctf_field_common_set_is_frozen_recursive(var_field, is_frozen); - } - - bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); -} - -BT_HIDDEN -void bt_ctf_field_common_array_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - int64_t i; - struct bt_ctf_field_common_array *array_field = BT_CTF_FROM_COMMON(field); - - BT_LOGD("Freezing array field object: addr=%p", field); - - for (i = 0; i < array_field->elements->len; i++) { - struct bt_ctf_field_common *elem_field = - g_ptr_array_index(array_field->elements, i); - - BT_LOGD("Freezing array field object's element field: " - "element-field-addr=%p, index=%" PRId64, - elem_field, i); - bt_ctf_field_common_set_is_frozen_recursive(elem_field, is_frozen); - } - - bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); -} - -BT_HIDDEN -void bt_ctf_field_common_sequence_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - int64_t i; - struct bt_ctf_field_common_sequence *sequence_field = - BT_CTF_FROM_COMMON(field); - - BT_LOGD("Freezing sequence field object: addr=%p", field); - - for (i = 0; i < sequence_field->length; i++) { - struct bt_ctf_field_common *elem_field = - g_ptr_array_index(sequence_field->elements, i); - - BT_LOGD("Freezing sequence field object's element field: " - "element-field-addr=%p, index=%" PRId64, - elem_field, i); - bt_ctf_field_common_set_is_frozen_recursive(elem_field, is_frozen); - } - - bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); -} - -BT_HIDDEN -void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field, - bool is_frozen) -{ - if (!field) { - goto end; - } - - BT_LOGD("Setting field object's frozen state: addr=%p, is-frozen=%d", - field, is_frozen); - BT_ASSERT(field_type_common_has_known_id(field->type)); - BT_ASSERT(field->methods->set_is_frozen); - field->methods->set_is_frozen(field, is_frozen); - -end: - return; -} - -BT_HIDDEN -bt_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field) -{ - return field && field->payload_set; -} - -BT_HIDDEN -bt_bool bt_ctf_field_common_structure_is_set_recursive( - struct bt_ctf_field_common *field) -{ - bt_bool is_set = BT_FALSE; - size_t i; - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < structure->fields->len; i++) { - is_set = bt_ctf_field_common_is_set_recursive( - structure->fields->pdata[i]); - if (!is_set) { - goto end; - } - } - -end: - return is_set; -} - -BT_HIDDEN -bt_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - bt_bool is_set = BT_FALSE; - - BT_ASSERT(field); - - if (variant->current_field) { - is_set = bt_ctf_field_common_is_set_recursive( - variant->current_field); - } - - return is_set; -} - -BT_HIDDEN -bt_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field) -{ - size_t i; - bt_bool is_set = BT_FALSE; - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - for (i = 0; i < array->elements->len; i++) { - is_set = bt_ctf_field_common_is_set_recursive(array->elements->pdata[i]); - if (!is_set) { - goto end; - } - } - -end: - return is_set; -} - -BT_HIDDEN -bt_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field) -{ - size_t i; - bt_bool is_set = BT_FALSE; - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - - if (!sequence->elements) { - goto end; - } - - for (i = 0; i < sequence->elements->len; i++) { - is_set = bt_ctf_field_common_is_set_recursive( - sequence->elements->pdata[i]); - if (!is_set) { - goto end; - } - } - -end: - return is_set; -} - -static struct bt_ctf_field_common_methods bt_ctf_field_integer_methods = { - .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, - .validate = bt_ctf_field_common_generic_validate, - .copy = NULL, - .is_set = bt_ctf_field_common_generic_is_set, - .reset = bt_ctf_field_common_generic_reset, -}; - -static struct bt_ctf_field_common_methods bt_ctf_field_floating_point_methods = { - .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, - .validate = bt_ctf_field_common_generic_validate, - .copy = NULL, - .is_set = bt_ctf_field_common_generic_is_set, - .reset = bt_ctf_field_common_generic_reset, -}; - -static -void bt_ctf_field_enumeration_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen); - -static -int bt_ctf_field_enumeration_validate_recursive(struct bt_ctf_field_common *field); - -static -bt_bool bt_ctf_field_enumeration_is_set_recursive( - struct bt_ctf_field_common *field); - -static -void bt_ctf_field_enumeration_reset_recursive(struct bt_ctf_field_common *field); - -static struct bt_ctf_field_common_methods bt_ctf_field_enumeration_methods = { - .set_is_frozen = bt_ctf_field_enumeration_set_is_frozen_recursive, - .validate = bt_ctf_field_enumeration_validate_recursive, - .copy = NULL, - .is_set = bt_ctf_field_enumeration_is_set_recursive, - .reset = bt_ctf_field_enumeration_reset_recursive, -}; - -static struct bt_ctf_field_common_methods bt_ctf_field_string_methods = { - .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, - .validate = bt_ctf_field_common_generic_validate, - .copy = NULL, - .is_set = bt_ctf_field_common_generic_is_set, - .reset = bt_ctf_field_common_generic_reset, -}; - -static struct bt_ctf_field_common_methods bt_ctf_field_structure_methods = { - .set_is_frozen = bt_ctf_field_common_structure_set_is_frozen_recursive, - .validate = bt_ctf_field_common_structure_validate_recursive, - .copy = NULL, - .is_set = bt_ctf_field_common_structure_is_set_recursive, - .reset = bt_ctf_field_common_structure_reset_recursive, -}; - -static struct bt_ctf_field_common_methods bt_ctf_field_sequence_methods = { - .set_is_frozen = bt_ctf_field_common_sequence_set_is_frozen_recursive, - .validate = bt_ctf_field_common_sequence_validate_recursive, - .copy = NULL, - .is_set = bt_ctf_field_common_sequence_is_set_recursive, - .reset = bt_ctf_field_common_sequence_reset_recursive, -}; - -static struct bt_ctf_field_common_methods bt_ctf_field_array_methods = { - .set_is_frozen = bt_ctf_field_common_array_set_is_frozen_recursive, - .validate = bt_ctf_field_common_array_validate_recursive, - .copy = NULL, - .is_set = bt_ctf_field_common_array_is_set_recursive, - .reset = bt_ctf_field_common_array_reset_recursive, -}; - -static -void bt_ctf_field_variant_set_is_frozen_recursive(struct bt_ctf_field_common *field, - bool is_frozen); - -static -int bt_ctf_field_variant_validate_recursive(struct bt_ctf_field_common *field); - -static -bt_bool bt_ctf_field_variant_is_set_recursive(struct bt_ctf_field_common *field); - -static -void bt_ctf_field_variant_reset_recursive(struct bt_ctf_field_common *field); - -static struct bt_ctf_field_common_methods bt_ctf_field_variant_methods = { - .set_is_frozen = bt_ctf_field_variant_set_is_frozen_recursive, - .validate = bt_ctf_field_variant_validate_recursive, - .copy = NULL, - .is_set = bt_ctf_field_variant_is_set_recursive, - .reset = bt_ctf_field_variant_reset_recursive, -}; - -static -struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_enumeration_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_floating_point_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_structure_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_variant_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_array_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_sequence_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *); - -static -struct bt_ctf_field *(* const field_create_funcs[])(struct bt_ctf_field_type *) = { - [BT_CTF_FIELD_TYPE_ID_INTEGER] = bt_ctf_field_integer_create, - [BT_CTF_FIELD_TYPE_ID_ENUM] = bt_ctf_field_enumeration_create, - [BT_CTF_FIELD_TYPE_ID_FLOAT] = bt_ctf_field_floating_point_create, - [BT_CTF_FIELD_TYPE_ID_STRUCT] = bt_ctf_field_structure_create, - [BT_CTF_FIELD_TYPE_ID_VARIANT] = bt_ctf_field_variant_create, - [BT_CTF_FIELD_TYPE_ID_ARRAY] = bt_ctf_field_array_create, - [BT_CTF_FIELD_TYPE_ID_SEQUENCE] = bt_ctf_field_sequence_create, - [BT_CTF_FIELD_TYPE_ID_STRING] = bt_ctf_field_string_create, -}; - -typedef int (*bt_ctf_field_serialize_recursive_func)( - struct bt_ctf_field_common *, struct bt_ctfser *, - enum bt_ctf_byte_order); - -static -void bt_ctf_field_integer_destroy(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer integer field object: addr=%p", field); - bt_ctf_field_common_integer_finalize((void *) field); - g_free(field); -} - -static -void bt_ctf_field_floating_point_destroy(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer floating point field object: addr=%p", - field); - bt_ctf_field_common_floating_point_finalize((void *) field); - g_free(field); -} - -static -void bt_ctf_field_enumeration_destroy_recursive(struct bt_ctf_field *field) -{ - struct bt_ctf_field_enumeration *enumeration = BT_CTF_FROM_COMMON(field); - - BT_LOGD("Destroying CTF writer enumeration field object: addr=%p", - field); - BT_LOGD_STR("Putting container field."); - bt_ctf_object_put_ref(enumeration->container); - bt_ctf_field_common_finalize((void *) field); - g_free(field); -} - -static -void bt_ctf_field_structure_destroy_recursive(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer structure field object: addr=%p", field); - bt_ctf_field_common_structure_finalize_recursive((void *) field); - g_free(field); -} - -static -void bt_ctf_field_variant_destroy_recursive(struct bt_ctf_field *field) -{ - struct bt_ctf_field_variant *variant = BT_CTF_FROM_COMMON(field); - - BT_LOGD("Destroying CTF writer variant field object: addr=%p", field); - BT_LOGD_STR("Putting tag field."); - bt_ctf_object_put_ref(variant->tag); - bt_ctf_field_common_variant_finalize_recursive((void *) field); - g_free(field); -} - -static -void bt_ctf_field_array_destroy_recursive(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer array field object: addr=%p", field); - bt_ctf_field_common_array_finalize_recursive((void *) field); - g_free(field); -} - -static -void bt_ctf_field_sequence_destroy_recursive(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer sequence field object: addr=%p", field); - bt_ctf_field_common_sequence_finalize_recursive((void *) field); - g_free(field); -} - -static -void bt_ctf_field_string_destroy(struct bt_ctf_field *field) -{ - BT_LOGD("Destroying CTF writer string field object: addr=%p", field); - bt_ctf_field_common_string_finalize((void *) field); - g_free(field); -} - -BT_HIDDEN -int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - struct bt_ctf_field_common *field_common = (void *) field; - bt_ctf_field_serialize_recursive_func serialize_func; - - BT_ASSERT(ctfser); - BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT(field_common->spec.writer.serialize_func); - serialize_func = field_common->spec.writer.serialize_func; - return serialize_func(field_common, ctfser, - native_byte_order); -} - -static -int bt_ctf_field_integer_serialize(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int ret; - struct bt_ctf_field_type_common_integer *int_type = - BT_CTF_FROM_COMMON(field->type); - struct bt_ctf_field_common_integer *int_field = - BT_CTF_FROM_COMMON(field); - enum bt_ctf_byte_order byte_order; - - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Integer field"); - BT_LOGV("Serializing CTF writer integer field: addr=%p, native-bo=%s", - field, - bt_ctf_byte_order_string(native_byte_order)); - byte_order = int_type->user_byte_order; - if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { - byte_order = native_byte_order; - } - - if (int_type->is_signed) { - ret = bt_ctfser_write_signed_int(ctfser, - int_field->payload.signd, int_type->common.alignment, - int_type->size, - byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? - LITTLE_ENDIAN : BIG_ENDIAN); - } else { - ret = bt_ctfser_write_unsigned_int(ctfser, - int_field->payload.unsignd, int_type->common.alignment, - int_type->size, - byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? - LITTLE_ENDIAN : BIG_ENDIAN); - } - - if (unlikely(ret)) { - BT_LOGE("Cannot serialize integer field: ret=%d", ret); - goto end; - } - -end: - return ret; -} - -static -int bt_ctf_field_enumeration_serialize_recursive( - struct bt_ctf_field_common *field, struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - BT_LOGV("Serializing enumeration field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string(native_byte_order)); - BT_LOGV_STR("Serializing enumeration field's payload field."); - return bt_ctf_field_serialize_recursive( - (void *) enumeration->container, ctfser, native_byte_order); -} - -static -int bt_ctf_field_floating_point_serialize(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int ret = -1; - struct bt_ctf_field_type_common_floating_point *flt_type = - BT_CTF_FROM_COMMON(field->type); - struct bt_ctf_field_common_floating_point *flt_field = BT_CTF_FROM_COMMON(field); - enum bt_ctf_byte_order byte_order; - - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Floating point number field"); - BT_LOGV("Serializing floating point number field: " - "addr=%p, native-bo=%s", field, - bt_ctf_byte_order_string(native_byte_order)); - - byte_order = flt_type->user_byte_order; - if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { - byte_order = native_byte_order; - } - - if (flt_type->mant_dig == FLT_MANT_DIG) { - ret = bt_ctfser_write_float32(ctfser, flt_field->payload, - flt_type->common.alignment, - byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? - LITTLE_ENDIAN : BIG_ENDIAN); - } else if (flt_type->mant_dig == DBL_MANT_DIG) { - ret = bt_ctfser_write_float64(ctfser, flt_field->payload, - flt_type->common.alignment, - byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? - LITTLE_ENDIAN : BIG_ENDIAN); - } else { - abort(); - } - - if (unlikely(ret)) { - BT_LOGE("Cannot serialize floating point number field: " - "ret=%d", ret); - goto end; - } - -end: - return ret; -} - -static -int bt_ctf_field_structure_serialize_recursive(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int64_t i; - int ret; - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_LOGV("Serializing structure field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string(native_byte_order)); - ret = bt_ctfser_align_offset_in_current_packet(ctfser, - field->type->alignment); - if (unlikely(ret)) { - BT_LOGE("Cannot align offset before serializing structure field: " - "ret=%d", ret); - goto end; - } - - for (i = 0; i < structure->fields->len; i++) { - struct bt_ctf_field_common *member = g_ptr_array_index( - structure->fields, i); - const char *field_name = NULL; - - BT_LOGV("Serializing structure field's field: ser-offset=%" PRIu64 ", " - "field-addr=%p, index=%" PRIu64, - bt_ctfser_get_offset_in_current_packet_bits(ctfser), - member, i); - - if (unlikely(!member)) { - ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - field->type, &field_name, NULL, i); - BT_ASSERT(ret == 0); - BT_LOGW("Cannot serialize structure field's field: field is not set: " - "struct-field-addr=%p, " - "field-name=\"%s\", index=%" PRId64, - field, field_name, i); - ret = -1; - goto end; - } - - ret = bt_ctf_field_serialize_recursive((void *) member, ctfser, - native_byte_order); - if (unlikely(ret)) { - ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - field->type, &field_name, NULL, i); - BT_ASSERT(ret == 0); - BT_LOGW("Cannot serialize structure field's field: " - "struct-field-addr=%p, field-addr=%p, " - "field-name=\"%s\", index=%" PRId64, - field->type, member, field_name, i); - break; - } - } - -end: - return ret; -} - -static -int bt_ctf_field_variant_serialize_recursive(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - - BT_LOGV("Serializing variant field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string(native_byte_order)); - BT_LOGV_STR("Serializing variant field's payload field."); - return bt_ctf_field_serialize_recursive( - (void *) variant->current_field, ctfser, native_byte_order); -} - -static -int bt_ctf_field_array_serialize_recursive(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int64_t i; - int ret = 0; - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_LOGV("Serializing array field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string(native_byte_order)); - - for (i = 0; i < array->elements->len; i++) { - struct bt_ctf_field_common *elem_field = - g_ptr_array_index(array->elements, i); - - BT_LOGV("Serializing array field's element field: " - "ser-offset=%" PRIu64 ", field-addr=%p, index=%" PRId64, - bt_ctfser_get_offset_in_current_packet_bits(ctfser), - elem_field, i); - ret = bt_ctf_field_serialize_recursive( - (void *) elem_field, ctfser, native_byte_order); - if (unlikely(ret)) { - BT_LOGW("Cannot serialize array field's element field: " - "array-field-addr=%p, field-addr=%p, " - "index=%" PRId64, field, elem_field, i); - goto end; - } - } - -end: - return ret; -} - -static -int bt_ctf_field_sequence_serialize_recursive(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int64_t i; - int ret = 0; - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_LOGV("Serializing sequence field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string(native_byte_order)); - - for (i = 0; i < sequence->elements->len; i++) { - struct bt_ctf_field_common *elem_field = - g_ptr_array_index(sequence->elements, i); - - BT_LOGV("Serializing sequence field's element field: " - "ser-offset=%" PRIu64 ", field-addr=%p, index=%" PRId64, - bt_ctfser_get_offset_in_current_packet_bits(ctfser), - elem_field, i); - ret = bt_ctf_field_serialize_recursive( - (void *) elem_field, ctfser, native_byte_order); - if (unlikely(ret)) { - BT_LOGW("Cannot serialize sequence field's element field: " - "sequence-field-addr=%p, field-addr=%p, " - "index=%" PRId64, field, elem_field, i); - goto end; - } - } - -end: - return ret; -} - -static -int bt_ctf_field_string_serialize(struct bt_ctf_field_common *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order) -{ - int ret; - struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "String field"); - BT_LOGV("Serializing string field: addr=%p, native-bo=%s", - field, bt_ctf_byte_order_string((int) native_byte_order)); - ret = bt_ctfser_write_string(ctfser, (const char *) string->buf->data); - if (unlikely(ret)) { - BT_LOGE("Cannot serialize string field: ret=%d", ret); - goto end; - } - -end: - return ret; -} - -struct bt_ctf_field *bt_ctf_field_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field *field = NULL; - enum bt_ctf_field_type_id type_id; - - BT_CTF_ASSERT_PRE_NON_NULL(type, "Field type"); - BT_ASSERT(field_type_common_has_known_id((void *) type)); - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_validate((void *) type) == 0, - "Field type is invalid: ft-addr=%p", type); - type_id = bt_ctf_field_type_get_type_id(type); - field = field_create_funcs[type_id](type); - if (!field) { - goto end; - } - - bt_ctf_field_type_common_freeze((void *) type); - -end: - return field; -} - -struct bt_ctf_field_type *bt_ctf_field_get_type(struct bt_ctf_field *field) -{ - return bt_ctf_object_get_ref(bt_ctf_field_common_borrow_type((void *) field)); -} - -enum bt_ctf_field_type_id bt_ctf_field_get_type_id(struct bt_ctf_field *field) -{ - struct bt_ctf_field_common *field_common = (void *) field; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); - return (int) field_common->type->id; -} - -int bt_ctf_field_sequence_set_length(struct bt_ctf_field *field, - struct bt_ctf_field *length_field) -{ - int ret; - struct bt_ctf_field_common *common_length_field = (void *) length_field; - uint64_t length; - - BT_CTF_ASSERT_PRE_NON_NULL(length_field, "Length field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((void *) length_field, "Length field"); - BT_CTF_ASSERT_PRE(common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_INTEGER || - common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_ENUM, - "Length field must be an integer or enumeration field: field-addr=%p", - length_field); - - if (common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_ENUM) { - struct bt_ctf_field_enumeration *enumeration = (void *) - length_field; - - length_field = (void *) enumeration->container; - } - - ret = bt_ctf_field_integer_unsigned_get_value(length_field, &length); - BT_ASSERT(ret == 0); - return bt_ctf_field_common_sequence_set_length((void *) field, - length, (bt_ctf_field_common_create_func) bt_ctf_field_create); -} - -struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index( - struct bt_ctf_field *field, uint64_t index) -{ - return bt_ctf_object_get_ref(bt_ctf_field_common_structure_borrow_field_by_index( - (void *) field, index)); -} - -struct bt_ctf_field *bt_ctf_field_structure_get_field_by_name( - struct bt_ctf_field *field, const char *name) -{ - return bt_ctf_object_get_ref(bt_ctf_field_common_structure_borrow_field_by_name( - (void *) field, name)); -} - -struct bt_ctf_field *bt_ctf_field_array_get_field( - struct bt_ctf_field *field, uint64_t index) -{ - return bt_ctf_object_get_ref( - bt_ctf_field_common_array_borrow_field((void *) field, index)); -} - -struct bt_ctf_field *bt_ctf_field_sequence_get_field( - struct bt_ctf_field *field, uint64_t index) -{ - return bt_ctf_object_get_ref( - bt_ctf_field_common_sequence_borrow_field((void *) field, index)); -} - -struct bt_ctf_field *bt_ctf_field_variant_get_field(struct bt_ctf_field *field, - struct bt_ctf_field *tag_field) -{ - struct bt_ctf_field_variant *variant_field = (void *) field; - struct bt_ctf_field_enumeration *enum_field = (void *) tag_field; - struct bt_ctf_field_type_common_variant *variant_ft; - struct bt_ctf_field_type_common_enumeration *tag_ft; - struct bt_ctf_field *current_field = NULL; - bt_bool is_signed; - uint64_t tag_uval; - int ret; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Variant field"); - BT_CTF_ASSERT_PRE_NON_NULL(tag_field, "Tag field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((void *) tag_field, "Tag field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( - (struct bt_ctf_field_common *) tag_field, - BT_CTF_FIELD_TYPE_ID_ENUM, "Tag field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( - (struct bt_ctf_field_common *) field, - BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); - BT_CTF_ASSERT_PRE( - bt_ctf_field_common_validate_recursive((void *) tag_field) == 0, - "Tag field is invalid: field-addr=%p", tag_field); - variant_ft = BT_CTF_FROM_COMMON(variant_field->common.common.type); - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( - BT_CTF_TO_COMMON(variant_ft->tag_ft), enum_field->common.type) == 0, - "Unexpected tag field's type: expected-ft-addr=%p, " - "tag-ft-addr=%p", variant_ft->tag_ft, - enum_field->common.type); - tag_ft = BT_CTF_FROM_COMMON(enum_field->common.type); - is_signed = tag_ft->container_ft->is_signed; - - if (is_signed) { - int64_t tag_ival; - - ret = bt_ctf_field_integer_signed_get_value( - (void *) enum_field->container, &tag_ival); - tag_uval = (uint64_t) tag_ival; - } else { - ret = bt_ctf_field_integer_unsigned_get_value( - (void *) enum_field->container, &tag_uval); - } - - BT_ASSERT(ret == 0); - ret = bt_ctf_field_common_variant_set_tag((void *) field, tag_uval, - is_signed); - if (ret) { - goto end; - } - - bt_ctf_object_put_ref(variant_field->tag); - variant_field->tag = bt_ctf_object_get_ref(tag_field); - current_field = bt_ctf_field_variant_get_current_field(field); - BT_ASSERT(current_field); - -end: - return current_field; -} - -struct bt_ctf_field *bt_ctf_field_variant_get_current_field( - struct bt_ctf_field *variant_field) -{ - return bt_ctf_object_get_ref(bt_ctf_field_common_variant_borrow_current_field( - (void *) variant_field)); -} - -BT_HIDDEN -struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container( - struct bt_ctf_field *field) -{ - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Enumeration field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID((struct bt_ctf_field_common *) field, - BT_CTF_FIELD_TYPE_ID_ENUM, "Field"); - BT_ASSERT(enumeration->container); - return (void *) enumeration->container; -} - -struct bt_ctf_field *bt_ctf_field_enumeration_get_container( - struct bt_ctf_field *field) -{ - return bt_ctf_object_get_ref(bt_ctf_field_enumeration_borrow_container(field)); -} - -int bt_ctf_field_integer_signed_get_value(struct bt_ctf_field *field, - int64_t *value) -{ - struct bt_ctf_field_common_integer *integer = (void *) field; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(BT_CTF_TO_COMMON(integer), "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), - BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); - BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_integer_is_signed( - integer->common.type), - "Field's type is unsigned: field-addr=%p", field); - *value = integer->payload.signd; - return 0; -} - -int bt_ctf_field_integer_signed_set_value(struct bt_ctf_field *field, - int64_t value) -{ - int ret = 0; - struct bt_ctf_field_common_integer *integer = (void *) field; - struct bt_ctf_field_type_common_integer *integer_type; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(BT_CTF_TO_COMMON(integer), "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), - BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); - integer_type = BT_CTF_FROM_COMMON(integer->common.type); - BT_CTF_ASSERT_PRE( - bt_ctf_field_type_common_integer_is_signed(integer->common.type), - "Field's type is unsigned: field-addr=%p", field); - BT_CTF_ASSERT_PRE(value_is_in_range_signed(integer_type->size, value), - "Value is out of bounds: value=%" PRId64 ", field-addr=%p", - value, field); - integer->payload.signd = value; - bt_ctf_field_common_set(BT_CTF_TO_COMMON(integer), true); - return ret; -} - -int bt_ctf_field_integer_unsigned_get_value(struct bt_ctf_field *field, - uint64_t *value) -{ - struct bt_ctf_field_common_integer *integer = (void *) field; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(BT_CTF_TO_COMMON(integer), "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), - BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); - BT_CTF_ASSERT_PRE( - !bt_ctf_field_type_common_integer_is_signed(integer->common.type), - "Field's type is signed: field-addr=%p", field); - *value = integer->payload.unsignd; - return 0; -} - -int bt_ctf_field_integer_unsigned_set_value(struct bt_ctf_field *field, - uint64_t value) -{ - struct bt_ctf_field_common_integer *integer = (void *) field; - struct bt_ctf_field_type_common_integer *integer_type; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(BT_CTF_TO_COMMON(integer), "Integer field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), - BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); - integer_type = BT_CTF_FROM_COMMON(integer->common.type); - BT_CTF_ASSERT_PRE( - !bt_ctf_field_type_common_integer_is_signed(integer->common.type), - "Field's type is signed: field-addr=%p", field); - BT_CTF_ASSERT_PRE(value_is_in_range_unsigned(integer_type->size, value), - "Value is out of bounds: value=%" PRIu64 ", field-addr=%p", - value, field); - integer->payload.unsignd = value; - bt_ctf_field_common_set(BT_CTF_TO_COMMON(integer), true); - return 0; -} - -int bt_ctf_field_floating_point_get_value(struct bt_ctf_field *field, - double *value) -{ - return bt_ctf_field_common_floating_point_get_value((void *) field, value); -} - -int bt_ctf_field_floating_point_set_value(struct bt_ctf_field *field, - double value) -{ - return bt_ctf_field_common_floating_point_set_value((void *) field, value); -} - -const char *bt_ctf_field_string_get_value(struct bt_ctf_field *field) -{ - return bt_ctf_field_common_string_get_value((void *) field); -} - -int bt_ctf_field_string_set_value(struct bt_ctf_field *field, const char *value) -{ - return bt_ctf_field_common_string_set_value((void *) field, value); -} - -int bt_ctf_field_string_append(struct bt_ctf_field *field, const char *value) -{ - return bt_ctf_field_common_string_append((void *) field, value); -} - -int bt_ctf_field_string_append_len(struct bt_ctf_field *field, - const char *value, unsigned int length) -{ - return bt_ctf_field_common_string_append_len((void *) field, value, length); -} - -struct bt_ctf_field *bt_ctf_field_copy(struct bt_ctf_field *field) -{ - return (void *) bt_ctf_field_common_copy((void *) field); -} - -static -struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_integer *integer = - g_new0(struct bt_ctf_field_common_integer, 1); - - BT_LOGD("Creating CTF writer integer field object: ft-addr=%p", type); - - if (integer) { - bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(integer), (void *) type, - true, - (bt_ctf_object_release_func) bt_ctf_field_integer_destroy, - &bt_ctf_field_integer_methods); - integer->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_integer_serialize; - BT_LOGD("Created CTF writer integer field object: addr=%p, ft-addr=%p", - integer, type); - } else { - BT_LOGE_STR("Failed to allocate one integer field."); - } - - return (void *) integer; -} - -static -struct bt_ctf_field *bt_ctf_field_enumeration_create( - struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_type_common_enumeration *enum_ft = (void *) type; - struct bt_ctf_field_enumeration *enumeration = g_new0( - struct bt_ctf_field_enumeration, 1); - - BT_LOGD("Creating CTF writer enumeration field object: ft-addr=%p", type); - - if (!enumeration) { - BT_LOGE_STR("Failed to allocate one enumeration field."); - goto end; - } - - bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(enumeration), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_enumeration_destroy_recursive, - &bt_ctf_field_enumeration_methods); - enumeration->container = (void *) bt_ctf_field_create( - BT_CTF_FROM_COMMON(enum_ft->container_ft)); - if (!enumeration->container) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(enumeration); - goto end; - } - - enumeration->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) - bt_ctf_field_enumeration_serialize_recursive; - BT_LOGD("Created CTF writer enumeration field object: addr=%p, ft-addr=%p", - enumeration, type); - -end: - return (void *) enumeration; -} - -static -struct bt_ctf_field *bt_ctf_field_floating_point_create( - struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_floating_point *floating_point; - - BT_LOGD("Creating CTF writer floating point number field object: ft-addr=%p", type); - floating_point = g_new0(struct bt_ctf_field_common_floating_point, 1); - - if (floating_point) { - bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(floating_point), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_floating_point_destroy, - &bt_ctf_field_floating_point_methods); - floating_point->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_floating_point_serialize; - BT_LOGD("Created CTF writer floating point number field object: addr=%p, ft-addr=%p", - floating_point, type); - } else { - BT_LOGE_STR("Failed to allocate one floating point number field."); - } - - return (void *) floating_point; -} - -static -struct bt_ctf_field *bt_ctf_field_structure_create( - struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_structure *structure = g_new0( - struct bt_ctf_field_common_structure, 1); - int iret; - - BT_LOGD("Creating CTF writer structure field object: ft-addr=%p", type); - - if (!structure) { - BT_LOGE_STR("Failed to allocate one structure field."); - goto end; - } - - iret = bt_ctf_field_common_structure_initialize(BT_CTF_TO_COMMON(structure), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_structure_destroy_recursive, - &bt_ctf_field_structure_methods, - (bt_ctf_field_common_create_func) bt_ctf_field_create, - (GDestroyNotify) bt_ctf_object_put_ref); - structure->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_structure_serialize_recursive; - if (iret) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(structure); - goto end; - } - - BT_LOGD("Created CTF writer structure field object: addr=%p, ft-addr=%p", - structure, type); - -end: - return (void *) structure; -} - -static -struct bt_ctf_field *bt_ctf_field_variant_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_type_common_variant *var_ft = (void *) type; - struct bt_ctf_field_variant *variant = g_new0( - struct bt_ctf_field_variant, 1); - - BT_LOGD("Creating CTF writer variant field object: ft-addr=%p", type); - - if (!variant) { - BT_LOGE_STR("Failed to allocate one variant field."); - goto end; - } - - bt_ctf_field_common_variant_initialize(BT_CTF_TO_COMMON(BT_CTF_TO_COMMON(variant)), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_variant_destroy_recursive, - &bt_ctf_field_variant_methods, - (bt_ctf_field_common_create_func) bt_ctf_field_create, - (GDestroyNotify) bt_ctf_object_put_ref); - variant->tag = (void *) bt_ctf_field_create( - BT_CTF_FROM_COMMON(var_ft->tag_ft)); - variant->common.common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_variant_serialize_recursive; - BT_LOGD("Created CTF writer variant field object: addr=%p, ft-addr=%p", - variant, type); - -end: - return (void *) variant; -} - -static -struct bt_ctf_field *bt_ctf_field_array_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_array *array = - g_new0(struct bt_ctf_field_common_array, 1); - int ret; - - BT_LOGD("Creating CTF writer array field object: ft-addr=%p", type); - BT_ASSERT(type); - - if (!array) { - BT_LOGE_STR("Failed to allocate one array field."); - goto end; - } - - ret = bt_ctf_field_common_array_initialize(BT_CTF_TO_COMMON(array), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_array_destroy_recursive, - &bt_ctf_field_array_methods, - (bt_ctf_field_common_create_func) bt_ctf_field_create, - (GDestroyNotify) bt_ctf_object_put_ref); - array->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_array_serialize_recursive; - if (ret) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(array); - goto end; - } - - BT_LOGD("Created CTF writer array field object: addr=%p, ft-addr=%p", - array, type); - -end: - return (void *) array; -} - -static -struct bt_ctf_field *bt_ctf_field_sequence_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_sequence *sequence = g_new0( - struct bt_ctf_field_common_sequence, 1); - - BT_LOGD("Creating CTF writer sequence field object: ft-addr=%p", type); - - if (sequence) { - bt_ctf_field_common_sequence_initialize(BT_CTF_TO_COMMON(sequence), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_sequence_destroy_recursive, - &bt_ctf_field_sequence_methods, - (GDestroyNotify) bt_ctf_object_put_ref); - sequence->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_sequence_serialize_recursive; - BT_LOGD("Created CTF writer sequence field object: addr=%p, ft-addr=%p", - sequence, type); - } else { - BT_LOGE_STR("Failed to allocate one sequence field."); - } - - return (void *) sequence; -} - -static -struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *type) -{ - struct bt_ctf_field_common_string *string = g_new0( - struct bt_ctf_field_common_string, 1); - - BT_LOGD("Creating CTF writer string field object: ft-addr=%p", type); - - if (string) { - bt_ctf_field_common_string_initialize(BT_CTF_TO_COMMON(string), - (void *) type, - true, (bt_ctf_object_release_func) - bt_ctf_field_string_destroy, - &bt_ctf_field_string_methods); - string->common.spec.writer.serialize_func = - (bt_ctf_field_serialize_recursive_func) bt_ctf_field_string_serialize; - BT_LOGD("Created CTF writer string field object: addr=%p, ft-addr=%p", - string, type); - } else { - BT_LOGE_STR("Failed to allocate one string field."); - } - - return (void *) string; -} - -static -void bt_ctf_field_enumeration_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - if (enumeration->container) { - bt_ctf_field_common_set_is_frozen_recursive( - (void *) enumeration->container, is_frozen); - } - - bt_ctf_field_common_generic_set_is_frozen((void *) field, is_frozen); -} - -static -int bt_ctf_field_enumeration_validate_recursive(struct bt_ctf_field_common *field) -{ - int ret = -1; - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - if (enumeration->container) { - ret = bt_ctf_field_common_validate_recursive( - (void *) enumeration->container); - } - - return ret; -} - -static -bt_bool bt_ctf_field_enumeration_is_set_recursive(struct bt_ctf_field_common *field) -{ - bt_bool is_set = BT_FALSE; - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - if (enumeration->container) { - is_set = bt_ctf_field_common_is_set_recursive( - (void *) enumeration->container); - } - - return is_set; -} - -static -void bt_ctf_field_enumeration_reset_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_enumeration *enumeration = (void *) field; - - if (enumeration->container) { - bt_ctf_field_common_reset_recursive( - (void *) enumeration->container); - } - - bt_ctf_field_common_generic_reset((void *) field); -} - -static -void bt_ctf_field_variant_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen) -{ - struct bt_ctf_field_variant *variant = (void *) field; - - if (variant->tag) { - bt_ctf_field_common_set_is_frozen_recursive( - (void *) variant->tag, is_frozen); - } - - bt_ctf_field_common_variant_set_is_frozen_recursive((void *) field, - is_frozen); -} - -static -int bt_ctf_field_variant_validate_recursive(struct bt_ctf_field_common *field) -{ - int ret; - struct bt_ctf_field_variant *variant = (void *) field; - - if (variant->tag) { - ret = bt_ctf_field_common_validate_recursive( - (void *) variant->tag); - if (ret) { - goto end; - } - } - - ret = bt_ctf_field_common_variant_validate_recursive((void *) field); - -end: - return ret; -} - -static -bt_bool bt_ctf_field_variant_is_set_recursive(struct bt_ctf_field_common *field) -{ - bt_bool is_set; - struct bt_ctf_field_variant *variant = (void *) field; - - if (variant->tag) { - is_set = bt_ctf_field_common_is_set_recursive( - (void *) variant->tag); - if (is_set) { - goto end; - } - } - - is_set = bt_ctf_field_common_variant_is_set_recursive((void *) field); - -end: - return is_set; -} - -static -void bt_ctf_field_variant_reset_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_variant *variant = (void *) field; - - if (variant->tag) { - bt_ctf_field_common_reset_recursive( - (void *) variant->tag); - } - - bt_ctf_field_common_variant_reset_recursive((void *) field); -} - -BT_CTF_ASSERT_PRE_FUNC -static inline bool field_to_set_has_expected_type( - struct bt_ctf_field_common *struct_field, - const char *name, struct bt_ctf_field_common *value) -{ - bool ret = true; - struct bt_ctf_field_type_common *expected_field_type = NULL; - - expected_field_type = - bt_ctf_field_type_common_structure_borrow_field_type_by_name( - struct_field->type, name); - - if (bt_ctf_field_type_common_compare(expected_field_type, value->type)) { - BT_CTF_ASSERT_PRE_MSG("Value field's type is different from the expected field type: " - "value-ft-addr=%p, expected-ft-addr=%p", value->type, - expected_field_type); - ret = false; - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field, - const char *name, struct bt_ctf_field *value) -{ - int ret = 0; - GQuark field_quark; - struct bt_ctf_field_common *common_field = (void *) field; - struct bt_ctf_field_common_structure *structure = - BT_CTF_FROM_COMMON(common_field); - struct bt_ctf_field_common *common_value = (void *) value; - size_t index; - GHashTable *field_name_to_index; - struct bt_ctf_field_type_common_structure *structure_ft; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Parent field"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Field name"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(common_field, - BT_CTF_FIELD_TYPE_ID_STRUCT, "Parent field"); - BT_CTF_ASSERT_PRE(field_to_set_has_expected_type(common_field, - name, common_value), - "Value field's type is different from the expected field type."); - field_quark = g_quark_from_string(name); - structure_ft = BT_CTF_FROM_COMMON(common_field->type); - field_name_to_index = structure_ft->field_name_to_index; - if (!g_hash_table_lookup_extended(field_name_to_index, - GUINT_TO_POINTER(field_quark), NULL, - (gpointer *) &index)) { - BT_LOGV("Invalid parameter: no such field in structure field's type: " - "struct-field-addr=%p, struct-ft-addr=%p, " - "field-ft-addr=%p, name=\"%s\"", - field, common_field->type, common_value->type, name); - ret = -1; - goto end; - } - bt_ctf_object_get_ref(value); - BT_CTF_OBJECT_MOVE_REF(structure->fields->pdata[index], value); - -end: - return ret; -} diff --git a/ctf-writer/functor.c b/ctf-writer/functor.c deleted file mode 100644 index e8ad20f8..00000000 --- a/ctf-writer/functor.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * functor.c - * - * Babeltrace CTF Writer - * - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -BT_HIDDEN -void value_exists(gpointer element, gpointer search_query) -{ - if (element == ((struct bt_ctf_search_query *)search_query)->value) { - ((struct bt_ctf_search_query *)search_query)->found = 1; - } -} diff --git a/ctf-writer/logging.c b/ctf-writer/logging.c deleted file mode 100644 index 641d5d99..00000000 --- a/ctf-writer/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_lib_ctf_writer_log_level -#include "logging.h" - -BT_LOG_INIT_LOG_LEVEL(bt_lib_ctf_writer_log_level, - "BABELTRACE_CTF_WRITER_LOG_LEVEL"); diff --git a/ctf-writer/logging.h b/ctf-writer/logging.h deleted file mode 100644 index a6cf543d..00000000 --- a/ctf-writer/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_LOGGING_H -#define BABELTRACE_CTF_WRITER_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_lib_ctf_writer_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_lib_ctf_writer_log_level); - -#endif /* BABELTRACE_CTF_WRITER_LOGGING_H */ diff --git a/ctf-writer/object-pool.c b/ctf-writer/object-pool.c deleted file mode 100644 index 2d932ac9..00000000 --- a/ctf-writer/object-pool.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "OBJECT-POOL" -#include "logging.h" - -#include -#include -#include - -int bt_ctf_object_pool_initialize(struct bt_ctf_object_pool *pool, - bt_ctf_object_pool_new_object_func new_object_func, - bt_ctf_object_pool_destroy_object_func destroy_object_func, - void *data) -{ - int ret = 0; - - BT_ASSERT(new_object_func); - BT_ASSERT(destroy_object_func); - BT_LOGD("Initializing object pool: addr=%p, data-addr=%p", - pool, data); - pool->objects = g_ptr_array_new(); - if (!pool->objects) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - pool->funcs.new_object = new_object_func; - pool->funcs.destroy_object = destroy_object_func; - pool->data = data; - pool->size = 0; - BT_LOGD("Initialized object pool."); - goto end; - -error: - if (pool) { - bt_ctf_object_pool_finalize(pool); - } - - ret = -1; - -end: - return ret; -} - -void bt_ctf_object_pool_finalize(struct bt_ctf_object_pool *pool) -{ - uint64_t i; - - BT_ASSERT(pool); - BT_LOGD("Finalizing object pool."); - - if (pool->objects) { - for (i = 0; i < pool->size; i++) { - void *obj = pool->objects->pdata[i]; - - if (obj) { - pool->funcs.destroy_object(obj, pool->data); - } - } - - g_ptr_array_free(pool->objects, TRUE); - pool->objects = NULL; - } -} diff --git a/ctf-writer/object.c b/ctf-writer/object.c deleted file mode 100644 index b6185121..00000000 --- a/ctf-writer/object.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -void *bt_ctf_object_get_ref(void *obj) -{ - if (unlikely(!obj)) { - goto end; - } - - bt_ctf_object_get_no_null_check(obj); - -end: - return obj; -} - -void bt_ctf_object_put_ref(void *obj) -{ - if (unlikely(!obj)) { - return; - } - - bt_ctf_object_put_no_null_check(obj); -} diff --git a/ctf-writer/resolve.c b/ctf-writer/resolve.c deleted file mode 100644 index b0a5e156..00000000 --- a/ctf-writer/resolve.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * resolve.c - * - * Babeltrace - CTF writer: Type resolving internal - * - * Copyright 2015 Jérémie Galarneau - * Copyright 2016 Philippe Proulx - * - * Authors: Jérémie Galarneau - * Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-RESOLVE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef GPtrArray type_stack; - -/* - * A stack frame. - * - * `type` contains a compound field type (structure, variant, array, - * or sequence) and `index` indicates the index of the field type in - * the upper frame (-1 for array and sequence field types). - * - * `type` is owned by the stack frame. - */ -struct type_stack_frame { - struct bt_ctf_field_type_common *type; - int index; -}; - -/* - * The current context of the resolving engine. - * - * `scopes` contain the 6 CTF scope field types (see CTF, sect. 7.3.2) - * in the following order: - * - * * Packet header - * * Packet context - * * Event header - * * Stream event context - * * Event context - * * Event payload - */ -struct resolve_context { - struct bt_ctf_private_value *environment; - struct bt_ctf_field_type_common *scopes[6]; - - /* Root scope being visited */ - enum bt_ctf_scope root_scope; - type_stack *type_stack; - struct bt_ctf_field_type_common *cur_field_type; -}; - -/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */ -static const char * const absolute_path_prefixes[] = { - [BT_CTF_SCOPE_ENV] = "env.", - [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = "trace.packet.header.", - [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = "stream.packet.context.", - [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = "stream.event.header.", - [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = "stream.event.context.", - [BT_CTF_SCOPE_EVENT_CONTEXT] = "event.context.", - [BT_CTF_SCOPE_EVENT_FIELDS] = "event.fields.", -}; - -/* Number of path tokens used for the absolute prefixes */ -static const int absolute_path_prefix_ptoken_counts[] = { - [BT_CTF_SCOPE_ENV] = 1, - [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = 3, - [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = 3, - [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = 3, - [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = 3, - [BT_CTF_SCOPE_EVENT_CONTEXT] = 2, - [BT_CTF_SCOPE_EVENT_FIELDS] = 2, -}; - -/* - * Destroys a type stack frame. - */ -static -void type_stack_destroy_notify(gpointer data) -{ - struct type_stack_frame *frame = data; - - BT_CTF_OBJECT_PUT_REF_AND_RESET(frame->type); - g_free(frame); -} - -/* - * Creates a type stack. - * - * Return value is owned by the caller. - */ -static -type_stack *type_stack_create(void) -{ - return g_ptr_array_new_with_free_func(type_stack_destroy_notify); -} - -/* - * Destroys a type stack. - */ -static -void type_stack_destroy(type_stack *stack) -{ - g_ptr_array_free(stack, TRUE); -} - -/* - * Pushes a field type onto a type stack. - * - * `type` is owned by the caller (stack frame gets a new reference). - */ -static -int type_stack_push(type_stack *stack, struct bt_ctf_field_type_common *type) -{ - int ret = 0; - struct type_stack_frame *frame = NULL; - - if (!stack || !type) { - BT_LOGW("Invalid parameter: stack or type is NULL."); - ret = -1; - goto end; - } - - frame = g_new0(struct type_stack_frame, 1); - if (!frame) { - BT_LOGE_STR("Failed to allocate one field type stack frame."); - ret = -1; - goto end; - } - - BT_LOGV("Pushing field type on context's stack: " - "ft-addr=%p, stack-size-before=%u", type, stack->len); - frame->type = bt_ctf_object_get_ref(type); - g_ptr_array_add(stack, frame); - -end: - return ret; -} - -/* - * Checks whether or not `stack` is empty. - */ -static -bt_bool type_stack_empty(type_stack *stack) -{ - return stack->len == 0; -} - -/* - * Returns the number of frames in `stack`. - */ -static -size_t type_stack_size(type_stack *stack) -{ - return stack->len; -} - -/* - * Returns the top frame of `stack`. - * - * Return value is owned by `stack`. - */ -static -struct type_stack_frame *type_stack_peek(type_stack *stack) -{ - struct type_stack_frame *entry = NULL; - - if (!stack || type_stack_empty(stack)) { - goto end; - } - - entry = g_ptr_array_index(stack, stack->len - 1); -end: - return entry; -} - -/* - * Returns the frame at index `index` in `stack`. - * - * Return value is owned by `stack`. - */ -static -struct type_stack_frame *type_stack_at(type_stack *stack, - size_t index) -{ - struct type_stack_frame *entry = NULL; - - if (!stack || index >= stack->len) { - goto end; - } - - entry = g_ptr_array_index(stack, index); - -end: - return entry; -} - -/* - * Removes the top frame of `stack`. - */ -static -void type_stack_pop(type_stack *stack) -{ - if (!type_stack_empty(stack)) { - /* - * This will call the frame's destructor and free it, as - * well as put its contained field type. - */ - BT_LOGV("Popping context's stack: stack-size-before=%u", - stack->len); - g_ptr_array_set_size(stack, stack->len - 1); - } -} - -/* - * Returns the scope field type of `scope` in the context `ctx`. - * - * Return value is owned by `ctx` on success. - */ -static -struct bt_ctf_field_type_common *get_type_from_ctx(struct resolve_context *ctx, - enum bt_ctf_scope scope) -{ - BT_ASSERT(scope >= BT_CTF_SCOPE_TRACE_PACKET_HEADER && - scope <= BT_CTF_SCOPE_EVENT_FIELDS); - - return ctx->scopes[scope - BT_CTF_SCOPE_TRACE_PACKET_HEADER]; -} - -/* - * Returns the CTF scope from a path string. May return - * CTF_NODE_UNKNOWN if the path is found to be relative. - */ -static -enum bt_ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr) -{ - enum bt_ctf_scope scope; - enum bt_ctf_scope ret = BT_CTF_SCOPE_UNKNOWN; - const size_t prefixes_count = sizeof(absolute_path_prefixes) / - sizeof(*absolute_path_prefixes); - - for (scope = BT_CTF_SCOPE_ENV; scope < BT_CTF_SCOPE_ENV + - prefixes_count; scope++) { - /* - * Chech if path string starts with a known absolute - * path prefix. - * - * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES. - */ - if (strncmp(pathstr, absolute_path_prefixes[scope], - strlen(absolute_path_prefixes[scope]))) { - /* Prefix does not match: try the next one */ - BT_LOGV("Prefix does not match: trying the next one: " - "path=\"%s\", path-prefix=\"%s\", scope=%s", - pathstr, absolute_path_prefixes[scope], - bt_ctf_scope_string(scope)); - continue; - } - - /* Found it! */ - ret = scope; - BT_LOGV("Found root scope from absolute path: " - "path=\"%s\", scope=%s", pathstr, - bt_ctf_scope_string(scope)); - goto end; - } - -end: - return ret; -} - -/* - * Destroys a path token. - */ -static -void ptokens_destroy_func(gpointer ptoken, gpointer data) -{ - g_string_free(ptoken, TRUE); -} - -/* - * Destroys a path token list. - */ -static -void ptokens_destroy(GList *ptokens) -{ - if (!ptokens) { - return; - } - - g_list_foreach(ptokens, ptokens_destroy_func, NULL); - g_list_free(ptokens); -} - -/* - * Returns the string contained in a path token. - */ -static -const char *ptoken_get_string(GList *ptoken) -{ - GString *tokenstr = (GString *) ptoken->data; - - return tokenstr->str; -} - -/* - * Converts a path string to a path token list, that is, splits the - * individual words of a path string into a list of individual - * strings. - * - * Return value is owned by the caller on success. - */ -static -GList *pathstr_to_ptokens(const char *pathstr) -{ - const char *at = pathstr; - const char *last = at; - GList *ptokens = NULL; - - for (;;) { - if (*at == '.' || *at == '\0') { - GString *tokenstr; - - if (at == last) { - /* Error: empty token */ - BT_LOGW("Empty path token: path=\"%s\", pos=%u", - pathstr, (int) (at - pathstr)); - goto error; - } - - tokenstr = g_string_new(NULL); - g_string_append_len(tokenstr, last, at - last); - ptokens = g_list_append(ptokens, tokenstr); - last = at + 1; - } - - if (*at == '\0') { - break; - } - - at++; - } - - return ptokens; - -error: - ptokens_destroy(ptokens); - return NULL; -} - -/* - * Converts a path token list to a field path object. The path token - * list is relative from `type`. The index of the source looking for - * its target within `type` is indicated by `src_index`. This can be - * `INT_MAX` if the source is contained in `type`. - * - * `ptokens` is owned by the caller. `field_path` is an output parameter - * owned by the caller that must be filled here. `type` is owned by the - * caller. - */ -static -int ptokens_to_field_path(GList *ptokens, struct bt_ctf_field_path *field_path, - struct bt_ctf_field_type_common *type, int src_index) -{ - int ret = 0; - GList *cur_ptoken = ptokens; - bt_bool first_level_done = BT_FALSE; - - /* Get our own reference */ - bt_ctf_object_get_ref(type); - - /* Locate target */ - while (cur_ptoken) { - int child_index; - struct bt_ctf_field_type_common *child_type; - const char *field_name = ptoken_get_string(cur_ptoken); - enum bt_ctf_field_type_id type_id = - bt_ctf_field_type_common_get_type_id(type); - - BT_LOGV("Current path token: token=\"%s\"", field_name); - - /* Find to which index corresponds the current path token */ - if (type_id == BT_CTF_FIELD_TYPE_ID_ARRAY || - type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE) { - child_index = -1; - } else { - child_index = bt_ctf_field_type_common_get_field_index(type, - field_name); - if (child_index < 0) { - /* - * Error: field name does not exist or - * wrong current type. - */ - BT_LOGW("Cannot get index of field type: " - "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", - field_name, src_index, child_index, first_level_done); - ret = -1; - goto end; - } else if (child_index > src_index && - !first_level_done) { - BT_LOGW("Child field type is located after source field type: " - "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", - field_name, src_index, child_index, first_level_done); - ret = -1; - goto end; - } - - /* Next path token */ - cur_ptoken = g_list_next(cur_ptoken); - first_level_done = BT_TRUE; - } - - /* Create new field path entry */ - g_array_append_val(field_path->indexes, child_index); - - /* Get child field type */ - child_type = bt_ctf_field_type_common_borrow_field_at_index(type, - child_index); - if (!child_type) { - BT_LOGW("Cannot get child field type: " - "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", - field_name, src_index, child_index, first_level_done); - ret = -1; - goto end; - } - - /* Move child type to current type */ - bt_ctf_object_get_ref(child_type); - BT_CTF_OBJECT_MOVE_REF(type, child_type); - } - -end: - bt_ctf_object_put_ref(type); - return ret; -} - -/* - * Converts a known absolute path token list to a field path object - * within the resolving context `ctx`. - * - * `ptokens` is owned by the caller. `field_path` is an output parameter - * owned by the caller that must be filled here. - */ -static -int absolute_ptokens_to_field_path(GList *ptokens, - struct bt_ctf_field_path *field_path, - struct resolve_context *ctx) -{ - int ret = 0; - GList *cur_ptoken; - struct bt_ctf_field_type_common *type; - - /* Skip absolute path tokens */ - cur_ptoken = g_list_nth(ptokens, - absolute_path_prefix_ptoken_counts[field_path->root]); - - /* Start with root type */ - type = get_type_from_ctx(ctx, field_path->root); - if (!type) { - /* Error: root type is not available */ - BT_LOGW("Root field type is not available: " - "root-scope=%s", - bt_ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - /* Locate target */ - ret = ptokens_to_field_path(cur_ptoken, field_path, type, INT_MAX); - -end: - return ret; -} - -/* - * Converts a known relative path token list to a field path object - * within the resolving context `ctx`. - * - * `ptokens` is owned by the caller. `field_path` is an output parameter - * owned by the caller that must be filled here. - */ -static -int relative_ptokens_to_field_path(GList *ptokens, - struct bt_ctf_field_path *field_path, - struct resolve_context *ctx) -{ - int ret = 0; - int parent_pos_in_stack; - struct bt_ctf_field_path *tail_field_path = bt_ctf_field_path_create(); - - if (!tail_field_path) { - BT_LOGE_STR("Cannot create empty field path."); - ret = -1; - goto end; - } - - parent_pos_in_stack = type_stack_size(ctx->type_stack) - 1; - - while (parent_pos_in_stack >= 0) { - struct bt_ctf_field_type_common *parent_type = - type_stack_at(ctx->type_stack, - parent_pos_in_stack)->type; - int cur_index = type_stack_at(ctx->type_stack, - parent_pos_in_stack)->index; - - BT_LOGV("Locating target field type from current parent field type: " - "parent-pos=%d, parent-ft-addr=%p, cur-index=%d", - parent_pos_in_stack, parent_type, cur_index); - - /* Locate target from current parent type */ - ret = ptokens_to_field_path(ptokens, tail_field_path, - parent_type, cur_index); - if (ret) { - /* Not found... yet */ - BT_LOGV_STR("Not found at this point."); - bt_ctf_field_path_clear(tail_field_path); - } else { - /* Found: stitch tail field path to head field path */ - int i = 0; - int tail_field_path_len = - tail_field_path->indexes->len; - - while (BT_TRUE) { - struct bt_ctf_field_type_common *cur_type = - type_stack_at(ctx->type_stack, i)->type; - int index = type_stack_at( - ctx->type_stack, i)->index; - - if (cur_type == parent_type) { - break; - } - - g_array_append_val(field_path->indexes, - index); - i++; - } - - for (i = 0; i < tail_field_path_len; i++) { - int index = g_array_index( - tail_field_path->indexes, - int, i); - - g_array_append_val(field_path->indexes, - index); - } - break; - } - - parent_pos_in_stack--; - } - - if (parent_pos_in_stack < 0) { - /* Not found: look in previous scopes */ - field_path->root--; - - while (field_path->root >= BT_CTF_SCOPE_TRACE_PACKET_HEADER) { - struct bt_ctf_field_type_common *root_type; - bt_ctf_field_path_clear(field_path); - - BT_LOGV("Looking into potential root scope: scope=%s", - bt_ctf_scope_string(field_path->root)); - root_type = get_type_from_ctx(ctx, field_path->root); - if (!root_type) { - field_path->root--; - continue; - } - - /* Locate target in previous scope */ - ret = ptokens_to_field_path(ptokens, field_path, - root_type, INT_MAX); - if (ret) { - /* Not found yet */ - BT_LOGV_STR("Not found in this scope."); - field_path->root--; - continue; - } - - /* Found */ - BT_LOGV_STR("Found in this scope."); - break; - } - } - -end: - BT_CTF_OBJECT_PUT_REF_AND_RESET(tail_field_path); - return ret; -} - -/* - * Converts a path string to a field path object within the resolving - * context `ctx`. - * - * Return value is owned by the caller on success. - */ -static -struct bt_ctf_field_path *pathstr_to_field_path(const char *pathstr, - struct resolve_context *ctx) -{ - int ret; - enum bt_ctf_scope root_scope; - GList *ptokens = NULL; - struct bt_ctf_field_path *field_path = NULL; - - /* Create field path */ - field_path = bt_ctf_field_path_create(); - if (!field_path) { - BT_LOGE_STR("Cannot create empty field path."); - ret = -1; - goto end; - } - - /* Convert path string to path tokens */ - ptokens = pathstr_to_ptokens(pathstr); - if (!ptokens) { - BT_LOGW("Cannot convert path string to path tokens: " - "path=\"%s\"", pathstr); - ret = -1; - goto end; - } - - /* Absolute or relative path? */ - root_scope = get_root_scope_from_absolute_pathstr(pathstr); - - if (root_scope == BT_CTF_SCOPE_UNKNOWN) { - /* Relative path: start with current root scope */ - field_path->root = ctx->root_scope; - BT_LOGV("Detected relative path: starting with current root scope: " - "scope=%s", bt_ctf_scope_string(field_path->root)); - ret = relative_ptokens_to_field_path(ptokens, field_path, ctx); - if (ret) { - BT_LOGW("Cannot get relative field path of path string: " - "path=\"%s\", start-scope=%s, end-scope=%s", - pathstr, bt_ctf_scope_string(ctx->root_scope), - bt_ctf_scope_string(field_path->root)); - goto end; - } - } else if (root_scope == BT_CTF_SCOPE_ENV) { - BT_LOGW("Sequence field types referring the trace environment are not supported as of this version: " - "path=\"%s\"", pathstr); - ret = -1; - goto end; - } else { - /* Absolute path: use found root scope */ - field_path->root = root_scope; - BT_LOGV("Detected absolute path: using root scope: " - "scope=%s", bt_ctf_scope_string(field_path->root)); - ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx); - if (ret) { - BT_LOGW("Cannot get absolute field path of path string: " - "path=\"%s\", root-scope=%s", - pathstr, bt_ctf_scope_string(root_scope)); - goto end; - } - } - - if (ret == 0) { - GString *field_path_pretty = - bt_ctf_field_path_string(field_path); - const char *field_path_pretty_str = - field_path_pretty ? field_path_pretty->str : NULL; - - BT_LOGV("Found field path: path=\"%s\", field-path=\"%s\"", - pathstr, field_path_pretty_str); - - if (field_path_pretty) { - g_string_free(field_path_pretty, TRUE); - } - } - -end: - if (ret) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); - } - - ptokens_destroy(ptokens); - return field_path; -} - -/* - * Retrieves a field type by following the field path `field_path` in - * the resolving context `ctx`. - * - * Return value is owned by the caller on success. - */ -static -struct bt_ctf_field_type_common *field_path_to_field_type( - struct bt_ctf_field_path *field_path, - struct resolve_context *ctx) -{ - int i; - struct bt_ctf_field_type_common *type; - - /* Start with root type */ - type = get_type_from_ctx(ctx, field_path->root); - bt_ctf_object_get_ref(type); - if (!type) { - /* Error: root type is not available */ - BT_LOGW("Root field type is not available: root-scope=%s", - bt_ctf_scope_string(field_path->root)); - goto error; - } - - /* Locate target */ - for (i = 0; i < field_path->indexes->len; i++) { - struct bt_ctf_field_type_common *child_type; - int child_index = - g_array_index(field_path->indexes, int, i); - - /* Get child field type */ - child_type = bt_ctf_field_type_common_borrow_field_at_index(type, - child_index); - if (!child_type) { - BT_LOGW("Cannot get field type: " - "parent-ft-addr=%p, index=%d", type, i); - goto error; - } - - /* Move child type to current type */ - bt_ctf_object_get_ref(child_type); - BT_CTF_OBJECT_MOVE_REF(type, child_type); - } - - return type; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(type); - return type; -} - -/* - * Returns the equivalent field path object of the context type stack. - * - * Return value is owned by the caller on success. - */ -static -struct bt_ctf_field_path *get_ctx_stack_field_path(struct resolve_context *ctx) -{ - int i; - struct bt_ctf_field_path *field_path; - - /* Create field path */ - field_path = bt_ctf_field_path_create(); - if (!field_path) { - BT_LOGE_STR("Cannot create empty field path."); - goto error; - } - - field_path->root = ctx->root_scope; - - for (i = 0; i < type_stack_size(ctx->type_stack); i++) { - struct type_stack_frame *frame; - - frame = type_stack_at(ctx->type_stack, i); - g_array_append_val(field_path->indexes, frame->index); - } - - return field_path; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); - return field_path; -} - -/* - * Returns the lowest common ancestor of two field path objects - * having the same root scope. - * - * `field_path1` and `field_path2` are owned by the caller. - */ -static -int get_field_paths_lca_index(struct bt_ctf_field_path *field_path1, - struct bt_ctf_field_path *field_path2) -{ - int lca_index = 0; - int field_path1_len, field_path2_len; - - if (BT_LOG_ON_VERBOSE) { - GString *field_path1_pretty = - bt_ctf_field_path_string(field_path1); - GString *field_path2_pretty = - bt_ctf_field_path_string(field_path2); - const char *field_path1_pretty_str = - field_path1_pretty ? field_path1_pretty->str : NULL; - const char *field_path2_pretty_str = - field_path2_pretty ? field_path2_pretty->str : NULL; - - BT_LOGV("Finding lowest common ancestor (LCA) between two field paths: " - "field-path-1=\"%s\", field-path-2=\"%s\"", - field_path1_pretty_str, field_path2_pretty_str); - - if (field_path1_pretty) { - g_string_free(field_path1_pretty, TRUE); - } - - if (field_path2_pretty) { - g_string_free(field_path2_pretty, TRUE); - } - } - - /* - * Start from both roots and find the first mismatch. - */ - BT_ASSERT(field_path1->root == field_path2->root); - field_path1_len = field_path1->indexes->len; - field_path2_len = field_path2->indexes->len; - - while (BT_TRUE) { - int target_index, ctx_index; - - if (lca_index == field_path2_len || - lca_index == field_path1_len) { - /* - * This means that both field paths never split. - * This is invalid because the target cannot be - * an ancestor of the source. - */ - BT_LOGW("Source field type is an ancestor of target field type or vice versa: " - "lca-index=%d, field-path-1-len=%d, " - "field-path-2-len=%d", - lca_index, field_path1_len, field_path2_len); - lca_index = -1; - break; - } - - target_index = g_array_index(field_path1->indexes, int, - lca_index); - ctx_index = g_array_index(field_path2->indexes, int, - lca_index); - - if (target_index != ctx_index) { - /* LCA index is the previous */ - break; - } - - lca_index++; - } - - BT_LOGV("Found LCA: lca-index=%d", lca_index); - return lca_index; -} - -/* - * Validates a target field path. - * - * `target_field_path` and `target_type` are owned by the caller. - */ -static -int validate_target_field_path(struct bt_ctf_field_path *target_field_path, - struct bt_ctf_field_type_common *target_type, - struct resolve_context *ctx) -{ - int ret = 0; - struct bt_ctf_field_path *ctx_field_path; - int target_field_path_len = target_field_path->indexes->len; - int lca_index; - enum bt_ctf_field_type_id ctx_cur_field_type_id; - enum bt_ctf_field_type_id target_type_id; - - /* Get context field path */ - ctx_field_path = get_ctx_stack_field_path(ctx); - if (!ctx_field_path) { - BT_LOGW_STR("Cannot get field path from context's stack."); - ret = -1; - goto end; - } - - /* - * Make sure the target is not a root. - */ - if (target_field_path_len == 0) { - BT_LOGW_STR("Target field path's length is 0 (targeting the root)."); - ret = -1; - goto end; - } - - /* - * Make sure the root of the target field path is not located - * after the context field path's root. - */ - if (target_field_path->root > ctx_field_path->root) { - BT_LOGW("Target field type is located after source field type: " - "target-root=%s, source-root=%s", - bt_ctf_scope_string(target_field_path->root), - bt_ctf_scope_string(ctx_field_path->root)); - ret = -1; - goto end; - } - - if (target_field_path->root == ctx_field_path->root) { - int target_index, ctx_index; - - /* - * Find the index of the lowest common ancestor of both field - * paths. - */ - lca_index = get_field_paths_lca_index(target_field_path, - ctx_field_path); - if (lca_index < 0) { - BT_LOGW_STR("Cannot get least common ancestor."); - ret = -1; - goto end; - } - - /* - * Make sure the target field path is located before the - * context field path. - */ - target_index = g_array_index(target_field_path->indexes, - int, lca_index); - ctx_index = g_array_index(ctx_field_path->indexes, - int, lca_index); - - if (target_index >= ctx_index) { - BT_LOGW("Target field type's index is greater than or equal to source field type's index in LCA: " - "lca-index=%d, target-index=%d, source-index=%d", - lca_index, target_index, ctx_index); - ret = -1; - goto end; - } - } - - /* - * Make sure the target type has the right type and properties. - */ - ctx_cur_field_type_id = bt_ctf_field_type_common_get_type_id( - ctx->cur_field_type); - target_type_id = bt_ctf_field_type_common_get_type_id(target_type); - - switch (ctx_cur_field_type_id) { - case BT_CTF_FIELD_TYPE_ID_VARIANT: - if (target_type_id != BT_CTF_FIELD_TYPE_ID_ENUM) { - BT_LOGW("Variant field type's tag field type is not an enumeration field type: " - "tag-ft-addr=%p, tag-ft-id=%s", - target_type, - bt_ctf_field_type_id_string(target_type_id)); - ret = -1; - goto end; - } - break; - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - if (target_type_id != BT_CTF_FIELD_TYPE_ID_INTEGER || - bt_ctf_field_type_common_integer_is_signed(target_type)) { - BT_LOGW("Sequence field type's length field type is not an unsigned integer field type: " - "length-ft-addr=%p, length-ft-id=%s", - target_type, - bt_ctf_field_type_id_string(target_type_id)); - ret = -1; - goto end; - } - break; - default: - abort(); - } - -end: - BT_CTF_OBJECT_PUT_REF_AND_RESET(ctx_field_path); - return ret; -} - -/* - * Resolves a variant or sequence field type `type`. - * - * `type` is owned by the caller. - */ -static -int resolve_sequence_or_variant_type(struct bt_ctf_field_type_common *type, - struct resolve_context *ctx) -{ - int ret = 0; - const char *pathstr; - enum bt_ctf_field_type_id type_id = bt_ctf_field_type_common_get_type_id(type); - struct bt_ctf_field_path *target_field_path = NULL; - struct bt_ctf_field_type_common *target_type = NULL; - GString *target_field_path_pretty = NULL; - const char *target_field_path_pretty_str; - - - /* Get path string */ - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - pathstr = - bt_ctf_field_type_common_sequence_get_length_field_name(type); - break; - case BT_CTF_FIELD_TYPE_ID_VARIANT: - pathstr = - bt_ctf_field_type_common_variant_get_tag_name(type); - break; - default: - abort(); - } - - if (!pathstr) { - BT_LOGW_STR("Cannot get path string."); - ret = -1; - goto end; - } - - /* Get target field path out of path string */ - target_field_path = pathstr_to_field_path(pathstr, ctx); - if (!target_field_path) { - BT_LOGW("Cannot get target field path for path string: " - "path=\"%s\"", pathstr); - ret = -1; - goto end; - } - - target_field_path_pretty = bt_ctf_field_path_string(target_field_path); - target_field_path_pretty_str = - target_field_path_pretty ? target_field_path_pretty->str : NULL; - - /* Get target field type */ - target_type = field_path_to_field_type(target_field_path, ctx); - if (!target_type) { - BT_LOGW("Cannot get target field type for path string: " - "path=\"%s\", target-field-path=\"%s\"", - pathstr, target_field_path_pretty_str); - ret = -1; - goto end; - } - - ret = validate_target_field_path(target_field_path, target_type, ctx); - if (ret) { - BT_LOGW("Invalid target field path for path string: " - "path=\"%s\", target-field-path=\"%s\"", - pathstr, target_field_path_pretty_str); - goto end; - } - - /* Set target field path and target field type */ - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - ret = bt_ctf_field_type_common_sequence_set_length_field_path( - type, target_field_path); - if (ret) { - BT_LOGW("Cannot set sequence field type's length field path: " - "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", - ret, type, pathstr, - target_field_path_pretty_str); - goto end; - } - break; - case BT_CTF_FIELD_TYPE_ID_VARIANT: - ret = bt_ctf_field_type_common_variant_set_tag_field_path( - type, target_field_path); - if (ret) { - BT_LOGW("Cannot set varaint field type's tag field path: " - "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", - ret, type, pathstr, - target_field_path_pretty_str); - goto end; - } - - ret = bt_ctf_field_type_common_variant_set_tag_field_type( - type, target_type); - if (ret) { - BT_LOGW("Cannot set varaint field type's tag field type: " - "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", - ret, type, pathstr, - target_field_path_pretty_str); - goto end; - } - break; - default: - abort(); - } - -end: - if (target_field_path_pretty) { - g_string_free(target_field_path_pretty, TRUE); - } - - BT_CTF_OBJECT_PUT_REF_AND_RESET(target_field_path); - BT_CTF_OBJECT_PUT_REF_AND_RESET(target_type); - return ret; -} - -/* - * Resolves a field type `type`. - * - * `type` is owned by the caller. - */ -static -int resolve_type(struct bt_ctf_field_type_common *type, struct resolve_context *ctx) -{ - int ret = 0; - enum bt_ctf_field_type_id type_id; - - if (!type) { - /* Type is not available; still valid */ - goto end; - } - - type_id = bt_ctf_field_type_common_get_type_id(type); - ctx->cur_field_type = type; - - /* Resolve sequence/variant field type */ - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - case BT_CTF_FIELD_TYPE_ID_VARIANT: - ret = resolve_sequence_or_variant_type(type, ctx); - if (ret) { - BT_LOGW("Cannot resolve sequence field type's length or variant field type's tag: " - "ret=%d, ft-addr=%p", ret, type); - goto end; - } - break; - default: - break; - } - - /* Recurse into compound types */ - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_STRUCT: - case BT_CTF_FIELD_TYPE_ID_VARIANT: - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - case BT_CTF_FIELD_TYPE_ID_ARRAY: - { - int64_t field_count, f_index; - - ret = type_stack_push(ctx->type_stack, type); - if (ret) { - BT_LOGW("Cannot push field type on context's stack: " - "ft-addr=%p", type); - goto end; - } - - field_count = bt_ctf_field_type_common_get_field_count(type); - if (field_count < 0) { - BT_LOGW("Cannot get field type's field count: " - "ret=%" PRId64 ", ft-addr=%p", - field_count, type); - ret = field_count; - goto end; - } - - for (f_index = 0; f_index < field_count; f_index++) { - struct bt_ctf_field_type_common *child_type = - bt_ctf_field_type_common_borrow_field_at_index(type, - f_index); - - if (!child_type) { - BT_LOGW("Cannot get field type's child field: " - "ft-addr=%p, index=%" PRId64 ", " - "count=%" PRId64, type, f_index, - field_count); - ret = -1; - goto end; - } - - if (type_id == BT_CTF_FIELD_TYPE_ID_ARRAY|| - type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE) { - type_stack_peek(ctx->type_stack)->index = -1; - } else { - type_stack_peek(ctx->type_stack)->index = - f_index; - } - - BT_LOGV("Resolving field type's child field type: " - "parent-ft-addr=%p, child-ft-addr=%p, " - "index=%" PRId64 ", count=%" PRId64, - type, child_type, f_index, field_count); - ret = resolve_type(child_type, ctx); - if (ret) { - goto end; - } - } - - type_stack_pop(ctx->type_stack); - break; - } - default: - break; - } - -end: - return ret; -} - -/* - * Resolves the root field type corresponding to the scope `root_scope`. - */ -static -int resolve_root_type(enum bt_ctf_scope root_scope, struct resolve_context *ctx) -{ - int ret; - - BT_ASSERT(type_stack_size(ctx->type_stack) == 0); - ctx->root_scope = root_scope; - ret = resolve_type(get_type_from_ctx(ctx, root_scope), ctx); - ctx->root_scope = BT_CTF_SCOPE_UNKNOWN; - - return ret; -} - -BT_HIDDEN -int bt_ctf_resolve_types( - struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type, - struct bt_ctf_field_type_common *event_context_type, - struct bt_ctf_field_type_common *event_payload_type, - enum bt_ctf_resolve_flag flags) -{ - int ret = 0; - struct resolve_context ctx = { - .environment = environment, - .scopes = { - packet_header_type, - packet_context_type, - event_header_type, - stream_event_ctx_type, - event_context_type, - event_payload_type, - }, - .root_scope = BT_CTF_SCOPE_UNKNOWN, - }; - - BT_LOGV("Resolving field types: " - "packet-header-ft-addr=%p, " - "packet-context-ft-addr=%p, " - "event-header-ft-addr=%p, " - "stream-event-context-ft-addr=%p, " - "event-context-ft-addr=%p, " - "event-payload-ft-addr=%p", - packet_header_type, packet_context_type, event_header_type, - stream_event_ctx_type, event_context_type, event_payload_type); - - /* Initialize type stack */ - ctx.type_stack = type_stack_create(); - if (!ctx.type_stack) { - BT_LOGE_STR("Cannot create field type stack."); - ret = -1; - goto end; - } - - /* Resolve packet header type */ - if (flags & BT_CTF_RESOLVE_FLAG_PACKET_HEADER) { - ret = resolve_root_type(BT_CTF_SCOPE_TRACE_PACKET_HEADER, &ctx); - if (ret) { - BT_LOGW("Cannot resolve trace packet header field type: " - "ret=%d", ret); - goto end; - } - } - - /* Resolve packet context type */ - if (flags & BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT) { - ret = resolve_root_type(BT_CTF_SCOPE_STREAM_PACKET_CONTEXT, &ctx); - if (ret) { - BT_LOGW("Cannot resolve stream packet context field type: " - "ret=%d", ret); - goto end; - } - } - - /* Resolve event header type */ - if (flags & BT_CTF_RESOLVE_FLAG_EVENT_HEADER) { - ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_HEADER, &ctx); - if (ret) { - BT_LOGW("Cannot resolve stream event header field type: " - "ret=%d", ret); - goto end; - } - } - - /* Resolve stream event context type */ - if (flags & BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX) { - ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_CONTEXT, &ctx); - if (ret) { - BT_LOGW("Cannot resolve stream event context field type: " - "ret=%d", ret); - goto end; - } - } - - /* Resolve event context type */ - if (flags & BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT) { - ret = resolve_root_type(BT_CTF_SCOPE_EVENT_CONTEXT, &ctx); - if (ret) { - BT_LOGW("Cannot resolve event context field type: " - "ret=%d", ret); - goto end; - } - } - - /* Resolve event payload type */ - if (flags & BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD) { - ret = resolve_root_type(BT_CTF_SCOPE_EVENT_FIELDS, &ctx); - if (ret) { - BT_LOGW("Cannot resolve event payload field type: " - "ret=%d", ret); - goto end; - } - } - - BT_LOGV_STR("Resolved field types."); - -end: - type_stack_destroy(ctx.type_stack); - - return ret; -} diff --git a/ctf-writer/stream-class.c b/ctf-writer/stream-class.c deleted file mode 100644 index 4b61a7ec..00000000 --- a/ctf-writer/stream-class.c +++ /dev/null @@ -1,1145 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-STREAM-CLASS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class, - const char *name, bt_ctf_object_release_func release_func) -{ - BT_LOGD("Initializing common stream class object: name=\"%s\"", name); - - bt_ctf_object_init_shared_with_parent(&stream_class->base, release_func); - stream_class->name = g_string_new(name); - stream_class->event_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_ctf_object_try_spec_release); - if (!stream_class->event_classes) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - stream_class->event_classes_ht = g_hash_table_new_full(g_int64_hash, - g_int64_equal, g_free, NULL); - if (!stream_class->event_classes_ht) { - BT_LOGE_STR("Failed to allocate a GHashTable."); - goto error; - } - - BT_LOGD("Initialized common stream class object: addr=%p, name=\"%s\"", - stream_class, name); - return 0; - -error: - return -1; -} - -BT_HIDDEN -void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class) -{ - BT_LOGD("Finalizing common stream class: addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - bt_ctf_object_put_ref(stream_class->clock_class); - - if (stream_class->event_classes_ht) { - g_hash_table_destroy(stream_class->event_classes_ht); - } - if (stream_class->event_classes) { - BT_LOGD_STR("Destroying event classes."); - g_ptr_array_free(stream_class->event_classes, TRUE); - } - - if (stream_class->name) { - g_string_free(stream_class->name, TRUE); - } - - BT_LOGD_STR("Putting event header field type."); - bt_ctf_object_put_ref(stream_class->event_header_field_type); - BT_LOGD_STR("Putting packet context field type."); - bt_ctf_object_put_ref(stream_class->packet_context_field_type); - BT_LOGD_STR("Putting event context field type."); - bt_ctf_object_put_ref(stream_class->event_context_field_type); -} - -static -void event_class_exists(gpointer element, gpointer query) -{ - struct bt_ctf_event_class_common *event_class_a = element; - struct bt_ctf_search_query *search_query = query; - struct bt_ctf_event_class_common *event_class_b = search_query->value; - int64_t id_a, id_b; - - if (search_query->value == element) { - search_query->found = 1; - goto end; - } - - /* - * Two event classes cannot share the same ID in a given - * stream class. - */ - id_a = bt_ctf_event_class_common_get_id(event_class_a); - id_b = bt_ctf_event_class_common_get_id(event_class_b); - - if (id_a < 0 || id_b < 0) { - /* at least one ID is not set: will be automatically set later */ - goto end; - } - - if (id_a == id_b) { - BT_LOGW("Event class with this ID already exists in the stream class: " - "id=%" PRId64 ", name=\"%s\"", - id_a, bt_ctf_event_class_common_get_name(event_class_a)); - search_query->found = 1; - goto end; - } - -end: - return; -} - -BT_HIDDEN -int bt_ctf_stream_class_common_add_event_class( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_event_class_common *event_class, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) -{ - int ret = 0; - int64_t *event_id = NULL; - struct bt_ctf_trace_common *trace = NULL; - struct bt_ctf_stream_class_common *old_stream_class = NULL; - struct bt_ctf_validation_output validation_output = { 0 }; - struct bt_ctf_field_type_common *packet_header_type = NULL; - struct bt_ctf_field_type_common *packet_context_type = NULL; - struct bt_ctf_field_type_common *event_header_type = NULL; - struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; - struct bt_ctf_field_type_common *event_context_type = NULL; - struct bt_ctf_field_type_common *event_payload_type = NULL; - const enum bt_ctf_validation_flag validation_flags = - BT_CTF_VALIDATION_FLAG_EVENT; - struct bt_ctf_clock_class *expected_clock_class = NULL; - - BT_ASSERT(copy_field_type_func); - - if (!stream_class || !event_class) { - BT_LOGW("Invalid parameter: stream class or event class is NULL: " - "stream-class-addr=%p, event-class-addr=%p", - stream_class, event_class); - ret = -1; - goto end; - } - - BT_LOGD("Adding event class to stream class: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", event-class-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_class, - bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - trace = bt_ctf_stream_class_common_borrow_trace(stream_class); - - if (stream_class->frozen) { - /* - * We only check that the event class to be added has a - * single class which matches the stream class's - * expected clock class if the stream class is frozen. - * If it's not, then this event class is added "as is" - * and the validation will be performed when calling - * either bt_ctf_trace_add_stream_class() or - * bt_ctf_event_create(). This is because the stream class's - * field types (packet context, event header, event - * context) could change before the next call to one of - * those two functions. - */ - expected_clock_class = bt_ctf_object_get_ref(stream_class->clock_class); - - /* - * At this point, `expected_clock_class` can be NULL, - * and bt_ctf_event_class_validate_single_clock_class() - * below can set it. - */ - ret = bt_ctf_event_class_common_validate_single_clock_class( - event_class, &expected_clock_class); - if (ret) { - BT_LOGW("Event class contains a field type which is not " - "recursively mapped to its stream class's " - "expected clock class: " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "stream-class-name=\"%s\", " - "expected-clock-class-addr=%p, " - "expected-clock-class-name=\"%s\"", - stream_class, - bt_ctf_stream_class_common_get_id(stream_class), - bt_ctf_stream_class_common_get_name(stream_class), - expected_clock_class, - expected_clock_class ? - bt_ctf_clock_class_get_name(expected_clock_class) : - NULL); - goto end; - } - } - - event_id = g_new(int64_t, 1); - if (!event_id) { - BT_LOGE_STR("Failed to allocate one int64_t."); - ret = -1; - goto end; - } - - /* Check for duplicate event classes */ - struct bt_ctf_search_query query = { .value = event_class, .found = 0 }; - g_ptr_array_foreach(stream_class->event_classes, event_class_exists, - &query); - if (query.found) { - BT_LOGW_STR("Another event class part of this stream class has the same ID."); - ret = -1; - goto end; - } - - old_stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); - if (old_stream_class) { - /* Event class is already associated to a stream class. */ - BT_LOGW("Event class is already part of another stream class: " - "event-class-stream-class-addr=%p, " - "event-class-stream-class-name=\"%s\", " - "event-class-stream-class-id=%" PRId64, - old_stream_class, - bt_ctf_stream_class_common_get_name(old_stream_class), - bt_ctf_stream_class_common_get_id(old_stream_class)); - ret = -1; - goto end; - } - - if (trace) { - /* - * If the stream class is associated with a trace, then - * both those objects are frozen. Also, this event class - * is about to be frozen. - * - * Therefore the event class must be validated here. - * The trace and stream class should be valid at this - * point. - */ - BT_ASSERT(trace->valid); - BT_ASSERT(stream_class->valid); - packet_header_type = - bt_ctf_trace_common_borrow_packet_header_field_type(trace); - packet_context_type = - bt_ctf_stream_class_common_borrow_packet_context_field_type( - stream_class); - event_header_type = - bt_ctf_stream_class_common_borrow_event_header_field_type( - stream_class); - stream_event_ctx_type = - bt_ctf_stream_class_common_borrow_event_context_field_type( - stream_class); - event_context_type = - bt_ctf_event_class_common_borrow_context_field_type( - event_class); - event_payload_type = - bt_ctf_event_class_common_borrow_payload_field_type( - event_class); - ret = bt_ctf_validate_class_types( - trace->environment, packet_header_type, - packet_context_type, event_header_type, - stream_event_ctx_type, event_context_type, - event_payload_type, trace->valid, - stream_class->valid, event_class->valid, - &validation_output, validation_flags, - copy_field_type_func); - - if (ret) { - /* - * This means something went wrong during the - * validation process, not that the objects are - * invalid. - */ - BT_LOGE("Failed to validate event class: ret=%d", ret); - goto end; - } - - if ((validation_output.valid_flags & validation_flags) != - validation_flags) { - /* Invalid event class */ - BT_LOGW("Invalid trace, stream class, or event class: " - "valid-flags=0x%x", - validation_output.valid_flags); - ret = -1; - goto end; - } - } - - /* Only set an event ID if none was explicitly set before */ - *event_id = bt_ctf_event_class_common_get_id(event_class); - if (*event_id < 0) { - BT_LOGV("Event class has no ID: automatically setting it: " - "id=%" PRId64, stream_class->next_event_id); - - if (bt_ctf_event_class_common_set_id(event_class, - stream_class->next_event_id)) { - BT_LOGE("Cannot set event class's ID: id=%" PRId64, - stream_class->next_event_id); - ret = -1; - goto end; - } - stream_class->next_event_id++; - *event_id = stream_class->next_event_id; - } - - bt_ctf_object_set_parent(&event_class->base, &stream_class->base); - - if (trace) { - /* - * At this point we know that the function will be - * successful. Therefore we can replace the event - * class's field types with what's in the validation - * output structure and mark this event class as valid. - */ - bt_ctf_validation_replace_types(NULL, NULL, event_class, - &validation_output, validation_flags); - event_class->valid = 1; - - /* - * Put what was not moved in - * bt_ctf_validation_replace_types(). - */ - bt_ctf_validation_output_put_types(&validation_output); - } - - /* Add to the event classes of the stream class */ - g_ptr_array_add(stream_class->event_classes, event_class); - g_hash_table_insert(stream_class->event_classes_ht, event_id, - event_class); - event_id = NULL; - - /* Freeze the event class */ - bt_ctf_event_class_common_freeze(event_class); - - /* - * It is safe to set the stream class's unique clock class - * now if the stream class is frozen. - */ - if (stream_class->frozen && expected_clock_class) { - BT_ASSERT(!stream_class->clock_class || - stream_class->clock_class == expected_clock_class); - BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); - } - - BT_LOGD("Added event class to stream class: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", event-class-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_class, - bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - -end: - bt_ctf_validation_output_put_types(&validation_output); - bt_ctf_object_put_ref(expected_clock_class); - g_free(event_id); - return ret; -} - -static -int64_t get_event_class_count(void *element) -{ - return bt_ctf_stream_class_get_event_class_count( - (struct bt_ctf_stream_class *) element); -} - -static -void *get_event_class(void *element, int i) -{ - return bt_ctf_stream_class_get_event_class_by_index( - (struct bt_ctf_stream_class *) element, i); -} - -static -int visit_event_class(void *object, bt_ctf_visitor visitor,void *data) -{ - struct bt_ctf_visitor_object obj = { - .object = object, - .type = BT_CTF_VISITOR_OBJECT_TYPE_EVENT_CLASS - }; - - return visitor(&obj, data); -} - -BT_HIDDEN -int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class, - bt_ctf_visitor visitor, void *data) -{ - int ret; - struct bt_ctf_visitor_object obj = { - .object = stream_class, - .type = BT_CTF_VISITOR_OBJECT_TYPE_STREAM_CLASS - }; - - if (!stream_class || !visitor) { - BT_LOGW("Invalid parameter: stream class or visitor is NULL: " - "stream-class-addr=%p, visitor=%p", - stream_class, visitor); - ret = -1; - goto end; - } - - ret = bt_ctf_visitor_helper(&obj, get_event_class_count, - get_event_class, - visit_event_class, visitor, data); - BT_LOGV("bt_ctf_visitor_helper() returned: ret=%d", ret); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class, - bt_ctf_visitor visitor, void *data) -{ - return bt_ctf_stream_class_common_visit(BT_CTF_FROM_COMMON(stream_class), - visitor, data); -} - -BT_HIDDEN -void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class) -{ - if (!stream_class || stream_class->frozen) { - return; - } - - BT_LOGD("Freezing stream class: addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - stream_class->frozen = 1; - bt_ctf_field_type_common_freeze(stream_class->event_header_field_type); - bt_ctf_field_type_common_freeze(stream_class->packet_context_field_type); - bt_ctf_field_type_common_freeze(stream_class->event_context_field_type); - bt_ctf_clock_class_freeze(stream_class->clock_class); -} - -BT_HIDDEN -int bt_ctf_stream_class_common_validate_single_clock_class( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_clock_class **expected_clock_class) -{ - int ret; - uint64_t i; - - BT_ASSERT(stream_class); - BT_ASSERT(expected_clock_class); - ret = bt_ctf_field_type_common_validate_single_clock_class( - stream_class->packet_context_field_type, - expected_clock_class); - if (ret) { - BT_LOGW("Stream class's packet context field type " - "is not recursively mapped to the " - "expected clock class: " - "stream-class-addr=%p, " - "stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", " - "ft-addr=%p", - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - stream_class->id, - stream_class->packet_context_field_type); - goto end; - } - - ret = bt_ctf_field_type_common_validate_single_clock_class( - stream_class->event_header_field_type, - expected_clock_class); - if (ret) { - BT_LOGW("Stream class's event header field type " - "is not recursively mapped to the " - "expected clock class: " - "stream-class-addr=%p, " - "stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", " - "ft-addr=%p", - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - stream_class->id, - stream_class->event_header_field_type); - goto end; - } - - ret = bt_ctf_field_type_common_validate_single_clock_class( - stream_class->event_context_field_type, - expected_clock_class); - if (ret) { - BT_LOGW("Stream class's event context field type " - "is not recursively mapped to the " - "expected clock class: " - "stream-class-addr=%p, " - "stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", " - "ft-addr=%p", - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - stream_class->id, - stream_class->event_context_field_type); - goto end; - } - - for (i = 0; i < stream_class->event_classes->len; i++) { - struct bt_ctf_event_class_common *event_class = - g_ptr_array_index(stream_class->event_classes, i); - - BT_ASSERT(event_class); - ret = bt_ctf_event_class_common_validate_single_clock_class( - event_class, expected_clock_class); - if (ret) { - BT_LOGW("Stream class's event class contains a " - "field type which is not recursively mapped to " - "the expected clock class: " - "stream-class-addr=%p, " - "stream-class-name=\"%s\", " - "stream-class-id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - stream_class->id); - goto end; - } - } - -end: - return ret; -} - -static -int init_event_header(struct bt_ctf_stream_class *stream_class) -{ - int ret = 0; - struct bt_ctf_field_type *event_header_type = - bt_ctf_field_type_structure_create(); - struct bt_ctf_field_type *_uint32_t = - get_field_type(FIELD_TYPE_ALIAS_UINT32_T); - struct bt_ctf_field_type *_uint64_t = - get_field_type(FIELD_TYPE_ALIAS_UINT64_T); - - if (!event_header_type) { - BT_LOGE_STR("Cannot create empty structure field type."); - ret = -1; - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(event_header_type, - _uint32_t, "id"); - if (ret) { - BT_LOGE_STR("Cannot add `id` field to event header field type."); - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(event_header_type, - _uint64_t, "timestamp"); - if (ret) { - BT_LOGE_STR("Cannot add `timestamp` field to event header field type."); - goto end; - } - - bt_ctf_object_put_ref(stream_class->common.event_header_field_type); - stream_class->common.event_header_field_type = - (void *) event_header_type; - event_header_type = NULL; - -end: - if (ret) { - bt_ctf_object_put_ref(event_header_type); - } - - bt_ctf_object_put_ref(_uint32_t); - bt_ctf_object_put_ref(_uint64_t); - return ret; -} - -static -int init_packet_context(struct bt_ctf_stream_class *stream_class) -{ - int ret = 0; - struct bt_ctf_field_type *packet_context_type = - bt_ctf_field_type_structure_create(); - struct bt_ctf_field_type *_uint64_t = - get_field_type(FIELD_TYPE_ALIAS_UINT64_T); - struct bt_ctf_field_type *ts_begin_end_uint64_t; - - if (!packet_context_type) { - BT_LOGE_STR("Cannot create empty structure field type."); - ret = -1; - goto end; - } - - ts_begin_end_uint64_t = bt_ctf_field_type_copy(_uint64_t); - if (!ts_begin_end_uint64_t) { - BT_LOGE_STR("Cannot copy integer field type for `timestamp_begin` and `timestamp_end` fields."); - ret = -1; - goto end; - } - - /* - * We create a stream packet context as proposed in the CTF - * specification. - */ - ret = bt_ctf_field_type_structure_add_field(packet_context_type, - ts_begin_end_uint64_t, "timestamp_begin"); - if (ret) { - BT_LOGE_STR("Cannot add `timestamp_begin` field to event header field type."); - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(packet_context_type, - ts_begin_end_uint64_t, "timestamp_end"); - if (ret) { - BT_LOGE_STR("Cannot add `timestamp_end` field to event header field type."); - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(packet_context_type, - _uint64_t, "content_size"); - if (ret) { - BT_LOGE_STR("Cannot add `content_size` field to event header field type."); - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(packet_context_type, - _uint64_t, "packet_size"); - if (ret) { - BT_LOGE_STR("Cannot add `packet_size` field to event header field type."); - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(packet_context_type, - _uint64_t, "events_discarded"); - if (ret) { - BT_LOGE_STR("Cannot add `events_discarded` field to event header field type."); - goto end; - } - - bt_ctf_object_put_ref(stream_class->common.packet_context_field_type); - stream_class->common.packet_context_field_type = - (void *) packet_context_type; - packet_context_type = NULL; - -end: - if (ret) { - bt_ctf_object_put_ref(packet_context_type); - goto end; - } - - bt_ctf_object_put_ref(_uint64_t); - bt_ctf_object_put_ref(ts_begin_end_uint64_t); - return ret; -} - -static -void bt_ctf_stream_class_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_stream_class *stream_class; - - stream_class = (void *) obj; - BT_LOGD("Destroying CTF writer stream class: addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class)); - bt_ctf_stream_class_common_finalize(BT_CTF_TO_COMMON(stream_class)); - bt_ctf_object_put_ref(stream_class->clock); - g_free(stream_class); -} - -struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name) -{ - struct bt_ctf_stream_class *stream_class; - int ret; - - BT_LOGD("Creating CTF writer stream class object: name=\"%s\"", name); - stream_class = g_new0(struct bt_ctf_stream_class, 1); - if (!stream_class) { - BT_LOGE_STR("Failed to allocate one CTF writer stream class."); - goto error; - } - - ret = bt_ctf_stream_class_common_initialize(BT_CTF_TO_COMMON(stream_class), - name, bt_ctf_stream_class_destroy); - if (ret) { - /* bt_ctf_stream_class_common_initialize() logs errors */ - goto error; - } - - ret = init_event_header(stream_class); - if (ret) { - BT_LOGE_STR("Cannot initialize stream class's event header field type."); - goto error; - } - - ret = init_packet_context(stream_class); - if (ret) { - BT_LOGE_STR("Cannot initialize stream class's packet context field type."); - goto error; - } - - BT_LOGD("Created CTF writer stream class object: addr=%p, name=\"%s\"", - stream_class, name); - return stream_class; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_class); - return stream_class; -} - -static -int try_map_clock_class(struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *parent_ft, const char *field_name) -{ - struct bt_ctf_clock_class *mapped_clock_class = NULL; - int ret = 0; - struct bt_ctf_field_type *ft = - bt_ctf_field_type_structure_get_field_type_by_name(parent_ft, - field_name); - - BT_ASSERT(stream_class->clock); - - if (!ft) { - /* Field does not exist: not an error */ - goto end; - } - - BT_ASSERT(((struct bt_ctf_field_type_common *) ft)->id == - BT_CTF_FIELD_TYPE_ID_INTEGER); - mapped_clock_class = - bt_ctf_field_type_integer_get_mapped_clock_class(ft); - if (!mapped_clock_class) { - struct bt_ctf_field_type *ft_copy; - - if (!stream_class->clock) { - BT_LOGW("Cannot automatically set field's type mapped clock class: stream class's clock is not set: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", ft-addr=%p", - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class), ft); - ret = -1; - goto end; - } - - ft_copy = bt_ctf_field_type_copy(ft); - if (!ft_copy) { - BT_LOGE("Failed to copy integer field type: ft-addr=%p", - ft); - } - - ret = bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( - (void *) ft_copy, stream_class->clock->clock_class); - BT_ASSERT(ret == 0); - - ret = bt_ctf_field_type_common_structure_replace_field( - (void *) parent_ft, field_name, (void *) ft_copy); - bt_ctf_object_put_ref(ft_copy); - BT_LOGV("Automatically mapped field type to stream class's clock class: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", ft-addr=%p, " - "ft-copy-addr=%p", - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class), ft, ft_copy); - } - -end: - bt_ctf_object_put_ref(ft); - bt_ctf_object_put_ref(mapped_clock_class); - return ret; -} - -BT_HIDDEN -int bt_ctf_stream_class_map_clock_class( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *packet_context_type, - struct bt_ctf_field_type *event_header_type) -{ - int ret = 0; - - BT_ASSERT(stream_class); - - if (!stream_class->clock) { - /* No clock class to map to */ - goto end; - } - - if (packet_context_type) { - if (try_map_clock_class(stream_class, packet_context_type, - "timestamp_begin")) { - BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_begin` field's mapped clock class."); - ret = -1; - goto end; - } - - if (try_map_clock_class(stream_class, packet_context_type, - "timestamp_end")) { - BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_end` field's mapped clock class."); - ret = -1; - goto end; - } - } - - if (event_header_type) { - if (try_map_clock_class(stream_class, event_header_type, - "timestamp")) { - BT_LOGE_STR("Cannot automatically set stream class's event header field type's `timestamp` field's mapped clock class."); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -struct bt_ctf_clock *bt_ctf_stream_class_get_clock( - struct bt_ctf_stream_class *stream_class) -{ - struct bt_ctf_clock *clock = NULL; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - goto end; - } - - if (!stream_class->clock) { - BT_LOGV("Stream class has no clock: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class)); - goto end; - } - - clock = bt_ctf_object_get_ref(stream_class->clock); - -end: - return clock; -} - -int bt_ctf_stream_class_set_clock( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_clock *clock) -{ - int ret = 0; - - if (!stream_class || !clock) { - BT_LOGW("Invalid parameter: stream class or clock is NULL: " - "stream-class-addr=%p, clock-addr=%p", - stream_class, clock); - ret = -1; - goto end; - } - - if (stream_class->common.frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class)); - ret = -1; - goto end; - } - - /* Replace the current clock of this stream class. */ - bt_ctf_object_put_ref(stream_class->clock); - stream_class->clock = bt_ctf_object_get_ref(clock); - BT_LOGV("Set stream class's clock: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "clock-addr=%p, clock-name=\"%s\"", - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class), - stream_class->clock, - bt_ctf_clock_get_name(stream_class->clock)); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class, - struct metadata_context *context) -{ - int ret = 0; - size_t i; - struct bt_ctf_trace *trace; - struct bt_ctf_field_type *packet_header_type = NULL; - - BT_LOGD("Serializing stream class's metadata: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", metadata-context-addr=%p", - stream_class, - bt_ctf_stream_class_get_name(stream_class), - bt_ctf_stream_class_get_id(stream_class), context); - g_string_assign(context->field_name, ""); - context->current_indentation_level = 1; - if (!stream_class->common.id_set) { - BT_LOGW_STR("Stream class's ID is not set."); - ret = -1; - goto end; - } - - g_string_append(context->string, "stream {\n"); - - /* - * The reference to the trace is only borrowed since the - * serialization of the stream class might have been triggered - * by the trace's destruction. In such a case, the trace's - * reference count would, unexepectedly, go through the sequence - * 1 -> 0 -> 1 -> 0 -> ..., provoking an endless loop of destruction - * and serialization. - */ - trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( - BT_CTF_TO_COMMON(stream_class))); - BT_ASSERT(trace); - packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace); - trace = NULL; - if (packet_header_type) { - struct bt_ctf_field_type *stream_id_type; - - stream_id_type = - bt_ctf_field_type_structure_get_field_type_by_name( - packet_header_type, "stream_id"); - if (stream_id_type) { - /* - * Only set the stream's id if the trace's packet header - * contains a stream_id field. This field is only - * needed if the trace contains only one stream - * class. - */ - g_string_append_printf(context->string, - "\tid = %" PRId64 ";\n", - stream_class->common.id); - } - bt_ctf_object_put_ref(stream_id_type); - } - if (stream_class->common.event_header_field_type) { - BT_LOGD_STR("Serializing stream class's event header field type's metadata."); - g_string_append(context->string, "\tevent.header := "); - ret = bt_ctf_field_type_serialize_recursive( - (void *) stream_class->common.event_header_field_type, - context); - if (ret) { - BT_LOGW("Cannot serialize stream class's event header field type's metadata: " - "ret=%d", ret); - goto end; - } - g_string_append(context->string, ";"); - } - - - if (stream_class->common.packet_context_field_type) { - BT_LOGD_STR("Serializing stream class's packet context field type's metadata."); - g_string_append(context->string, "\n\n\tpacket.context := "); - ret = bt_ctf_field_type_serialize_recursive( - (void *) stream_class->common.packet_context_field_type, - context); - if (ret) { - BT_LOGW("Cannot serialize stream class's packet context field type's metadata: " - "ret=%d", ret); - goto end; - } - g_string_append(context->string, ";"); - } - - if (stream_class->common.event_context_field_type) { - BT_LOGD_STR("Serializing stream class's event context field type's metadata."); - g_string_append(context->string, "\n\n\tevent.context := "); - ret = bt_ctf_field_type_serialize_recursive( - (void *) stream_class->common.event_context_field_type, - context); - if (ret) { - BT_LOGW("Cannot serialize stream class's event context field type's metadata: " - "ret=%d", ret); - goto end; - } - g_string_append(context->string, ";"); - } - - g_string_append(context->string, "\n};\n\n"); - - for (i = 0; i < stream_class->common.event_classes->len; i++) { - struct bt_ctf_event_class *event_class = - stream_class->common.event_classes->pdata[i]; - - ret = bt_ctf_event_class_serialize(event_class, context); - if (ret) { - BT_LOGW("Cannot serialize event class's metadata: " - "event-class-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64, - event_class, - bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class)); - goto end; - } - } - -end: - bt_ctf_object_put_ref(packet_header_type); - context->current_indentation_level = 0; - return ret; -} - -struct bt_ctf_trace *bt_ctf_stream_class_get_trace( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_object_get_ref(bt_ctf_stream_class_common_borrow_trace( - BT_CTF_TO_COMMON(stream_class))); -} - -const char *bt_ctf_stream_class_get_name( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_stream_class_common_get_name(BT_CTF_TO_COMMON(stream_class)); -} - -int bt_ctf_stream_class_set_name( - struct bt_ctf_stream_class *stream_class, const char *name) -{ - return bt_ctf_stream_class_common_set_name(BT_CTF_TO_COMMON(stream_class), - name); -} - -int64_t bt_ctf_stream_class_get_id( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_stream_class_common_get_id(BT_CTF_TO_COMMON(stream_class)); -} - -int bt_ctf_stream_class_set_id( - struct bt_ctf_stream_class *stream_class, uint64_t id) -{ - return bt_ctf_stream_class_common_set_id(BT_CTF_TO_COMMON(stream_class), id); -} - -struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_object_get_ref( - bt_ctf_stream_class_common_borrow_packet_context_field_type( - BT_CTF_TO_COMMON(stream_class))); -} - -int bt_ctf_stream_class_set_packet_context_type( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *packet_context_type) -{ - return bt_ctf_stream_class_common_set_packet_context_field_type( - BT_CTF_TO_COMMON(stream_class), (void *) packet_context_type); -} - -struct bt_ctf_field_type * -bt_ctf_stream_class_get_event_header_type( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_object_get_ref( - bt_ctf_stream_class_common_borrow_event_header_field_type( - BT_CTF_TO_COMMON(stream_class))); -} - -int bt_ctf_stream_class_set_event_header_type( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *event_header_type) -{ - return bt_ctf_stream_class_common_set_event_header_field_type( - BT_CTF_TO_COMMON(stream_class), (void *) event_header_type); -} - -struct bt_ctf_field_type * -bt_ctf_stream_class_get_event_context_type( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_object_get_ref( - bt_ctf_stream_class_common_borrow_event_context_field_type( - BT_CTF_TO_COMMON(stream_class))); -} - -int bt_ctf_stream_class_set_event_context_type( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *event_context_type) -{ - return bt_ctf_stream_class_common_set_event_context_field_type( - BT_CTF_TO_COMMON(stream_class), (void *) event_context_type); -} - -int64_t bt_ctf_stream_class_get_event_class_count( - struct bt_ctf_stream_class *stream_class) -{ - return bt_ctf_stream_class_common_get_event_class_count( - BT_CTF_TO_COMMON(stream_class)); -} - -struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index( - struct bt_ctf_stream_class *stream_class, uint64_t index) -{ - return bt_ctf_object_get_ref( - bt_ctf_stream_class_common_borrow_event_class_by_index( - BT_CTF_TO_COMMON(stream_class), index)); -} - -struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id( - struct bt_ctf_stream_class *stream_class, uint64_t id) -{ - return bt_ctf_object_get_ref( - bt_ctf_stream_class_common_borrow_event_class_by_id( - BT_CTF_TO_COMMON(stream_class), id)); -} - -int bt_ctf_stream_class_add_event_class( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_event_class *event_class) -{ - return bt_ctf_stream_class_common_add_event_class( - BT_CTF_TO_COMMON(stream_class), BT_CTF_TO_COMMON(event_class), - (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy); -} diff --git a/ctf-writer/stream.c b/ctf-writer/stream.c deleted file mode 100644 index fc7557ee..00000000 --- a/ctf-writer/stream.c +++ /dev/null @@ -1,1953 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-STREAM" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream) -{ - BT_LOGD("Finalizing common stream object: addr=%p, name=\"%s\"", - stream, bt_ctf_stream_common_get_name(stream)); - - if (stream->name) { - g_string_free(stream->name, TRUE); - } -} - -BT_HIDDEN -int bt_ctf_stream_common_initialize( - struct bt_ctf_stream_common *stream, - struct bt_ctf_stream_class_common *stream_class, const char *name, - uint64_t id, bt_ctf_object_release_func release_func) -{ - int ret = 0; - struct bt_ctf_trace_common *trace = NULL; - - bt_ctf_object_init_shared_with_parent(&stream->base, release_func); - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - goto error; - } - - BT_LOGD("Initializing common stream object: stream-class-addr=%p, " - "stream-class-name=\"%s\", stream-name=\"%s\", " - "stream-id=%" PRIu64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - name, id); - trace = bt_ctf_stream_class_common_borrow_trace(stream_class); - if (!trace) { - BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-name=\"%s\"", - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), name); - goto error; - } - - if (id != -1ULL) { - /* - * Validate that the given ID is unique amongst all the - * existing trace's streams created from the same stream - * class. - */ - size_t i; - - for (i = 0; i < trace->streams->len; i++) { - struct bt_ctf_stream_common *trace_stream = - g_ptr_array_index(trace->streams, i); - - if (trace_stream->stream_class != (void *) stream_class) { - continue; - } - - if (trace_stream->id == id) { - BT_LOGW_STR("Invalid parameter: another stream in the same trace already has this ID."); - goto error; - } - } - } - - /* - * Acquire reference to parent since stream will become publicly - * reachable; it needs its parent to remain valid. - */ - bt_ctf_object_set_parent(&stream->base, &trace->base); - stream->stream_class = stream_class; - stream->id = (int64_t) id; - - if (name) { - stream->name = g_string_new(name); - if (!stream->name) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - } - - BT_LOGD("Set common stream's trace parent: trace-addr=%p", trace); - - /* Add this stream to the trace's streams */ - BT_LOGD("Created common stream object: addr=%p", stream); - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -void bt_ctf_stream_destroy(struct bt_ctf_object *obj); - -static -int try_set_structure_field_integer(struct bt_ctf_field *, char *, uint64_t); - -static -int set_integer_field_value(struct bt_ctf_field* field, uint64_t value) -{ - int ret = 0; - struct bt_ctf_field_type *field_type = NULL; - - if (!field) { - BT_LOGW_STR("Invalid parameter: field is NULL."); - ret = -1; - goto end; - } - - field_type = bt_ctf_field_get_type(field); - BT_ASSERT(field_type); - - if (bt_ctf_field_type_get_type_id(field_type) != - BT_CTF_FIELD_TYPE_ID_INTEGER) { - /* Not an integer and the value is unset, error. */ - BT_LOGW("Invalid parameter: field's type is not an integer field type: " - "field-addr=%p, ft-addr=%p, ft-id=%s", - field, field_type, - bt_ctf_field_type_id_string((int) - bt_ctf_field_type_get_type_id(field_type))); - ret = -1; - goto end; - } - - if (bt_ctf_field_type_integer_is_signed(field_type)) { - ret = bt_ctf_field_integer_signed_set_value(field, (int64_t) value); - if (ret) { - /* Value is out of range, error. */ - BT_LOGW("Cannot set signed integer field's value: " - "addr=%p, value=%" PRId64, - field, (int64_t) value); - goto end; - } - } else { - ret = bt_ctf_field_integer_unsigned_set_value(field, value); - if (ret) { - /* Value is out of range, error. */ - BT_LOGW("Cannot set unsigned integer field's value: " - "addr=%p, value=%" PRIu64, - field, value); - goto end; - } - } -end: - bt_ctf_object_put_ref(field_type); - return ret; -} - -static -int set_packet_header_magic(struct bt_ctf_stream *stream) -{ - int ret = 0; - struct bt_ctf_field *magic_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_header, "magic"); - const uint32_t magic_value = 0xc1fc1fc1; - - BT_ASSERT(stream); - - if (!magic_field) { - /* No magic field found. Not an error, skip. */ - BT_LOGV("No field named `magic` in packet header: skipping: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - ret = bt_ctf_field_integer_unsigned_set_value(magic_field, - (uint64_t) magic_value); - - if (ret) { - BT_LOGW("Cannot set packet header field's `magic` integer field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - magic_field, (uint64_t) magic_value); - } else { - BT_LOGV("Set packet header field's `magic` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - magic_field, (uint64_t) magic_value); - } -end: - bt_ctf_object_put_ref(magic_field); - return ret; -} - -static -int set_packet_header_uuid(struct bt_ctf_stream *stream) -{ - int ret = 0; - int64_t i; - struct bt_ctf_trace *trace = NULL; - struct bt_ctf_field *uuid_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_header, "uuid"); - - BT_ASSERT(stream); - - if (!uuid_field) { - /* No uuid field found. Not an error, skip. */ - BT_LOGV("No field named `uuid` in packet header: skipping: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - trace = (struct bt_ctf_trace *) - bt_ctf_object_get_parent(&stream->common.base); - - for (i = 0; i < 16; i++) { - struct bt_ctf_field *uuid_element = - bt_ctf_field_array_get_field(uuid_field, i); - - ret = bt_ctf_field_integer_unsigned_set_value( - uuid_element, (uint64_t) trace->common.uuid[i]); - bt_ctf_object_put_ref(uuid_element); - if (ret) { - BT_LOGW("Cannot set integer field's value (for `uuid` packet header field): " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, " - "value=%" PRIu64 ", index=%" PRId64, - stream, bt_ctf_stream_get_name(stream), - uuid_element, (uint64_t) trace->common.uuid[i], i); - goto end; - } - } - - BT_LOGV("Set packet header field's `uuid` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p", - stream, bt_ctf_stream_get_name(stream), uuid_field); - -end: - bt_ctf_object_put_ref(uuid_field); - BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); - return ret; -} -static -int set_packet_header_stream_id(struct bt_ctf_stream *stream) -{ - int ret = 0; - uint32_t stream_id; - struct bt_ctf_field *stream_id_field = - bt_ctf_field_structure_get_field_by_name( - stream->packet_header, "stream_id"); - - if (!stream_id_field) { - /* No stream_id field found. Not an error, skip. */ - BT_LOGV("No field named `stream_id` in packet header: skipping: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - stream_id = stream->common.stream_class->id; - ret = bt_ctf_field_integer_unsigned_set_value(stream_id_field, - (uint64_t) stream_id); - if (ret) { - BT_LOGW("Cannot set packet header field's `stream_id` integer field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - stream_id_field, (uint64_t) stream_id); - } else { - BT_LOGV("Set packet header field's `stream_id` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - stream_id_field, (uint64_t) stream_id); - } - -end: - bt_ctf_object_put_ref(stream_id_field); - return ret; -} - -static -int auto_populate_packet_header(struct bt_ctf_stream *stream) -{ - int ret = 0; - - if (!stream->packet_header) { - goto end; - } - - ret = set_packet_header_magic(stream); - if (ret) { - BT_LOGW("Cannot set packet header's magic number field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - ret = set_packet_header_uuid(stream); - if (ret) { - BT_LOGW("Cannot set packet header's UUID field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - ret = set_packet_header_stream_id(stream); - if (ret) { - BT_LOGW("Cannot set packet header's stream class ID field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - BT_LOGV("Automatically populated stream's packet header's known fields: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - -end: - return ret; -} - -static -int set_packet_context_packet_size(struct bt_ctf_stream *stream, - uint64_t packet_size_bits) -{ - int ret = 0; - struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "packet_size"); - - ret = bt_ctf_field_integer_unsigned_set_value(field, packet_size_bits); - if (ret) { - BT_LOGW("Cannot set packet context field's `packet_size` integer field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, packet_size_bits); - } else { - BT_LOGV("Set packet context field's `packet_size` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, packet_size_bits); - } - - bt_ctf_object_put_ref(field); - return ret; -} - -static -int set_packet_context_content_size(struct bt_ctf_stream *stream, - uint64_t content_size_bits) -{ - int ret = 0; - struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "content_size"); - - BT_ASSERT(stream); - - if (!field) { - /* No content size field found. Not an error, skip. */ - BT_LOGV("No field named `content_size` in packet context: skipping: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - ret = bt_ctf_field_integer_unsigned_set_value(field, content_size_bits); - if (ret) { - BT_LOGW("Cannot set packet context field's `content_size` integer field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, content_size_bits); - } else { - BT_LOGV("Set packet context field's `content_size` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, content_size_bits); - } - -end: - bt_ctf_object_put_ref(field); - return ret; -} - -static -int set_packet_context_events_discarded(struct bt_ctf_stream *stream) -{ - int ret = 0; - struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "events_discarded"); - - BT_ASSERT(stream); - - if (!field) { - /* No discarded events count field found. Not an error, skip. */ - BT_LOGV("No field named `events_discarded` in packet context: skipping: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - /* - * If the field is set by the user, make sure that the value is - * greater than or equal to the stream's current count of - * discarded events. We do not allow wrapping here. If it's - * valid, update the stream's current count. - */ - if (bt_ctf_field_is_set_recursive(field)) { - uint64_t user_val; - - ret = bt_ctf_field_integer_unsigned_get_value(field, - &user_val); - if (ret) { - BT_LOGW("Cannot get packet context `events_discarded` field's unsigned value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p", - stream, bt_ctf_stream_get_name(stream), field); - goto end; - } - - if (user_val < stream->discarded_events) { - BT_LOGW("Invalid packet context `events_discarded` field's unsigned value: " - "value is lesser than the stream's current discarded events count: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, " - "value=%" PRIu64 ", " - "stream-discarded-events-count=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), field, - user_val, stream->discarded_events); - goto end; - } - - stream->discarded_events = user_val; - } else { - ret = bt_ctf_field_integer_unsigned_set_value(field, - stream->discarded_events); - if (ret) { - BT_LOGW("Cannot set packet context field's `events_discarded` integer field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, stream->discarded_events); - } else { - BT_LOGV("Set packet context field's `events_discarded` field's value: " - "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - field, stream->discarded_events); - } - } - -end: - bt_ctf_object_put_ref(field); - return ret; -} - -static -void update_clock_value(uint64_t *val, uint64_t new_val, - unsigned int new_val_size) -{ - const uint64_t pow2 = 1ULL << new_val_size; - const uint64_t mask = pow2 - 1; - uint64_t val_masked; - -#ifdef BT_LOG_ENABLED_VERBOSE - uint64_t old_val = *val; -#endif - - if (new_val_size == 64) { - *val = new_val; - goto end; - } - - val_masked = *val & mask; - - if (new_val < val_masked) { - /* Wrapped once */ - new_val |= pow2; - } - - *val &= ~mask; - *val |= new_val; - -end: - BT_LOGV("Updated clock value: old-val=%" PRIu64 ", new-val=%" PRIu64, - old_val, *val); - return; -} - -static -int visit_field_update_clock_value(struct bt_ctf_field *field, uint64_t *val) -{ - int ret = 0; - struct bt_ctf_field_common *field_common = (void *) field; - - if (!field) { - goto end; - } - - switch (bt_ctf_field_get_type_id(field)) { - case BT_CTF_FIELD_TYPE_ID_INTEGER: - { - struct bt_ctf_clock_class *cc = - bt_ctf_field_type_integer_get_mapped_clock_class( - (void *) field_common->type); - int val_size; - uint64_t uval; - - if (!cc) { - goto end; - } - - bt_ctf_object_put_ref(cc); - val_size = bt_ctf_field_type_integer_get_size( - (void *) field_common->type); - BT_ASSERT(val_size >= 1); - - if (bt_ctf_field_type_integer_is_signed( - (void *) field_common->type)) { - int64_t ival; - - ret = bt_ctf_field_integer_signed_get_value(field, &ival); - uval = (uint64_t) ival; - } else { - ret = bt_ctf_field_integer_unsigned_get_value(field, &uval); - } - - if (ret) { - /* Not set */ - goto end; - } - - update_clock_value(val, uval, val_size); - break; - } - case BT_CTF_FIELD_TYPE_ID_ENUM: - { - struct bt_ctf_field *int_field = - bt_ctf_field_enumeration_get_container(field); - - BT_ASSERT(int_field); - ret = visit_field_update_clock_value(int_field, val); - bt_ctf_object_put_ref(int_field); - break; - } - case BT_CTF_FIELD_TYPE_ID_ARRAY: - { - uint64_t i; - int64_t len = bt_ctf_field_type_array_get_length( - (void *) field_common->type); - - BT_ASSERT(len >= 0); - - for (i = 0; i < len; i++) { - struct bt_ctf_field *elem_field = - bt_ctf_field_array_get_field(field, i); - - BT_ASSERT(elem_field); - ret = visit_field_update_clock_value(elem_field, val); - bt_ctf_object_put_ref(elem_field); - if (ret) { - goto end; - } - } - break; - } - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - { - uint64_t i; - int64_t len = bt_ctf_field_common_sequence_get_length( - (void *) field); - - if (len < 0) { - ret = -1; - goto end; - } - - for (i = 0; i < len; i++) { - struct bt_ctf_field *elem_field = - bt_ctf_field_sequence_get_field(field, i); - - BT_ASSERT(elem_field); - ret = visit_field_update_clock_value(elem_field, val); - bt_ctf_object_put_ref(elem_field); - if (ret) { - goto end; - } - } - break; - } - case BT_CTF_FIELD_TYPE_ID_STRUCT: - { - uint64_t i; - int64_t len = bt_ctf_field_type_structure_get_field_count( - (void *) field_common->type); - - BT_ASSERT(len >= 0); - - for (i = 0; i < len; i++) { - struct bt_ctf_field *member_field = - bt_ctf_field_structure_get_field_by_index(field, i); - - BT_ASSERT(member_field); - ret = visit_field_update_clock_value(member_field, val); - bt_ctf_object_put_ref(member_field); - if (ret) { - goto end; - } - } - break; - } - case BT_CTF_FIELD_TYPE_ID_VARIANT: - { - struct bt_ctf_field *cur_field = - bt_ctf_field_variant_get_current_field(field); - - if (!cur_field) { - ret = -1; - goto end; - } - - ret = visit_field_update_clock_value(cur_field, val); - bt_ctf_object_put_ref(cur_field); - break; - } - default: - break; - } - -end: - return ret; -} - -int visit_event_update_clock_value(struct bt_ctf_event *event, uint64_t *val) -{ - int ret = 0; - struct bt_ctf_field *field; - - field = bt_ctf_event_get_header(event); - ret = visit_field_update_clock_value(field, val); - bt_ctf_object_put_ref(field); - if (ret) { - BT_LOGW_STR("Cannot automatically update clock value in " - "event's header."); - goto end; - } - - field = bt_ctf_event_get_stream_event_context(event); - ret = visit_field_update_clock_value(field, val); - bt_ctf_object_put_ref(field); - if (ret) { - BT_LOGW_STR("Cannot automatically update clock value in " - "event's stream event context."); - goto end; - } - - field = bt_ctf_event_get_context(event); - ret = visit_field_update_clock_value(field, val); - bt_ctf_object_put_ref(field); - if (ret) { - BT_LOGW_STR("Cannot automatically update clock value in " - "event's context."); - goto end; - } - - field = bt_ctf_event_get_payload_field(event); - ret = visit_field_update_clock_value(field, val); - bt_ctf_object_put_ref(field); - if (ret) { - BT_LOGW_STR("Cannot automatically update clock value in " - "event's payload."); - goto end; - } - -end: - return ret; -} - -static -int set_packet_context_timestamps(struct bt_ctf_stream *stream) -{ - int ret = 0; - uint64_t val; - uint64_t cur_clock_value; - uint64_t init_clock_value = 0; - struct bt_ctf_field *ts_begin_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "timestamp_begin"); - struct bt_ctf_field *ts_end_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "timestamp_end"); - struct bt_ctf_field_common *packet_context = - (void *) stream->packet_context; - uint64_t i; - int64_t len; - - if (ts_begin_field && bt_ctf_field_is_set_recursive(ts_begin_field)) { - /* Use provided `timestamp_begin` value as starting value */ - ret = bt_ctf_field_integer_unsigned_get_value(ts_begin_field, &val); - BT_ASSERT(ret == 0); - init_clock_value = val; - } else if (stream->last_ts_end != -1ULL) { - /* Use last packet's ending timestamp as starting value */ - init_clock_value = stream->last_ts_end; - } - - cur_clock_value = init_clock_value; - - if (stream->last_ts_end != -1ULL && - cur_clock_value < stream->last_ts_end) { - BT_LOGW("Packet's initial timestamp is less than previous " - "packet's final timestamp: " - "stream-addr=%p, stream-name=\"%s\", " - "cur-packet-ts-begin=%" PRIu64 ", " - "prev-packet-ts-end=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - cur_clock_value, stream->last_ts_end); - ret = -1; - goto end; - } - - /* - * Visit all the packet context fields, followed by all the - * fields of all the events, in order, updating our current - * clock value as we visit. - * - * While visiting the packet context fields, do not consider - * `timestamp_begin` and `timestamp_end` because this function's - * purpose is to set them anyway. Also do not consider - * `packet_size`, `content_size`, `events_discarded`, and - * `packet_seq_num` if they are not set because those are - * autopopulating fields. - */ - len = bt_ctf_field_type_structure_get_field_count( - (void *) packet_context->type); - BT_ASSERT(len >= 0); - - for (i = 0; i < len; i++) { - const char *member_name; - struct bt_ctf_field *member_field; - - ret = bt_ctf_field_type_structure_get_field_by_index( - (void *) packet_context->type, &member_name, NULL, i); - BT_ASSERT(ret == 0); - - if (strcmp(member_name, "timestamp_begin") == 0 || - strcmp(member_name, "timestamp_end") == 0) { - continue; - } - - member_field = bt_ctf_field_structure_get_field_by_index( - stream->packet_context, i); - BT_ASSERT(member_field); - - if (strcmp(member_name, "packet_size") == 0 && - !bt_ctf_field_is_set_recursive(member_field)) { - bt_ctf_object_put_ref(member_field); - continue; - } - - if (strcmp(member_name, "content_size") == 0 && - !bt_ctf_field_is_set_recursive(member_field)) { - bt_ctf_object_put_ref(member_field); - continue; - } - - if (strcmp(member_name, "events_discarded") == 0 && - !bt_ctf_field_is_set_recursive(member_field)) { - bt_ctf_object_put_ref(member_field); - continue; - } - - if (strcmp(member_name, "packet_seq_num") == 0 && - !bt_ctf_field_is_set_recursive(member_field)) { - bt_ctf_object_put_ref(member_field); - continue; - } - - ret = visit_field_update_clock_value(member_field, - &cur_clock_value); - bt_ctf_object_put_ref(member_field); - if (ret) { - BT_LOGW("Cannot automatically update clock value " - "in stream's packet context: " - "stream-addr=%p, stream-name=\"%s\", " - "field-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream), - member_name); - goto end; - } - } - - for (i = 0; i < stream->events->len; i++) { - struct bt_ctf_event *event = g_ptr_array_index(stream->events, i); - - BT_ASSERT(event); - ret = visit_event_update_clock_value(event, &cur_clock_value); - if (ret) { - BT_LOGW("Cannot automatically update clock value " - "in stream's packet context: " - "stream-addr=%p, stream-name=\"%s\", " - "index=%" PRIu64 ", event-addr=%p, " - "event-class-id=%" PRId64 ", " - "event-class-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream), - i, event, - bt_ctf_event_class_common_get_id(event->common.class), - bt_ctf_event_class_common_get_name(event->common.class)); - goto end; - } - } - - /* - * Everything is visited, thus the current clock value - * corresponds to the ending timestamp. Validate this value - * against the provided value of `timestamp_end`, if any, - * otherwise set it. - */ - if (ts_end_field && bt_ctf_field_is_set_recursive(ts_end_field)) { - ret = bt_ctf_field_integer_unsigned_get_value(ts_end_field, &val); - BT_ASSERT(ret == 0); - - if (val < cur_clock_value) { - BT_LOGW("Packet's final timestamp is less than " - "computed packet's final timestamp: " - "stream-addr=%p, stream-name=\"%s\", " - "cur-packet-ts-end=%" PRIu64 ", " - "computed-packet-ts-end=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), - val, cur_clock_value); - ret = -1; - goto end; - } - - stream->last_ts_end = val; - } - - if (ts_end_field && !bt_ctf_field_is_set_recursive(ts_end_field)) { - ret = set_integer_field_value(ts_end_field, cur_clock_value); - BT_ASSERT(ret == 0); - stream->last_ts_end = cur_clock_value; - } - - if (!ts_end_field) { - stream->last_ts_end = cur_clock_value; - } - - /* Set `timestamp_begin` field to initial clock value */ - if (ts_begin_field && !bt_ctf_field_is_set_recursive(ts_begin_field)) { - ret = set_integer_field_value(ts_begin_field, init_clock_value); - BT_ASSERT(ret == 0); - } - -end: - bt_ctf_object_put_ref(ts_begin_field); - bt_ctf_object_put_ref(ts_end_field); - return ret; -} - -static -int auto_populate_packet_context(struct bt_ctf_stream *stream, bool set_ts, - uint64_t packet_size_bits, uint64_t content_size_bits) -{ - int ret = 0; - - if (!stream->packet_context) { - goto end; - } - - ret = set_packet_context_packet_size(stream, packet_size_bits); - if (ret) { - BT_LOGW("Cannot set packet context's packet size field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - ret = set_packet_context_content_size(stream, content_size_bits); - if (ret) { - BT_LOGW("Cannot set packet context's content size field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - if (set_ts) { - ret = set_packet_context_timestamps(stream); - if (ret) { - BT_LOGW("Cannot set packet context's timestamp fields: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - } - - ret = set_packet_context_events_discarded(stream); - if (ret) { - BT_LOGW("Cannot set packet context's discarded events count field: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - goto end; - } - - BT_LOGV("Automatically populated stream's packet context's known fields: " - "stream-addr=%p, stream-name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - -end: - return ret; -} - -static -void release_event(struct bt_ctf_event *event) -{ - if (bt_ctf_object_get_ref_count(&event->common.base)) { - /* - * The event is being orphaned, but it must guarantee the - * existence of its event class for the duration of its - * lifetime. - */ - bt_ctf_object_get_ref(event->common.class); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event->common.base.parent); - } else { - bt_ctf_object_try_spec_release(&event->common.base); - } -} - -static -int create_stream_file(struct bt_ctf_writer *writer, - struct bt_ctf_stream *stream) -{ - int ret = 0; - GString *filename = g_string_new(NULL); - int64_t stream_class_id; - char *file_path = NULL; - - BT_LOGD("Creating stream file: writer-addr=%p, stream-addr=%p, " - "stream-name=\"%s\", stream-class-addr=%p, stream-class-name=\"%s\"", - writer, stream, bt_ctf_stream_get_name(stream), - stream->common.stream_class, - stream->common.stream_class->name->str); - - if (stream->common.name && stream->common.name->len > 0) { - /* Use stream name's base name as prefix */ - gchar *basename = g_path_get_basename(stream->common.name->str); - - BT_ASSERT(basename); - - if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) { - g_string_assign(filename, "stream"); - } else { - g_string_assign(filename, basename); - } - - g_free(basename); - goto append_ids; - } - - if (stream->common.stream_class->name && - stream->common.stream_class->name->len > 0) { - /* Use stream class name's base name as prefix */ - gchar *basename = - g_path_get_basename( - stream->common.stream_class->name->str); - - BT_ASSERT(basename); - - if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) { - g_string_assign(filename, "stream"); - } else { - g_string_assign(filename, basename); - } - - g_free(basename); - goto append_ids; - } - - /* Default to using `stream-` as prefix */ - g_string_assign(filename, "stream"); - -append_ids: - stream_class_id = bt_ctf_stream_class_common_get_id(stream->common.stream_class); - BT_ASSERT(stream_class_id >= 0); - BT_ASSERT(stream->common.id >= 0); - g_string_append_printf(filename, "-%" PRId64 "-%" PRId64, - stream_class_id, stream->common.id); - - file_path = g_build_filename(writer->path->str, filename->str, NULL); - if (file_path == NULL) { - ret = -1; - goto end; - } - - ret = bt_ctfser_init(&stream->ctfser, file_path); - g_free(file_path); - if (ret) { - /* bt_ctfser_init() logs errors */ - goto end; - } - - BT_LOGD("Created stream file for writing: " - "stream-addr=%p, stream-name=\"%s\", " - "filename=\"%s\"", stream, bt_ctf_stream_get_name(stream), - filename->str); - -end: - g_string_free(filename, TRUE); - return ret; -} - -BT_HIDDEN -struct bt_ctf_stream *bt_ctf_stream_create_with_id( - struct bt_ctf_stream_class *stream_class, - const char *name, uint64_t id) -{ - int ret; - int fd; - struct bt_ctf_stream *stream = NULL; - struct bt_ctf_trace *trace = NULL; - struct bt_ctf_writer *writer = NULL; - - BT_LOGD("Creating CTF writer stream object: stream-class-addr=%p, " - "stream-class-name=\"%s\", stream-name=\"%s\", " - "stream-id=%" PRIu64, - stream_class, bt_ctf_stream_class_get_name(stream_class), - name, id); - stream = g_new0(struct bt_ctf_stream, 1); - if (!stream) { - BT_LOGE_STR("Failed to allocate one stream."); - goto error; - } - - if (id == -1ULL) { - id = stream_class->next_stream_id; - } - - ret = bt_ctf_stream_common_initialize(BT_CTF_TO_COMMON(stream), - BT_CTF_TO_COMMON(stream_class), name, id, bt_ctf_stream_destroy); - if (ret) { - /* bt_ctf_stream_common_initialize() logs errors */ - goto error; - } - - trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( - BT_CTF_TO_COMMON(stream_class))); - if (!trace) { - BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-name=\"%s\"", - stream_class, bt_ctf_stream_class_get_name(stream_class), - name); - goto error; - } - - writer = (struct bt_ctf_writer *) - bt_ctf_object_get_parent(&trace->common.base); - stream->last_ts_end = -1ULL; - BT_LOGD("CTF writer stream object belongs writer's trace: " - "writer-addr=%p", writer); - BT_ASSERT(writer); - - if (stream_class->common.packet_context_field_type) { - BT_LOGD("Creating stream's packet context field: " - "ft-addr=%p", - stream_class->common.packet_context_field_type); - stream->packet_context = bt_ctf_field_create( - (void *) stream_class->common.packet_context_field_type); - if (!stream->packet_context) { - BT_LOGW_STR("Cannot create stream's packet context field."); - goto error; - } - - /* Initialize events_discarded */ - ret = try_set_structure_field_integer( - stream->packet_context, "events_discarded", 0); - if (ret < 0) { - BT_LOGW("Cannot set `events_discarded` field in packet context: " - "ret=%d, packet-context-field-addr=%p", - ret, stream->packet_context); - goto error; - } - } - - stream->events = g_ptr_array_new_with_free_func( - (GDestroyNotify) release_event); - if (!stream->events) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - if (trace->common.packet_header_field_type) { - BT_LOGD("Creating stream's packet header field: " - "ft-addr=%p", trace->common.packet_header_field_type); - stream->packet_header = - bt_ctf_field_create( - (void *) trace->common.packet_header_field_type); - if (!stream->packet_header) { - BT_LOGW_STR("Cannot create stream's packet header field."); - goto error; - } - } - - /* - * Attempt to populate the default trace packet header fields - * (magic, uuid and stream_id). This will _not_ fail shall the - * fields not be found or be of an incompatible type; they will - * simply not be populated automatically. The user will have to - * make sure to set the trace packet header fields himself - * before flushing. - */ - ret = auto_populate_packet_header(stream); - if (ret) { - BT_LOGW_STR("Cannot automatically populate the stream's packet header."); - goto error; - } - - /* Create file associated with this stream */ - fd = create_stream_file(writer, stream); - if (fd < 0) { - BT_LOGW_STR("Cannot create stream file."); - goto error; - } - - /* Freeze the writer */ - BT_LOGD_STR("Freezing stream's CTF writer."); - bt_ctf_writer_freeze(writer); - - /* Add this stream to the trace's streams */ - g_ptr_array_add(trace->common.streams, stream); - stream_class->next_stream_id++; - BT_LOGD("Created stream object: addr=%p", stream); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(stream); - -end: - bt_ctf_object_put_ref(writer); - return stream; -} - -struct bt_ctf_stream *bt_ctf_stream_create( - struct bt_ctf_stream_class *stream_class, - const char *name, uint64_t id_param) -{ - return bt_ctf_stream_create_with_id(stream_class, - name, id_param); -} - -int bt_ctf_stream_get_discarded_events_count( - struct bt_ctf_stream *stream, uint64_t *count) -{ - int ret = 0; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - ret = -1; - goto end; - } - - if (!count) { - BT_LOGW_STR("Invalid parameter: count is NULL."); - ret = -1; - goto end; - } - - *count = (uint64_t) stream->discarded_events; - -end: - return ret; -} - -static -int set_packet_context_events_discarded_field(struct bt_ctf_stream *stream, - uint64_t count) -{ - int ret = 0; - struct bt_ctf_field *events_discarded_field = NULL; - - if (!stream->packet_context) { - goto end; - } - - events_discarded_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "events_discarded"); - if (!events_discarded_field) { - goto end; - } - - ret = bt_ctf_field_integer_unsigned_set_value( - events_discarded_field, count); - if (ret) { - BT_LOGW("Cannot set packet context's `events_discarded` field: " - "field-addr=%p, value=%" PRIu64, - events_discarded_field, count); - goto end; - } - -end: - bt_ctf_object_put_ref(events_discarded_field); - return ret; -} - -void bt_ctf_stream_append_discarded_events(struct bt_ctf_stream *stream, - uint64_t event_count) -{ - int ret; - uint64_t new_count; - struct bt_ctf_field *events_discarded_field = NULL; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - goto end; - } - - BT_LOGV("Appending discarded events to stream: " - "stream-addr=%p, stream-name=\"%s\", append-count=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), event_count); - - if (!stream->packet_context) { - BT_LOGW_STR("Invalid parameter: stream has no packet context field."); - goto end; - } - - events_discarded_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "events_discarded"); - if (!events_discarded_field) { - BT_LOGW_STR("No field named `events_discarded` in stream's packet context."); - goto end; - } - - new_count = stream->discarded_events + event_count; - if (new_count < stream->discarded_events) { - BT_LOGW("New discarded events count is less than the stream's current discarded events count: " - "cur-count=%" PRIu64 ", new-count=%" PRIu64, - stream->discarded_events, new_count); - goto end; - } - - ret = set_packet_context_events_discarded_field(stream, new_count); - if (ret) { - /* set_packet_context_events_discarded_field() logs errors */ - goto end; - } - - stream->discarded_events = new_count; - BT_LOGV("Appended discarded events to stream: " - "stream-addr=%p, stream-name=\"%s\", append-count=%" PRIu64, - stream, bt_ctf_stream_get_name(stream), event_count); - -end: - bt_ctf_object_put_ref(events_discarded_field); -} - -static int auto_populate_event_header(struct bt_ctf_stream *stream, - struct bt_ctf_event *event) -{ - int ret = 0; - struct bt_ctf_field *id_field = NULL, *timestamp_field = NULL; - struct bt_ctf_clock_class *mapped_clock_class = NULL; - struct bt_ctf_stream_class *stream_class = - BT_CTF_FROM_COMMON(bt_ctf_stream_common_borrow_class( - BT_CTF_TO_COMMON(stream))); - int64_t event_class_id; - - BT_ASSERT(event); - - if (!event->common.header_field) { - goto end; - } - - if (event->common.frozen) { - BT_LOGW_STR("Cannot populate event header field: event is frozen."); - ret = -1; - goto end; - } - - BT_LOGV("Automatically populating event's header field: " - "stream-addr=%p, stream-name=\"%s\", event-addr=%p", - stream, bt_ctf_stream_get_name(stream), event); - - id_field = bt_ctf_field_structure_get_field_by_name( - (void *) event->common.header_field->field, "id"); - event_class_id = bt_ctf_event_class_common_get_id(event->common.class); - BT_ASSERT(event_class_id >= 0); - - if (id_field && bt_ctf_field_get_type_id(id_field) == BT_CTF_FIELD_TYPE_ID_INTEGER) { - ret = set_integer_field_value(id_field, event_class_id); - if (ret) { - BT_LOGW("Cannot set event header's `id` field's value: " - "addr=%p, value=%" PRIu64, id_field, - event_class_id); - goto end; - } - } - - /* - * The conditions to automatically set the timestamp are: - * - * 1. The event header field "timestamp" exists and is an - * integer field. - * 2. This stream's class has a registered clock (set with - * bt_ctf_stream_class_set_clock()). - * 3. The "timestamp" field is not set. - */ - timestamp_field = bt_ctf_field_structure_get_field_by_name( - (void *) event->common.header_field->field, "timestamp"); - if (timestamp_field && stream_class->clock && - bt_ctf_field_get_type_id(id_field) == BT_CTF_FIELD_TYPE_ID_INTEGER && - !bt_ctf_field_is_set_recursive(timestamp_field)) { - mapped_clock_class = - bt_ctf_field_type_integer_get_mapped_clock_class( - (void *) ((struct bt_ctf_field_common *) timestamp_field)->type); - if (mapped_clock_class) { - uint64_t timestamp; - - BT_ASSERT(mapped_clock_class == - stream_class->clock->clock_class); - ret = bt_ctf_clock_get_value( - stream_class->clock, - ×tamp); - BT_ASSERT(ret == 0); - ret = set_integer_field_value(timestamp_field, - timestamp); - if (ret) { - BT_LOGW("Cannot set event header's `timestamp` field's value: " - "addr=%p, value=%" PRIu64, - timestamp_field, timestamp); - goto end; - } - } - } - - BT_LOGV("Automatically populated event's header field: " - "stream-addr=%p, stream-name=\"%s\", event-addr=%p", - stream, bt_ctf_stream_get_name(stream), event); - -end: - bt_ctf_object_put_ref(id_field); - bt_ctf_object_put_ref(timestamp_field); - bt_ctf_object_put_ref(mapped_clock_class); - return ret; -} - -int bt_ctf_stream_append_event(struct bt_ctf_stream *stream, - struct bt_ctf_event *event) -{ - int ret = 0; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - ret = -1; - goto end; - } - - if (!event) { - BT_LOGW_STR("Invalid parameter: event is NULL."); - ret = -1; - goto end; - } - - BT_LOGV("Appending event to stream: " - "stream-addr=%p, stream-name=\"%s\", event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - stream, bt_ctf_stream_get_name(stream), event, - bt_ctf_event_class_common_get_name( - bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))), - bt_ctf_event_class_common_get_id( - bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event)))); - - /* - * The event is not supposed to have a parent stream at this - * point. The only other way an event can have a parent stream - * is if it was assigned when setting a packet to the event, - * in which case the packet's stream is not a writer stream, - * and thus the user is trying to append an event which belongs - * to another stream. - */ - if (event->common.base.parent) { - ret = -1; - goto end; - } - - bt_ctf_object_set_parent(&event->common.base, &stream->common.base); - BT_LOGV_STR("Automatically populating the header of the event to append."); - ret = auto_populate_event_header(stream, event); - if (ret) { - /* auto_populate_event_header() reports errors */ - goto error; - } - - /* Make sure the various scopes of the event are set */ - BT_LOGV_STR("Validating event to append."); - BT_CTF_ASSERT_PRE(bt_ctf_event_common_validate(BT_CTF_TO_COMMON(event)) == 0, - "Invalid event: event-addr=%p", event); - - /* Save the new event and freeze it */ - BT_LOGV_STR("Freezing the event to append."); - bt_ctf_event_common_set_is_frozen(BT_CTF_TO_COMMON(event), true); - g_ptr_array_add(stream->events, event); - - /* - * Event had to hold a reference to its event class as long as it wasn't - * part of the same trace hierarchy. From now on, the event and its - * class share the same lifetime guarantees and the reference is no - * longer needed. - */ - BT_LOGV_STR("Putting the event's class."); - bt_ctf_object_put_ref(event->common.class); - BT_LOGV("Appended event to stream: " - "stream-addr=%p, stream-name=\"%s\", event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - stream, bt_ctf_stream_get_name(stream), event, - bt_ctf_event_class_common_get_name( - bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))), - bt_ctf_event_class_common_get_id( - bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event)))); - -end: - return ret; - -error: - /* - * Orphan the event; we were not successful in associating it to - * a stream. - */ - bt_ctf_object_set_parent(&event->common.base, NULL); - return ret; -} - -struct bt_ctf_field *bt_ctf_stream_get_packet_context(struct bt_ctf_stream *stream) -{ - struct bt_ctf_field *packet_context = NULL; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - goto end; - } - - packet_context = stream->packet_context; - if (packet_context) { - bt_ctf_object_get_ref(packet_context); - } -end: - return packet_context; -} - -int bt_ctf_stream_set_packet_context(struct bt_ctf_stream *stream, - struct bt_ctf_field *field) -{ - int ret = 0; - struct bt_ctf_field_type *field_type; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - ret = -1; - goto end; - } - - field_type = bt_ctf_field_get_type(field); - if (bt_ctf_field_type_common_compare((void *) field_type, - stream->common.stream_class->packet_context_field_type)) { - BT_LOGW("Invalid parameter: packet context's field type is different from the stream's packet context field type: " - "stream-addr=%p, stream-name=\"%s\", " - "packet-context-field-addr=%p, " - "packet-context-ft-addr=%p", - stream, bt_ctf_stream_get_name(stream), - field, field_type); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(field_type); - bt_ctf_object_put_ref(stream->packet_context); - stream->packet_context = bt_ctf_object_get_ref(field); - BT_LOGV("Set stream's packet context field: " - "stream-addr=%p, stream-name=\"%s\", " - "packet-context-field-addr=%p", - stream, bt_ctf_stream_get_name(stream), field); -end: - return ret; -} - -struct bt_ctf_field *bt_ctf_stream_get_packet_header(struct bt_ctf_stream *stream) -{ - struct bt_ctf_field *packet_header = NULL; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - goto end; - } - - packet_header = stream->packet_header; - if (packet_header) { - bt_ctf_object_get_ref(packet_header); - } -end: - return packet_header; -} - -int bt_ctf_stream_set_packet_header(struct bt_ctf_stream *stream, - struct bt_ctf_field *field) -{ - int ret = 0; - struct bt_ctf_trace *trace = NULL; - struct bt_ctf_field_type *field_type = NULL; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - ret = -1; - goto end; - } - - trace = (struct bt_ctf_trace *) - bt_ctf_object_get_parent(&stream->common.base); - - if (!field) { - if (trace->common.packet_header_field_type) { - BT_LOGW("Invalid parameter: setting no packet header but packet header field type is not NULL: " - "stream-addr=%p, stream-name=\"%s\", " - "packet-header-field-addr=%p, " - "expected-ft-addr=%p", - stream, bt_ctf_stream_get_name(stream), - field, trace->common.packet_header_field_type); - ret = -1; - goto end; - } - - goto skip_validation; - } - - field_type = bt_ctf_field_get_type(field); - BT_ASSERT(field_type); - - if (bt_ctf_field_type_common_compare((void *) field_type, - trace->common.packet_header_field_type)) { - BT_LOGW("Invalid parameter: packet header's field type is different from the stream's packet header field type: " - "stream-addr=%p, stream-name=\"%s\", " - "packet-header-field-addr=%p, " - "packet-header-ft-addr=%p", - stream, bt_ctf_stream_get_name(stream), - field, field_type); - ret = -1; - goto end; - } - -skip_validation: - bt_ctf_object_put_ref(stream->packet_header); - stream->packet_header = bt_ctf_object_get_ref(field); - BT_LOGV("Set stream's packet header field: " - "stream-addr=%p, stream-name=\"%s\", " - "packet-header-field-addr=%p", - stream, bt_ctf_stream_get_name(stream), field); -end: - BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); - bt_ctf_object_put_ref(field_type); - return ret; -} - -static -void reset_structure_field(struct bt_ctf_field *structure, const char *name) -{ - struct bt_ctf_field *member; - - member = bt_ctf_field_structure_get_field_by_name(structure, name); - if (member) { - bt_ctf_field_common_reset_recursive((void *) member); - bt_ctf_object_put_ref(member); - } -} - -int bt_ctf_stream_flush(struct bt_ctf_stream *stream) -{ - int ret = 0; - size_t i; - uint64_t packet_context_offset_bits = 0; - struct bt_ctf_trace *trace; - enum bt_ctf_byte_order native_byte_order; - bool has_packet_size = false; - uint64_t packet_size_bits = 0; - uint64_t content_size_bits = 0; - - if (!stream) { - BT_LOGW_STR("Invalid parameter: stream is NULL."); - ret = -1; - goto end_no_stream; - } - - if (stream->packet_context) { - struct bt_ctf_field *packet_size_field; - - packet_size_field = bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "packet_size"); - has_packet_size = (packet_size_field != NULL); - bt_ctf_object_put_ref(packet_size_field); - } - - if (stream->flushed_packet_count == 1) { - if (!stream->packet_context) { - BT_LOGW_STR("Cannot flush a stream which has no packet context field more than once."); - ret = -1; - goto end; - } - - if (!has_packet_size) { - BT_LOGW_STR("Cannot flush a stream which has no packet context's `packet_size` field more than once."); - ret = -1; - goto end; - } - } - - BT_LOGV("Flushing stream's current packet: stream-addr=%p, " - "stream-name=\"%s\", packet-index=%u", stream, - bt_ctf_stream_get_name(stream), stream->flushed_packet_count); - trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( - stream->common.stream_class)); - BT_ASSERT(trace); - native_byte_order = bt_ctf_trace_get_native_byte_order(trace); - - ret = auto_populate_packet_header(stream); - if (ret) { - BT_LOGW_STR("Cannot automatically populate the stream's packet header field."); - ret = -1; - goto end; - } - - /* Initialize packet/content sizes to `0`; we will overwrite later */ - ret = auto_populate_packet_context(stream, true, 0, 0); - if (ret) { - BT_LOGW_STR("Cannot automatically populate the stream's packet context field."); - ret = -1; - goto end; - } - - ret = bt_ctfser_open_packet(&stream->ctfser); - if (ret) { - /* bt_ctfser_open_packet() logs errors */ - ret = -1; - goto end; - } - - if (stream->packet_header) { - BT_LOGV_STR("Serializing packet header field (initial)."); - ret = bt_ctf_field_serialize_recursive(stream->packet_header, - &stream->ctfser, native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize stream's packet header field: " - "field-addr=%p", stream->packet_header); - goto end; - } - } - - if (stream->packet_context) { - /* Save packet context's position to overwrite it later */ - packet_context_offset_bits = - bt_ctfser_get_offset_in_current_packet_bits( - &stream->ctfser); - - /* Write packet context */ - BT_LOGV_STR("Serializing packet context field (initial)."); - ret = bt_ctf_field_serialize_recursive(stream->packet_context, - &stream->ctfser, native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize stream's packet context field: " - "field-addr=%p", stream->packet_context); - goto end; - } - } - - BT_LOGV("Serializing events: count=%u", stream->events->len); - - for (i = 0; i < stream->events->len; i++) { - struct bt_ctf_event *event = g_ptr_array_index( - stream->events, i); - struct bt_ctf_event_class *event_class = - BT_CTF_FROM_COMMON(bt_ctf_event_common_borrow_class( - BT_CTF_TO_COMMON(event))); - - BT_LOGV("Serializing event: index=%zu, event-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "ser-offset=%" PRIu64, - i, event, bt_ctf_event_class_get_name(event_class), - bt_ctf_event_class_get_id(event_class), - bt_ctfser_get_offset_in_current_packet_bits( - &stream->ctfser)); - - /* Write event header */ - if (event->common.header_field) { - BT_LOGV_STR("Serializing event's header field."); - ret = bt_ctf_field_serialize_recursive( - (void *) event->common.header_field->field, - &stream->ctfser, native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize event's header field: " - "field-addr=%p", - event->common.header_field->field); - goto end; - } - } - - /* Write stream event context */ - if (event->common.stream_event_context_field) { - BT_LOGV_STR("Serializing event's stream event context field."); - ret = bt_ctf_field_serialize_recursive( - (void *) event->common.stream_event_context_field, - &stream->ctfser, native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize event's stream event context field: " - "field-addr=%p", - event->common.stream_event_context_field); - goto end; - } - } - - /* Write event content */ - ret = bt_ctf_event_serialize(event, &stream->ctfser, - native_byte_order); - if (ret) { - /* bt_ctf_event_serialize() logs errors */ - goto end; - } - } - - content_size_bits = bt_ctfser_get_offset_in_current_packet_bits( - &stream->ctfser); - - if (!has_packet_size && content_size_bits % 8 != 0) { - BT_LOGW("Stream's packet context field type has no `packet_size` field, " - "but current content size is not a multiple of 8 bits: " - "content-size=%" PRIu64 ", " - "packet-size=%" PRIu64, - content_size_bits, - packet_size_bits); - ret = -1; - goto end; - } - - /* Set packet size; make it a multiple of 8 */ - packet_size_bits = (content_size_bits + 7) & ~UINT64_C(7); - - if (stream->packet_context) { - /* - * The whole packet is serialized at this point. Make - * sure that, if `packet_size` is missing, the current - * content size is equal to the current packet size. - */ - struct bt_ctf_field *field = - bt_ctf_field_structure_get_field_by_name( - stream->packet_context, "content_size"); - - bt_ctf_object_put_ref(field); - if (!field) { - if (content_size_bits != packet_size_bits) { - BT_LOGW("Stream's packet context's `content_size` field is missing, " - "but current packet's content size is not equal to its packet size: " - "content-size=%" PRIu64 ", " - "packet-size=%" PRIu64, - bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser), - packet_size_bits); - ret = -1; - goto end; - } - } - - /* - * Overwrite the packet context now that the stream - * position's packet and content sizes have the correct - * values. - */ - bt_ctfser_set_offset_in_current_packet_bits(&stream->ctfser, - packet_context_offset_bits); - ret = auto_populate_packet_context(stream, false, - packet_size_bits, content_size_bits); - if (ret) { - BT_LOGW_STR("Cannot automatically populate the stream's packet context field."); - ret = -1; - goto end; - } - - BT_LOGV("Rewriting (serializing) packet context field."); - ret = bt_ctf_field_serialize_recursive(stream->packet_context, - &stream->ctfser, native_byte_order); - if (ret) { - BT_LOGW("Cannot serialize stream's packet context field: " - "field-addr=%p", stream->packet_context); - goto end; - } - } - - g_ptr_array_set_size(stream->events, 0); - stream->flushed_packet_count++; - bt_ctfser_close_current_packet(&stream->ctfser, packet_size_bits / 8); - -end: - /* Reset automatically-set fields. */ - if (stream->packet_context) { - reset_structure_field(stream->packet_context, "timestamp_begin"); - reset_structure_field(stream->packet_context, "timestamp_end"); - reset_structure_field(stream->packet_context, "packet_size"); - reset_structure_field(stream->packet_context, "content_size"); - reset_structure_field(stream->packet_context, "events_discarded"); - } - - if (ret == 0) { - BT_LOGV("Flushed stream's current packet: " - "content-size=%" PRIu64 ", packet-size=%" PRIu64, - content_size_bits, packet_size_bits); - } - -end_no_stream: - return ret; -} - -static -void bt_ctf_stream_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_stream *stream = (void *) obj; - - BT_LOGD("Destroying CTF writer stream object: addr=%p, name=\"%s\"", - stream, bt_ctf_stream_get_name(stream)); - - bt_ctf_stream_common_finalize(BT_CTF_TO_COMMON(stream)); - bt_ctfser_fini(&stream->ctfser); - - if (stream->events) { - BT_LOGD_STR("Putting events."); - g_ptr_array_free(stream->events, TRUE); - } - - BT_LOGD_STR("Putting packet header field."); - bt_ctf_object_put_ref(stream->packet_header); - BT_LOGD_STR("Putting packet context field."); - bt_ctf_object_put_ref(stream->packet_context); - g_free(stream); -} - -static -int _set_structure_field_integer(struct bt_ctf_field *structure, char *name, - uint64_t value, bt_bool force) -{ - int ret = 0; - struct bt_ctf_field_type *field_type = NULL; - struct bt_ctf_field *integer; - - BT_ASSERT(structure); - BT_ASSERT(name); - - integer = bt_ctf_field_structure_get_field_by_name(structure, name); - if (!integer) { - /* Field not found, not an error. */ - BT_LOGV("Field not found: struct-field-addr=%p, " - "name=\"%s\", force=%d", structure, name, force); - goto end; - } - - /* Make sure the payload has not already been set. */ - if (!force && bt_ctf_field_is_set_recursive(integer)) { - /* Payload already set, not an error */ - BT_LOGV("Field's payload is already set: struct-field-addr=%p, " - "name=\"%s\", force=%d", structure, name, force); - goto end; - } - - field_type = bt_ctf_field_get_type(integer); - BT_ASSERT(field_type); - if (bt_ctf_field_type_get_type_id(field_type) != BT_CTF_FIELD_TYPE_ID_INTEGER) { - /* - * The user most likely meant for us to populate this field - * automatically. However, we can only do this if the field - * is an integer. Return an error. - */ - BT_LOGW("Invalid parameter: field's type is not an integer field type: " - "field-addr=%p, ft-addr=%p, ft-id=%s", - integer, field_type, - bt_ctf_field_type_id_string((int) - bt_ctf_field_type_get_type_id(field_type))); - ret = -1; - goto end; - } - - if (bt_ctf_field_type_integer_is_signed(field_type)) { - ret = bt_ctf_field_integer_signed_set_value(integer, - (int64_t) value); - } else { - ret = bt_ctf_field_integer_unsigned_set_value(integer, value); - } - ret = !ret ? 1 : ret; -end: - bt_ctf_object_put_ref(integer); - bt_ctf_object_put_ref(field_type); - return ret; -} - -/* - * Returns the following codes: - * 1 if the field was found and set, - * 0 if nothing was done (field not found, or was already set), - * <0 if an error was encoutered - */ -static -int try_set_structure_field_integer(struct bt_ctf_field *structure, char *name, - uint64_t value) -{ - return _set_structure_field_integer(structure, name, value, BT_FALSE); -} - -struct bt_ctf_stream_class *bt_ctf_stream_get_class( - struct bt_ctf_stream *stream) -{ - return bt_ctf_object_get_ref(bt_ctf_stream_common_borrow_class(BT_CTF_TO_COMMON(stream))); -} - -const char *bt_ctf_stream_get_name(struct bt_ctf_stream *stream) -{ - return bt_ctf_stream_common_get_name(BT_CTF_TO_COMMON(stream)); -} - -int64_t bt_ctf_stream_get_id(struct bt_ctf_stream *stream) -{ - return bt_ctf_stream_common_get_id(BT_CTF_TO_COMMON(stream)); -} diff --git a/ctf-writer/trace.c b/ctf-writer/trace.c deleted file mode 100644 index 8737470c..00000000 --- a/ctf-writer/trace.c +++ /dev/null @@ -1,1883 +0,0 @@ -/* - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-TRACE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_IDENTIFIER_SIZE 128 -#define DEFAULT_METADATA_STRING_SIZE 4096 - -BT_HIDDEN -int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace, - bt_ctf_object_release_func release_func) -{ - int ret = 0; - - BT_LOGD_STR("Initializing common trace object."); - trace->native_byte_order = BT_CTF_BYTE_ORDER_UNSPECIFIED; - bt_ctf_object_init_shared_with_parent(&trace->base, release_func); - trace->clock_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_ctf_object_put_ref); - if (!trace->clock_classes) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - - trace->streams = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_ctf_object_try_spec_release); - if (!trace->streams) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - - trace->stream_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_ctf_object_try_spec_release); - if (!trace->stream_classes) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - - /* Create the environment array object */ - trace->environment = bt_ctf_attributes_create(); - if (!trace->environment) { - BT_LOGE_STR("Cannot create empty attributes object."); - goto error; - } - - BT_LOGD("Initialized common trace object: addr=%p", trace); - goto end; - -error: - ret = -1; - -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace) -{ - BT_LOGD("Finalizing common trace object: addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - - if (trace->environment) { - BT_LOGD_STR("Destroying environment attributes."); - bt_ctf_attributes_destroy(trace->environment); - } - - if (trace->name) { - g_string_free(trace->name, TRUE); - } - - if (trace->clock_classes) { - BT_LOGD_STR("Putting clock classes."); - g_ptr_array_free(trace->clock_classes, TRUE); - } - - if (trace->streams) { - BT_LOGD_STR("Destroying streams."); - g_ptr_array_free(trace->streams, TRUE); - } - - if (trace->stream_classes) { - BT_LOGD_STR("Destroying stream classes."); - g_ptr_array_free(trace->stream_classes, TRUE); - } - - BT_LOGD_STR("Putting packet header field type."); - bt_ctf_object_put_ref(trace->packet_header_field_type); -} - -BT_HIDDEN -int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!name) { - BT_LOGW_STR("Invalid parameter: name is NULL."); - ret = -1; - goto end; - } - - if (trace->frozen) { - BT_LOGW("Invalid parameter: trace is frozen: " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - ret = -1; - goto end; - } - - trace->name = trace->name ? g_string_assign(trace->name, name) : - g_string_new(name); - if (!trace->name) { - BT_LOGE_STR("Failed to allocate one GString."); - ret = -1; - goto end; - } - - BT_LOGV("Set trace's name: addr=%p, name=\"%s\"", trace, name); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace, - const unsigned char *uuid) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!uuid) { - BT_LOGW_STR("Invalid parameter: UUID is NULL."); - ret = -1; - goto end; - } - - if (trace->frozen) { - BT_LOGW("Invalid parameter: trace is frozen: " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - ret = -1; - goto end; - } - - memcpy(trace->uuid, uuid, BABELTRACE_UUID_LEN); - trace->uuid_set = BT_TRUE; - BT_LOGV("Set trace's UUID: addr=%p, name=\"%s\", " - "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - trace, bt_ctf_trace_common_get_name(trace), - (unsigned int) uuid[0], - (unsigned int) uuid[1], - (unsigned int) uuid[2], - (unsigned int) uuid[3], - (unsigned int) uuid[4], - (unsigned int) uuid[5], - (unsigned int) uuid[6], - (unsigned int) uuid[7], - (unsigned int) uuid[8], - (unsigned int) uuid[9], - (unsigned int) uuid[10], - (unsigned int) uuid[11], - (unsigned int) uuid[12], - (unsigned int) uuid[13], - (unsigned int) uuid[14], - (unsigned int) uuid[15]); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace, - const char *name, struct bt_ctf_private_value *value) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!name) { - BT_LOGW_STR("Invalid parameter: name is NULL."); - ret = -1; - goto end; - } - - if (!value) { - BT_LOGW_STR("Invalid parameter: value is NULL."); - ret = -1; - goto end; - } - - if (!bt_ctf_identifier_is_valid(name)) { - BT_LOGW("Invalid parameter: environment field's name is not a valid CTF identifier: " - "trace-addr=%p, trace-name=\"%s\", " - "env-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), name); - ret = -1; - goto end; - } - - if (!bt_ctf_value_is_integer(bt_ctf_private_value_as_value(value)) && - !bt_ctf_value_is_string(bt_ctf_private_value_as_value(value))) { - BT_LOGW("Invalid parameter: environment field's value is not an integer or string value: " - "trace-addr=%p, trace-name=\"%s\", " - "env-name=\"%s\", env-value-type=%s", - trace, bt_ctf_trace_common_get_name(trace), name, - bt_ctf_value_type_string( - bt_ctf_value_get_type( - bt_ctf_private_value_as_value(value)))); - ret = -1; - goto end; - } - - if (trace->frozen) { - /* - * New environment fields may be added to a frozen trace, - * but existing fields may not be changed. - * - * The object passed is frozen like all other attributes. - */ - struct bt_ctf_private_value *attribute = - bt_ctf_attributes_borrow_field_value_by_name( - trace->environment, name); - - if (attribute) { - BT_LOGW("Invalid parameter: trace is frozen and environment field already exists with this name: " - "trace-addr=%p, trace-name=\"%s\", " - "env-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), name); - ret = -1; - goto end; - } - - bt_ctf_value_freeze(bt_ctf_private_value_as_value(value)); - } - - ret = bt_ctf_attributes_set_field_value(trace->environment, name, - value); - if (ret) { - BT_LOGE("Cannot set environment field's value: " - "trace-addr=%p, trace-name=\"%s\", " - "env-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), name); - } else { - BT_LOGV("Set environment field's value: " - "trace-addr=%p, trace-name=\"%s\", " - "env-name=\"%s\", value-addr=%p", - trace, bt_ctf_trace_common_get_name(trace), name, value); - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace, - const char *name, const char *value) -{ - int ret = 0; - struct bt_ctf_private_value *env_value_string_obj = NULL; - - if (!value) { - BT_LOGW_STR("Invalid parameter: value is NULL."); - ret = -1; - goto end; - } - - env_value_string_obj = bt_ctf_private_value_string_create_init(value); - if (!env_value_string_obj) { - BT_LOGE_STR("Cannot create string value object."); - ret = -1; - goto end; - } - - /* bt_ctf_trace_common_set_environment_field() logs errors */ - ret = bt_ctf_trace_common_set_environment_field(trace, name, - env_value_string_obj); - -end: - bt_ctf_object_put_ref(env_value_string_obj); - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field_integer( - struct bt_ctf_trace_common *trace, const char *name, int64_t value) -{ - int ret = 0; - struct bt_ctf_private_value *env_value_integer_obj = NULL; - - env_value_integer_obj = bt_ctf_private_value_integer_create_init(value); - if (!env_value_integer_obj) { - BT_LOGE_STR("Cannot create integer value object."); - ret = -1; - goto end; - } - - /* bt_ctf_trace_common_set_environment_field() logs errors */ - ret = bt_ctf_trace_common_set_environment_field(trace, name, - env_value_integer_obj); - -end: - bt_ctf_object_put_ref(env_value_integer_obj); - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_clock_class *clock_class) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!bt_ctf_clock_class_is_valid(clock_class)) { - BT_LOGW("Invalid parameter: clock class is invalid: " - "trace-addr=%p, trace-name=\"%s\", " - "clock-class-addr=%p, clock-class-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - /* Check for duplicate clock classes */ - if (bt_ctf_trace_common_has_clock_class(trace, clock_class)) { - BT_LOGW("Invalid parameter: clock class already exists in trace: " - "trace-addr=%p, trace-name=\"%s\", " - "clock-class-addr=%p, clock-class-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), - clock_class, bt_ctf_clock_class_get_name(clock_class)); - ret = -1; - goto end; - } - - bt_ctf_object_get_ref(clock_class); - g_ptr_array_add(trace->clock_classes, clock_class); - - if (trace->frozen) { - BT_LOGV_STR("Freezing added clock class because trace is frozen."); - bt_ctf_clock_class_freeze(clock_class); - } - - BT_LOGV("Added clock class to trace: " - "trace-addr=%p, trace-name=\"%s\", " - "clock-class-addr=%p, clock-class-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), - clock_class, bt_ctf_clock_class_get_name(clock_class)); - -end: - return ret; -} - -static -bool packet_header_field_type_is_valid(struct bt_ctf_trace_common *trace, - struct bt_ctf_field_type_common *packet_header_type) -{ - int ret; - bool is_valid = true; - struct bt_ctf_field_type_common *field_type = NULL; - - if (!packet_header_type) { - /* - * No packet header field type: trace must have only - * one stream. At this point the stream class being - * added is not part of the trace yet, so we validate - * that the trace contains no stream classes yet. - */ - if (trace->stream_classes->len >= 1) { - BT_LOGW_STR("Invalid packet header field type: " - "packet header field type does not exist but there's more than one stream class in the trace."); - goto invalid; - } - - /* No packet header field type: valid at this point */ - goto end; - } - - /* Packet header field type, if it exists, must be a structure */ - if (packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid packet header field type: must be a structure field type if it exists: " - "ft-addr=%p, ft-id=%s", - packet_header_type, - bt_ctf_field_type_id_string(packet_header_type->id)); - goto invalid; - } - - /* - * If there's a `magic` field, it must be a 32-bit unsigned - * integer field type. Also it must be the first field of the - * packet header field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_header_type, "magic"); - if (field_type) { - const char *field_name; - - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet header field type: `magic` field must be an integer field type: " - "magic-ft-addr=%p, magic-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet header field type: `magic` field must be an unsigned integer field type: " - "magic-ft-addr=%p", field_type); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_get_size(field_type) != 32) { - BT_LOGW("Invalid packet header field type: `magic` field must be a 32-bit unsigned integer field type: " - "magic-ft-addr=%p, magic-ft-size=%u", - field_type, - bt_ctf_field_type_common_integer_get_size(field_type)); - goto invalid; - } - - ret = bt_ctf_field_type_common_structure_borrow_field_by_index( - packet_header_type, &field_name, NULL, 0); - BT_ASSERT(ret == 0); - - if (strcmp(field_name, "magic") != 0) { - BT_LOGW("Invalid packet header field type: `magic` field must be the first field: " - "magic-ft-addr=%p, first-field-name=\"%s\"", - field_type, field_name); - goto invalid; - } - } - - /* - * If there's a `uuid` field, it must be an array field type of - * length 16 with an 8-bit unsigned integer element field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_header_type, "uuid"); - if (field_type) { - struct bt_ctf_field_type_common *elem_ft; - - if (field_type->id != BT_CTF_FIELD_TYPE_ID_ARRAY) { - BT_LOGW("Invalid packet header field type: `uuid` field must be an array field type: " - "uuid-ft-addr=%p, uuid-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_array_get_length(field_type) != 16) { - BT_LOGW("Invalid packet header field type: `uuid` array field type's length must be 16: " - "uuid-ft-addr=%p, uuid-ft-length=%" PRId64, - field_type, - bt_ctf_field_type_common_array_get_length(field_type)); - goto invalid; - } - - elem_ft = bt_ctf_field_type_common_array_borrow_element_field_type(field_type); - BT_ASSERT(elem_ft); - - if (elem_ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an integer field type: " - "elem-ft-addr=%p, elem-ft-id=%s", - elem_ft, - bt_ctf_field_type_id_string(elem_ft->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(elem_ft)) { - BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an unsigned integer field type: " - "elem-ft-addr=%p", elem_ft); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_get_size(elem_ft) != 8) { - BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an 8-bit unsigned integer field type: " - "elem-ft-addr=%p, elem-ft-size=%u", - elem_ft, - bt_ctf_field_type_common_integer_get_size(elem_ft)); - goto invalid; - } - } - - /* - * The `stream_id` field must exist if there's more than one - * stream classes in the trace. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_header_type, "stream_id"); - - if (!field_type && trace->stream_classes->len >= 1) { - BT_LOGW_STR("Invalid packet header field type: " - "`stream_id` field does not exist but there's more than one stream class in the trace."); - goto invalid; - } - - /* - * If there's a `stream_id` field, it must be an unsigned - * integer field type. - */ - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet header field type: `stream_id` field must be an integer field type: " - "stream-id-ft-addr=%p, stream-id-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet header field type: `stream_id` field must be an unsigned integer field type: " - "stream-id-ft-addr=%p", field_type); - goto invalid; - } - } - - /* - * If there's a `packet_seq_num` field, it must be an unsigned - * integer field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_header_type, "packet_seq_num"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an integer field type: " - "stream-id-ft-addr=%p, packet-seq-num-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an unsigned integer field type: " - "packet-seq-num-ft-addr=%p", field_type); - goto invalid; - } - } - - goto end; - -invalid: - is_valid = false; - -end: - return is_valid; -} - -static -bool packet_context_field_type_is_valid(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_type, - bool check_ts_begin_end_mapped) -{ - bool is_valid = true; - struct bt_ctf_field_type_common *field_type = NULL; - - if (!packet_context_type) { - /* No packet context field type: valid at this point */ - goto end; - } - - /* Packet context field type, if it exists, must be a structure */ - if (packet_context_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid packet context field type: must be a structure field type if it exists: " - "ft-addr=%p, ft-id=%s", - packet_context_type, - bt_ctf_field_type_id_string(packet_context_type->id)); - goto invalid; - } - - /* - * If there's a `packet_size` field, it must be an unsigned - * integer field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_context_type, "packet_size"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet context field type: `packet_size` field must be an integer field type: " - "packet-size-ft-addr=%p, packet-size-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet context field type: `packet_size` field must be an unsigned integer field type: " - "packet-size-ft-addr=%p", field_type); - goto invalid; - } - } - - /* - * If there's a `content_size` field, it must be an unsigned - * integer field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_context_type, "content_size"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet context field type: `content_size` field must be an integer field type: " - "content-size-ft-addr=%p, content-size-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet context field type: `content_size` field must be an unsigned integer field type: " - "content-size-ft-addr=%p", field_type); - goto invalid; - } - } - - /* - * If there's a `events_discarded` field, it must be an unsigned - * integer field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_context_type, "events_discarded"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet context field type: `events_discarded` field must be an integer field type: " - "events-discarded-ft-addr=%p, events-discarded-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet context field type: `events_discarded` field must be an unsigned integer field type: " - "events-discarded-ft-addr=%p", field_type); - goto invalid; - } - } - - /* - * If there's a `timestamp_begin` field, it must be an unsigned - * integer field type. Also, if the trace is not a CTF writer's - * trace, then we cannot automatically set the mapped clock - * class of this field, so it must have a mapped clock class. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_context_type, "timestamp_begin"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an integer field type: " - "timestamp-begin-ft-addr=%p, timestamp-begin-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an unsigned integer field type: " - "timestamp-begin-ft-addr=%p", field_type); - goto invalid; - } - - if (check_ts_begin_end_mapped) { - struct bt_ctf_clock_class *clock_class = - bt_ctf_field_type_common_integer_borrow_mapped_clock_class( - field_type); - - if (!clock_class) { - BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be mapped to a clock class: " - "timestamp-begin-ft-addr=%p", field_type); - goto invalid; - } - } - } - - /* - * If there's a `timestamp_end` field, it must be an unsigned - * integer field type. Also, if the trace is not a CTF writer's - * trace, then we cannot automatically set the mapped clock - * class of this field, so it must have a mapped clock class. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - packet_context_type, "timestamp_end"); - if (field_type) { - if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { - BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an integer field type: " - "timestamp-end-ft-addr=%p, timestamp-end-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - if (bt_ctf_field_type_common_integer_is_signed(field_type)) { - BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an unsigned integer field type: " - "timestamp-end-ft-addr=%p", field_type); - goto invalid; - } - - if (check_ts_begin_end_mapped) { - struct bt_ctf_clock_class *clock_class = - bt_ctf_field_type_common_integer_borrow_mapped_clock_class( - field_type); - - if (!clock_class) { - BT_LOGW("Invalid packet context field type: `timestamp_end` field must be mapped to a clock class: " - "timestamp-end-ft-addr=%p", field_type); - goto invalid; - } - } - } - - goto end; - -invalid: - is_valid = false; - -end: - return is_valid; -} - -static -bool event_header_field_type_is_valid(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *event_header_type) -{ - bool is_valid = true; - struct bt_ctf_field_type_common *field_type = NULL; - - /* - * We do not validate that the `timestamp` field exists here - * because CTF does not require this exact name to be mapped to - * a clock class. - */ - - if (!event_header_type) { - /* - * No event header field type: stream class must have - * only one event class. - */ - if (bt_ctf_stream_class_common_get_event_class_count(stream_class) > 1) { - BT_LOGW_STR("Invalid event header field type: " - "event header field type does not exist but there's more than one event class in the stream class."); - goto invalid; - } - - /* No event header field type: valid at this point */ - goto end; - } - - /* Event header field type, if it exists, must be a structure */ - if (event_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid event header field type: must be a structure field type if it exists: " - "ft-addr=%p, ft-id=%s", - event_header_type, - bt_ctf_field_type_id_string(event_header_type->id)); - goto invalid; - } - - /* - * If there's an `id` field, it must be an unsigned integer - * field type or an enumeration field type with an unsigned - * integer container field type. - */ - field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( - event_header_type, "id"); - if (field_type) { - struct bt_ctf_field_type_common *int_ft; - - if (field_type->id == BT_CTF_FIELD_TYPE_ID_INTEGER) { - int_ft = field_type; - } else if (field_type->id == BT_CTF_FIELD_TYPE_ID_ENUM) { - int_ft = bt_ctf_field_type_common_enumeration_borrow_container_field_type( - field_type); - } else { - BT_LOGW("Invalid event header field type: `id` field must be an integer or enumeration field type: " - "id-ft-addr=%p, id-ft-id=%s", - field_type, - bt_ctf_field_type_id_string(field_type->id)); - goto invalid; - } - - BT_ASSERT(int_ft); - if (bt_ctf_field_type_common_integer_is_signed(int_ft)) { - BT_LOGW("Invalid event header field type: `id` field must be an unsigned integer or enumeration field type: " - "id-ft-addr=%p", int_ft); - goto invalid; - } - } - - goto end; - -invalid: - is_valid = false; - -end: - return is_valid; -} - -static -int check_packet_header_type_has_no_clock_class(struct bt_ctf_trace_common *trace) -{ - int ret = 0; - - if (trace->packet_header_field_type) { - struct bt_ctf_clock_class *clock_class = NULL; - - ret = bt_ctf_field_type_common_validate_single_clock_class( - trace->packet_header_field_type, - &clock_class); - bt_ctf_object_put_ref(clock_class); - if (ret || clock_class) { - BT_LOGW("Trace's packet header field type cannot " - "contain a field type which is mapped to " - "a clock class: " - "trace-addr=%p, trace-name=\"%s\", " - "clock-class-name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace), - clock_class ? - bt_ctf_clock_class_get_name(clock_class) : - NULL); - ret = -1; - } - } - - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func, - struct bt_ctf_clock_class *init_expected_clock_class, - int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_field_type, - struct bt_ctf_field_type_common *event_header_field_type), - bool check_ts_begin_end_mapped) -{ - int ret; - int64_t i; - int64_t stream_id; - struct bt_ctf_validation_output trace_sc_validation_output = { 0 }; - struct bt_ctf_validation_output *ec_validation_outputs = NULL; - const enum bt_ctf_validation_flag trace_sc_validation_flags = - BT_CTF_VALIDATION_FLAG_TRACE | - BT_CTF_VALIDATION_FLAG_STREAM; - const enum bt_ctf_validation_flag ec_validation_flags = - BT_CTF_VALIDATION_FLAG_EVENT; - struct bt_ctf_field_type_common *packet_header_type = NULL; - struct bt_ctf_field_type_common *packet_context_type = NULL; - struct bt_ctf_field_type_common *event_header_type = NULL; - struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; - int64_t event_class_count; - struct bt_ctf_trace_common *current_parent_trace = NULL; - struct bt_ctf_clock_class *expected_clock_class = - bt_ctf_object_get_ref(init_expected_clock_class); - - BT_ASSERT(copy_field_type_func); - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - BT_LOGD("Adding stream class to trace: " - "trace-addr=%p, trace-name=\"%s\", " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64, - trace, bt_ctf_trace_common_get_name(trace), - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - - current_parent_trace = bt_ctf_stream_class_common_borrow_trace(stream_class); - if (current_parent_trace) { - /* Stream class is already associated to a trace, abort. */ - BT_LOGW("Invalid parameter: stream class is already part of a trace: " - "stream-class-trace-addr=%p, " - "stream-class-trace-name=\"%s\"", - current_parent_trace, - bt_ctf_trace_common_get_name(current_parent_trace)); - ret = -1; - goto end; - } - - event_class_count = - bt_ctf_stream_class_common_get_event_class_count(stream_class); - BT_ASSERT(event_class_count >= 0); - - if (!stream_class->frozen) { - /* - * Stream class is not frozen yet. Validate that the - * stream class contains at most a single clock class - * because the previous - * bt_ctf_stream_class_common_add_event_class() calls did - * not make this validation since the stream class's - * direct field types (packet context, event header, - * event context) could change afterwards. This stream - * class is about to be frozen and those field types - * won't be changed if this function succeeds. - * - * At this point we're also sure that the stream class's - * clock, if any, has the same class as the stream - * class's expected clock class, if any. This is why, if - * bt_ctf_stream_class_common_validate_single_clock_class() - * succeeds below, the call to - * bt_ctf_stream_class_map_clock_class() at the end of this - * function is safe because it maps to the same, single - * clock class. - */ - ret = bt_ctf_stream_class_common_validate_single_clock_class( - stream_class, &expected_clock_class); - if (ret) { - BT_LOGW("Invalid parameter: stream class or one of its " - "event classes contains a field type which is " - "not recursively mapped to the expected " - "clock class: " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "stream-class-name=\"%s\", " - "expected-clock-class-addr=%p, " - "expected-clock-class-name=\"%s\"", - stream_class, bt_ctf_stream_class_common_get_id(stream_class), - bt_ctf_stream_class_common_get_name(stream_class), - expected_clock_class, - expected_clock_class ? - bt_ctf_clock_class_get_name(expected_clock_class) : - NULL); - goto end; - } - } - - ret = check_packet_header_type_has_no_clock_class(trace); - if (ret) { - /* check_packet_header_type_has_no_clock_class() logs errors */ - goto end; - } - - /* - * We're about to freeze both the trace and the stream class. - * Also, each event class contained in this stream class are - * already frozen. - * - * This trace, this stream class, and all its event classes - * should be valid at this point. - * - * Validate trace and stream class first, then each event - * class of this stream class can be validated individually. - */ - packet_header_type = - bt_ctf_trace_common_borrow_packet_header_field_type(trace); - packet_context_type = - bt_ctf_stream_class_common_borrow_packet_context_field_type(stream_class); - event_header_type = - bt_ctf_stream_class_common_borrow_event_header_field_type(stream_class); - stream_event_ctx_type = - bt_ctf_stream_class_common_borrow_event_context_field_type(stream_class); - - BT_LOGD("Validating trace and stream class field types."); - ret = bt_ctf_validate_class_types(trace->environment, - packet_header_type, packet_context_type, event_header_type, - stream_event_ctx_type, NULL, NULL, trace->valid, - stream_class->valid, 1, &trace_sc_validation_output, - trace_sc_validation_flags, copy_field_type_func); - - if (ret) { - /* - * This means something went wrong during the validation - * process, not that the objects are invalid. - */ - BT_LOGE("Failed to validate trace and stream class field types: " - "ret=%d", ret); - goto end; - } - - if ((trace_sc_validation_output.valid_flags & - trace_sc_validation_flags) != - trace_sc_validation_flags) { - /* Invalid trace/stream class */ - BT_LOGW("Invalid trace or stream class field types: " - "valid-flags=0x%x", - trace_sc_validation_output.valid_flags); - ret = -1; - goto end; - } - - if (event_class_count > 0) { - ec_validation_outputs = g_new0(struct bt_ctf_validation_output, - event_class_count); - if (!ec_validation_outputs) { - BT_LOGE_STR("Failed to allocate one validation output structure."); - ret = -1; - goto end; - } - } - - /* Validate each event class individually */ - for (i = 0; i < event_class_count; i++) { - struct bt_ctf_event_class_common *event_class = - bt_ctf_stream_class_common_borrow_event_class_by_index( - stream_class, i); - struct bt_ctf_field_type_common *event_context_type = NULL; - struct bt_ctf_field_type_common *event_payload_type = NULL; - - event_context_type = - bt_ctf_event_class_common_borrow_context_field_type( - event_class); - event_payload_type = - bt_ctf_event_class_common_borrow_payload_field_type( - event_class); - - /* - * It is important to use the field types returned by - * the previous trace and stream class validation here - * because copies could have been made. - */ - BT_LOGD("Validating event class's field types: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - ret = bt_ctf_validate_class_types(trace->environment, - trace_sc_validation_output.packet_header_type, - trace_sc_validation_output.packet_context_type, - trace_sc_validation_output.event_header_type, - trace_sc_validation_output.stream_event_ctx_type, - event_context_type, event_payload_type, - 1, 1, event_class->valid, &ec_validation_outputs[i], - ec_validation_flags, copy_field_type_func); - - if (ret) { - BT_LOGE("Failed to validate event class field types: " - "ret=%d", ret); - goto end; - } - - if ((ec_validation_outputs[i].valid_flags & - ec_validation_flags) != ec_validation_flags) { - /* Invalid event class */ - BT_LOGW("Invalid event class field types: " - "valid-flags=0x%x", - ec_validation_outputs[i].valid_flags); - ret = -1; - goto end; - } - } - - stream_id = bt_ctf_stream_class_common_get_id(stream_class); - if (stream_id < 0) { - stream_id = trace->next_stream_id++; - if (stream_id < 0) { - BT_LOGE_STR("No more stream class IDs available."); - ret = -1; - goto end; - } - - /* Try to assign a new stream id */ - for (i = 0; i < trace->stream_classes->len; i++) { - if (stream_id == bt_ctf_stream_class_common_get_id( - trace->stream_classes->pdata[i])) { - /* Duplicate stream id found */ - BT_LOGW("Duplicate stream class ID: " - "id=%" PRId64, (int64_t) stream_id); - ret = -1; - goto end; - } - } - - if (bt_ctf_stream_class_common_set_id_no_check(stream_class, - stream_id)) { - /* TODO Should retry with a different stream id */ - BT_LOGE("Cannot set stream class's ID: " - "id=%" PRId64, (int64_t) stream_id); - ret = -1; - goto end; - } - } - - /* - * At this point all the field types in the validation output - * are valid. Validate the semantics of some scopes according to - * the CTF specification. - */ - if (!packet_header_field_type_is_valid(trace, - trace_sc_validation_output.packet_header_type)) { - BT_LOGW_STR("Invalid trace's packet header field type."); - ret = -1; - goto end; - } - - if (!packet_context_field_type_is_valid(trace, - stream_class, - trace_sc_validation_output.packet_context_type, - check_ts_begin_end_mapped)) { - BT_LOGW_STR("Invalid stream class's packet context field type."); - ret = -1; - goto end; - } - - if (!event_header_field_type_is_valid(trace, - stream_class, - trace_sc_validation_output.event_header_type)) { - BT_LOGW_STR("Invalid steam class's event header field type."); - ret = -1; - goto end; - } - - /* - * Now is the time to automatically map specific field types of - * the stream class's packet context and event header field - * types to the stream class's clock's class if they are not - * mapped to a clock class yet. We do it here because we know - * that after this point, everything is frozen so it won't be - * possible for the user to modify the stream class's clock, or - * to map those field types to other clock classes. - */ - if (map_clock_classes_func) { - if (map_clock_classes_func(stream_class, - trace_sc_validation_output.packet_context_type, - trace_sc_validation_output.event_header_type)) { - /* map_clock_classes_func() logs errors */ - ret = -1; - goto end; - } - } - - bt_ctf_object_set_parent(&stream_class->base, &trace->base); - g_ptr_array_add(trace->stream_classes, stream_class); - - /* - * At this point we know that the function will be successful. - * Therefore we can replace the trace and stream class field - * types with what's in their validation output structure and - * mark them as valid. We can also replace the field types of - * all the event classes of the stream class and mark them as - * valid. - */ - bt_ctf_validation_replace_types(trace, stream_class, NULL, - &trace_sc_validation_output, trace_sc_validation_flags); - trace->valid = 1; - stream_class->valid = 1; - - /* - * Put what was not moved in bt_ctf_validation_replace_types(). - */ - bt_ctf_validation_output_put_types(&trace_sc_validation_output); - - for (i = 0; i < event_class_count; i++) { - struct bt_ctf_event_class_common *event_class = - bt_ctf_stream_class_common_borrow_event_class_by_index( - stream_class, i); - - bt_ctf_validation_replace_types(NULL, NULL, event_class, - &ec_validation_outputs[i], ec_validation_flags); - event_class->valid = 1; - - /* - * Put what was not moved in - * bt_ctf_validation_replace_types(). - */ - bt_ctf_validation_output_put_types(&ec_validation_outputs[i]); - } - - /* - * Freeze the trace and the stream class. - */ - bt_ctf_stream_class_common_freeze(stream_class); - bt_ctf_trace_common_freeze(trace); - - /* - * It is safe to set the stream class's unique clock class - * now because the stream class is frozen. - */ - if (expected_clock_class) { - BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); - } - - BT_LOGD("Added stream class to trace: " - "trace-addr=%p, trace-name=\"%s\", " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64, - trace, bt_ctf_trace_common_get_name(trace), - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - -end: - if (ret) { - bt_ctf_object_set_parent(&stream_class->base, NULL); - - if (ec_validation_outputs) { - for (i = 0; i < event_class_count; i++) { - bt_ctf_validation_output_put_types( - &ec_validation_outputs[i]); - } - } - } - - g_free(ec_validation_outputs); - bt_ctf_validation_output_put_types(&trace_sc_validation_output); - bt_ctf_object_put_ref(expected_clock_class); - return ret; -} - -BT_HIDDEN -bt_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_clock_class *clock_class) -{ - struct bt_ctf_search_query query = { .value = clock_class, .found = 0 }; - - BT_ASSERT(trace); - BT_ASSERT(clock_class); - - g_ptr_array_foreach(trace->clock_classes, value_exists, &query); - return query.found; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace, - enum bt_ctf_byte_order byte_order, bool allow_unspecified) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (trace->frozen) { - BT_LOGW("Invalid parameter: trace is frozen: " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - ret = -1; - goto end; - } - - if (byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED && !allow_unspecified) { - BT_LOGW("Invalid parameter: BT_CTF_BYTE_ORDER_UNSPECIFIED byte order is not allowed: " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - ret = -1; - goto end; - } - - if (byte_order != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN && - byte_order != BT_CTF_BYTE_ORDER_BIG_ENDIAN && - byte_order != BT_CTF_BYTE_ORDER_NETWORK) { - BT_LOGW("Invalid parameter: invalid byte order: " - "addr=%p, name=\"%s\", bo=%s", - trace, bt_ctf_trace_common_get_name(trace), - bt_ctf_byte_order_string(byte_order)); - ret = -1; - goto end; - } - - trace->native_byte_order = byte_order; - BT_LOGV("Set trace's native byte order: " - "addr=%p, name=\"%s\", bo=%s", - trace, bt_ctf_trace_common_get_name(trace), - bt_ctf_byte_order_string(byte_order)); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace, - struct bt_ctf_field_type_common *packet_header_type) -{ - int ret = 0; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (trace->frozen) { - BT_LOGW("Invalid parameter: trace is frozen: " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - ret = -1; - goto end; - } - - /* packet_header_type must be a structure. */ - if (packet_header_type && - packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid parameter: packet header field type must be a structure field type if it exists: " - "addr=%p, name=\"%s\", ft-addr=%p, ft-id=%s", - trace, bt_ctf_trace_common_get_name(trace), - packet_header_type, - bt_ctf_field_type_id_string(packet_header_type->id)); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(trace->packet_header_field_type); - trace->packet_header_field_type = bt_ctf_object_get_ref(packet_header_type); - BT_LOGV("Set trace's packet header field type: " - "addr=%p, name=\"%s\", packet-context-ft-addr=%p", - trace, bt_ctf_trace_common_get_name(trace), packet_header_type); -end: - return ret; -} - -static -int64_t get_stream_class_count(void *element) -{ - return bt_ctf_trace_get_stream_class_count( - (struct bt_ctf_trace *) element); -} - -static -void *get_stream_class(void *element, int i) -{ - return bt_ctf_trace_get_stream_class_by_index( - (struct bt_ctf_trace *) element, i); -} - -static -int visit_stream_class(void *object, bt_ctf_visitor visitor,void *data) -{ - return bt_ctf_stream_class_visit(object, visitor, data); -} - -int bt_ctf_trace_visit(struct bt_ctf_trace *trace, - bt_ctf_visitor visitor, void *data) -{ - int ret; - struct bt_ctf_visitor_object obj = { - .object = trace, - .type = BT_CTF_VISITOR_OBJECT_TYPE_TRACE - }; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!visitor) { - BT_LOGW_STR("Invalid parameter: visitor is NULL."); - ret = -1; - goto end; - } - - BT_LOGV("Visiting trace: addr=%p, name=\"%s\"", - trace, bt_ctf_trace_get_name(trace)); - ret = bt_ctf_visitor_helper(&obj, get_stream_class_count, - get_stream_class, visit_stream_class, visitor, data); -end: - return ret; -} - -static -void bt_ctf_trace_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_trace *trace = (void *) obj; - - BT_LOGD("Destroying CTF writer trace object: addr=%p, name=\"%s\"", - trace, bt_ctf_trace_get_name(trace)); - bt_ctf_trace_common_finalize(BT_CTF_TO_COMMON(trace)); - g_free(trace); -} - -BT_HIDDEN -struct bt_ctf_trace *bt_ctf_trace_create(void) -{ - struct bt_ctf_trace *trace = NULL; - int ret; - - BT_LOGD_STR("Creating CTF writer trace object."); - trace = g_new0(struct bt_ctf_trace, 1); - if (!trace) { - BT_LOGE_STR("Failed to allocate one CTF writer trace."); - goto error; - } - - ret = bt_ctf_trace_common_initialize(BT_CTF_TO_COMMON(trace), - bt_ctf_trace_destroy); - if (ret) { - /* bt_ctf_trace_common_initialize() logs errors */ - goto error; - } - - BT_LOGD("Created CTF writer trace object: addr=%p", trace); - return trace; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); - return trace; -} - -const unsigned char *bt_ctf_trace_get_uuid(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_uuid(BT_CTF_TO_COMMON(trace)); -} - -int bt_ctf_trace_set_uuid(struct bt_ctf_trace *trace, - const unsigned char *uuid) -{ - return bt_ctf_trace_common_set_uuid(BT_CTF_TO_COMMON(trace), uuid); -} - -int bt_ctf_trace_set_environment_field_string(struct bt_ctf_trace *trace, - const char *name, const char *value) -{ - return bt_ctf_trace_common_set_environment_field_string(BT_CTF_TO_COMMON(trace), - name, value); -} - -int bt_ctf_trace_set_environment_field_integer( - struct bt_ctf_trace *trace, const char *name, int64_t value) -{ - return bt_ctf_trace_common_set_environment_field_integer( - BT_CTF_TO_COMMON(trace), name, value); -} - -int64_t bt_ctf_trace_get_environment_field_count(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_environment_field_count(BT_CTF_TO_COMMON(trace)); -} - -const char * -bt_ctf_trace_get_environment_field_name_by_index(struct bt_ctf_trace *trace, - uint64_t index) -{ - return bt_ctf_trace_common_get_environment_field_name_by_index( - BT_CTF_TO_COMMON(trace), index); -} - -struct bt_ctf_value *bt_ctf_trace_get_environment_field_value_by_index( - struct bt_ctf_trace *trace, uint64_t index) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_environment_field_value_by_index( - BT_CTF_TO_COMMON(trace), index)); -} - -struct bt_ctf_value *bt_ctf_trace_get_environment_field_value_by_name( - struct bt_ctf_trace *trace, const char *name) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_environment_field_value_by_name( - BT_CTF_TO_COMMON(trace), name)); -} - -BT_HIDDEN -int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace, - struct bt_ctf_clock_class *clock_class) -{ - return bt_ctf_trace_common_add_clock_class(BT_CTF_TO_COMMON(trace), - (void *) clock_class); -} - -BT_HIDDEN -int64_t bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_clock_class_count(BT_CTF_TO_COMMON(trace)); -} - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index( - struct bt_ctf_trace *trace, uint64_t index) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_clock_class_by_index( - BT_CTF_TO_COMMON(trace), index)); -} - -static -int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type) -{ - int ret = bt_ctf_stream_class_map_clock_class( - BT_CTF_FROM_COMMON(stream_class), - BT_CTF_FROM_COMMON(packet_context_type), - BT_CTF_FROM_COMMON(event_header_type)); - - if (ret) { - BT_LOGW_STR("Cannot automatically map selected stream class's field types to stream class's clock's class."); - } - - return ret; -} - -int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace, - struct bt_ctf_stream_class *stream_class) -{ - int ret = 0; - struct bt_ctf_clock_class *expected_clock_class = NULL; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - ret = -1; - goto end; - } - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->clock) { - struct bt_ctf_clock_class *stream_clock_class = - stream_class->clock->clock_class; - - /* - * Make sure this clock was also added to the - * trace (potentially through its CTF writer - * owner). - */ - size_t i; - - for (i = 0; i < trace->common.clock_classes->len; i++) { - if (trace->common.clock_classes->pdata[i] == - stream_clock_class) { - /* Found! */ - break; - } - } - - if (i == trace->common.clock_classes->len) { - /* Not found */ - BT_LOGW("Stream class's clock's class is not part of the trace: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - stream_clock_class, - bt_ctf_clock_class_get_name(stream_clock_class)); - ret = -1; - goto end; - } - - if (stream_class->common.clock_class && - stream_class->common.clock_class != - stream_class->clock->clock_class) { - /* - * Stream class already has an expected clock - * class, but it does not match its clock's - * class. - */ - BT_LOGW("Invalid parameter: stream class's clock's " - "class does not match stream class's " - "expected clock class: " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "stream-class-name=\"%s\", " - "expected-clock-class-addr=%p, " - "expected-clock-class-name=\"%s\"", - stream_class, - bt_ctf_stream_class_get_id(stream_class), - bt_ctf_stream_class_get_name(stream_class), - stream_class->common.clock_class, - bt_ctf_clock_class_get_name(stream_class->common.clock_class)); - } else if (!stream_class->common.clock_class) { - /* - * Set expected clock class to stream class's - * clock's class. - */ - expected_clock_class = stream_class->clock->clock_class; - } - } - - - ret = bt_ctf_trace_common_add_stream_class(BT_CTF_TO_COMMON(trace), - BT_CTF_TO_COMMON(stream_class), - (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy, - expected_clock_class, map_clock_classes_func, - false); - -end: - return ret; -} - -int64_t bt_ctf_trace_get_stream_count(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_stream_count(BT_CTF_TO_COMMON(trace)); -} - -struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index( - struct bt_ctf_trace *trace, uint64_t index) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_by_index( - BT_CTF_TO_COMMON(trace), index)); -} - -int64_t bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_stream_class_count(BT_CTF_TO_COMMON(trace)); -} - -struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index( - struct bt_ctf_trace *trace, uint64_t index) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_class_by_index( - BT_CTF_TO_COMMON(trace), index)); -} - -struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id( - struct bt_ctf_trace *trace, uint64_t id) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_class_by_id( - BT_CTF_TO_COMMON(trace), id)); -} - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name( - struct bt_ctf_trace *trace, const char *name) -{ - return bt_ctf_object_get_ref( - bt_ctf_trace_common_borrow_clock_class_by_name(BT_CTF_TO_COMMON(trace), - name)); -} - -static -int append_trace_metadata(struct bt_ctf_trace *trace, - struct metadata_context *context) -{ - unsigned char *uuid = trace->common.uuid; - int ret = 0; - - if (trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NATIVE || - trace->common.native_byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED) { - BT_LOGW("Invalid parameter: trace's byte order cannot be BT_CTF_BYTE_ORDER_NATIVE or BT_CTF_BYTE_ORDER_UNSPECIFIED at this point; " - "set it with bt_ctf_trace_set_native_byte_order(): " - "addr=%p, name=\"%s\"", - trace, bt_ctf_trace_get_name(trace)); - ret = -1; - goto end; - } - - g_string_append(context->string, "trace {\n"); - g_string_append(context->string, "\tmajor = 1;\n"); - g_string_append(context->string, "\tminor = 8;\n"); - BT_ASSERT(trace->common.native_byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN || - trace->common.native_byte_order == BT_CTF_BYTE_ORDER_BIG_ENDIAN || - trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NETWORK); - - if (trace->common.uuid_set) { - g_string_append_printf(context->string, - "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", - uuid[0], uuid[1], uuid[2], uuid[3], - uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], - uuid[12], uuid[13], uuid[14], uuid[15]); - } - - g_string_append_printf(context->string, "\tbyte_order = %s;\n", - bt_ctf_get_byte_order_string(trace->common.native_byte_order)); - - if (trace->common.packet_header_field_type) { - g_string_append(context->string, "\tpacket.header := "); - context->current_indentation_level++; - g_string_assign(context->field_name, ""); - BT_LOGD_STR("Serializing trace's packet header field type's metadata."); - ret = bt_ctf_field_type_serialize_recursive( - (void *) trace->common.packet_header_field_type, - context); - if (ret) { - goto end; - } - context->current_indentation_level--; - } - - g_string_append(context->string, ";\n};\n\n"); -end: - return ret; -} - -static -void append_env_metadata(struct bt_ctf_trace *trace, - struct metadata_context *context) -{ - int64_t i; - int64_t env_size; - - env_size = bt_ctf_attributes_get_count(trace->common.environment); - if (env_size <= 0) { - return; - } - - g_string_append(context->string, "env {\n"); - - for (i = 0; i < env_size; i++) { - struct bt_ctf_private_value *env_field_value_obj = NULL; - const char *entry_name; - - entry_name = bt_ctf_attributes_get_field_name( - trace->common.environment, i); - env_field_value_obj = bt_ctf_attributes_borrow_field_value( - trace->common.environment, i); - - BT_ASSERT(entry_name); - BT_ASSERT(env_field_value_obj); - - switch (bt_ctf_value_get_type( - bt_ctf_private_value_as_value(env_field_value_obj))) { - case BT_CTF_VALUE_TYPE_INTEGER: - { - int64_t int_value; - - int_value = bt_ctf_value_integer_get( - bt_ctf_private_value_as_value( - env_field_value_obj)); - g_string_append_printf(context->string, - "\t%s = %" PRId64 ";\n", entry_name, - int_value); - break; - } - case BT_CTF_VALUE_TYPE_STRING: - { - const char *str_value; - char *escaped_str = NULL; - - str_value = bt_ctf_value_string_get( - bt_ctf_private_value_as_value( - env_field_value_obj)); - escaped_str = g_strescape(str_value, NULL); - if (!escaped_str) { - BT_LOGE("Cannot escape string: string=\"%s\"", - str_value); - continue; - } - - g_string_append_printf(context->string, - "\t%s = \"%s\";\n", entry_name, escaped_str); - free(escaped_str); - break; - } - default: - continue; - } - } - - g_string_append(context->string, "};\n\n"); -} - -char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace) -{ - char *metadata = NULL; - struct metadata_context *context = NULL; - int err = 0; - size_t i; - - if (!trace) { - BT_LOGW_STR("Invalid parameter: trace is NULL."); - goto end; - } - - context = g_new0(struct metadata_context, 1); - if (!context) { - BT_LOGE_STR("Failed to allocate one metadata context."); - goto end; - } - - context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE); - context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE); - g_string_append(context->string, "/* CTF 1.8 */\n\n"); - if (append_trace_metadata(trace, context)) { - /* append_trace_metadata() logs errors */ - goto error; - } - append_env_metadata(trace, context); - g_ptr_array_foreach(trace->common.clock_classes, - (GFunc) bt_ctf_clock_class_serialize, context); - - for (i = 0; i < trace->common.stream_classes->len; i++) { - /* bt_ctf_stream_class_serialize() logs details */ - err = bt_ctf_stream_class_serialize( - trace->common.stream_classes->pdata[i], context); - if (err) { - /* bt_ctf_stream_class_serialize() logs errors */ - goto error; - } - } - - metadata = context->string->str; - -error: - g_string_free(context->string, err ? TRUE : FALSE); - g_string_free(context->field_name, TRUE); - g_free(context); - -end: - return metadata; -} - -enum bt_ctf_byte_order bt_ctf_trace_get_native_byte_order( - struct bt_ctf_trace *trace) -{ - return (int) bt_ctf_trace_common_get_native_byte_order(BT_CTF_TO_COMMON(trace)); -} - -int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace, - enum bt_ctf_byte_order byte_order) -{ - return bt_ctf_trace_common_set_native_byte_order(BT_CTF_TO_COMMON(trace), - (int) byte_order, false); -} - -struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_field_type( - struct bt_ctf_trace *trace) -{ - return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_packet_header_field_type( - BT_CTF_TO_COMMON(trace))); -} - -int bt_ctf_trace_set_packet_header_field_type(struct bt_ctf_trace *trace, - struct bt_ctf_field_type *packet_header_type) -{ - return bt_ctf_trace_common_set_packet_header_field_type(BT_CTF_TO_COMMON(trace), - (void *) packet_header_type); -} - -const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace) -{ - return bt_ctf_trace_common_get_name(BT_CTF_TO_COMMON(trace)); -} diff --git a/ctf-writer/utils.c b/ctf-writer/utils.c deleted file mode 100644 index fdf0a933..00000000 --- a/ctf-writer/utils.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * utils.c - * - * Babeltrace CTF writer - Utilities - * - * Copyright 2015 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-UTILS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -static -const char * const reserved_keywords_str[] = {"align", "callsite", - "const", "char", "clock", "double", "enum", "env", "event", - "floating_point", "float", "integer", "int", "long", "short", "signed", - "stream", "string", "struct", "trace", "typealias", "typedef", - "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"}; - -static GHashTable *reserved_keywords_set; -static int init_done; - -static -void try_init_reserved_keywords(void) -{ - size_t i; - const size_t reserved_keywords_count = - sizeof(reserved_keywords_str) / sizeof(char *); - - if (reserved_keywords_set) { - return; - } - - reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal); - BT_ASSERT(reserved_keywords_set); - - for (i = 0; i < reserved_keywords_count; i++) { - gpointer quark = GINT_TO_POINTER(g_quark_from_string( - reserved_keywords_str[i])); - - g_hash_table_insert(reserved_keywords_set, quark, quark); - } - - init_done = 1; -} - -static __attribute__((destructor)) -void trace_finalize(void) -{ - if (reserved_keywords_set) { - g_hash_table_destroy(reserved_keywords_set); - } -} - -bt_bool bt_ctf_identifier_is_valid(const char *identifier) -{ - bt_bool is_valid = BT_TRUE; - char *string = NULL; - char *save_ptr, *token; - - if (!identifier) { - BT_LOGV_STR("Invalid parameter: input string is NULL."); - is_valid = BT_FALSE; - goto end; - } - - try_init_reserved_keywords(); - - if (identifier[0] == '\0') { - is_valid = BT_FALSE; - goto end; - } - - string = strdup(identifier); - if (!string) { - BT_LOGE("strdup() failed."); - is_valid = BT_FALSE; - goto end; - } - - token = strtok_r(string, " ", &save_ptr); - while (token) { - if (g_hash_table_lookup_extended(reserved_keywords_set, - GINT_TO_POINTER(g_quark_from_string(token)), - NULL, NULL)) { - is_valid = BT_FALSE; - goto end; - } - - token = strtok_r(NULL, " ", &save_ptr); - } -end: - free(string); - return is_valid; -} diff --git a/ctf-writer/validation.c b/ctf-writer/validation.c deleted file mode 100644 index 9c4276eb..00000000 --- a/ctf-writer/validation.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * validation.c - * - * Babeltrace - CTF writer: Validation of trace, stream class, and event class - * - * Copyright 2016-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-VALIDATION" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * This function resolves and validates the field types of an event - * class. Only `event_context_type` and `event_payload_type` are - * resolved and validated; the other field types are used as eventual - * resolving targets. - * - * All parameters are owned by the caller. - */ -static -int validate_event_class_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type, - struct bt_ctf_field_type_common *event_context_type, - struct bt_ctf_field_type_common *event_payload_type) -{ - int ret = 0; - - BT_LOGV("Validating event class field types: " - "packet-header-ft-addr=%p, " - "packet-context-ft-addr=%p, " - "event-header-ft-addr=%p, " - "stream-event-context-ft-addr=%p, " - "event-context-ft-addr=%p, " - "event-payload-ft-addr=%p", - packet_header_type, packet_context_type, event_header_type, - stream_event_ctx_type, event_context_type, event_payload_type); - - /* Resolve sequence type lengths and variant type tags first */ - ret = bt_ctf_resolve_types(environment, packet_header_type, - packet_context_type, event_header_type, stream_event_ctx_type, - event_context_type, event_payload_type, - BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT | - BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD); - if (ret) { - BT_LOGW("Cannot resolve event class field types: ret=%d", - ret); - goto end; - } - - /* Validate field types individually */ - if (event_context_type) { - ret = bt_ctf_field_type_common_validate(event_context_type); - if (ret) { - BT_LOGW("Invalid event class's context field type: " - "ret=%d", ret); - goto end; - } - } - - if (event_payload_type) { - ret = bt_ctf_field_type_common_validate(event_payload_type); - if (ret) { - BT_LOGW("Invalid event class's payload field type: " - "ret=%d", ret); - goto end; - } - } - -end: - return ret; -} - -/* - * This function resolves and validates the field types of a stream - * class. Only `packet_context_type`, `event_header_type`, and - * `stream_event_ctx_type` are resolved and validated; the other field - * type is used as an eventual resolving target. - * - * All parameters are owned by the caller. - */ -static -int validate_stream_class_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type) -{ - int ret = 0; - - BT_LOGV("Validating stream class field types: " - "packet-header-ft-addr=%p, " - "packet-context-ft-addr=%p, " - "event-header-ft-addr=%p, " - "stream-event-context-ft-addr=%p", - packet_header_type, packet_context_type, event_header_type, - stream_event_ctx_type); - - /* Resolve sequence type lengths and variant type tags first */ - ret = bt_ctf_resolve_types(environment, packet_header_type, - packet_context_type, event_header_type, stream_event_ctx_type, - NULL, NULL, - BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT | - BT_CTF_RESOLVE_FLAG_EVENT_HEADER | - BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX); - if (ret) { - BT_LOGW("Cannot resolve stream class field types: ret=%d", - ret); - goto end; - } - - /* Validate field types individually */ - if (packet_context_type) { - ret = bt_ctf_field_type_common_validate(packet_context_type); - if (ret) { - BT_LOGW("Invalid stream class's packet context field type: " - "ret=%d", ret); - goto end; - } - } - - if (event_header_type) { - ret = bt_ctf_field_type_common_validate(event_header_type); - if (ret) { - BT_LOGW("Invalid stream class's event header field type: " - "ret=%d", ret); - goto end; - } - } - - if (stream_event_ctx_type) { - ret = bt_ctf_field_type_common_validate( - stream_event_ctx_type); - if (ret) { - BT_LOGW("Invalid stream class's event context field type: " - "ret=%d", ret); - goto end; - } - } - -end: - return ret; -} - -/* - * This function resolves and validates the field types of a trace. - * - * All parameters are owned by the caller. - */ -static -int validate_trace_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type) -{ - int ret = 0; - - BT_LOGV("Validating event class field types: " - "packet-header-ft-addr=%p", packet_header_type); - - /* Resolve sequence type lengths and variant type tags first */ - ret = bt_ctf_resolve_types(environment, packet_header_type, - NULL, NULL, NULL, NULL, NULL, - BT_CTF_RESOLVE_FLAG_PACKET_HEADER); - if (ret) { - BT_LOGW("Cannot resolve trace field types: ret=%d", - ret); - goto end; - } - - /* Validate field types individually */ - if (packet_header_type) { - ret = bt_ctf_field_type_common_validate(packet_header_type); - if (ret) { - BT_LOGW("Invalid trace's packet header field type: " - "ret=%d", ret); - goto end; - } - } - -end: - return ret; -} - -/* - * Checks whether or not `field_type` contains a variant or a sequence - * field type, recursively. Returns 1 if it's the case. - * - * `field_type` is owned by the caller. - */ -static -int field_type_contains_sequence_or_variant_ft(struct bt_ctf_field_type_common *type) -{ - int ret = 0; - enum bt_ctf_field_type_id type_id = bt_ctf_field_type_common_get_type_id(type); - - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - case BT_CTF_FIELD_TYPE_ID_VARIANT: - ret = 1; - goto end; - case BT_CTF_FIELD_TYPE_ID_ARRAY: - case BT_CTF_FIELD_TYPE_ID_STRUCT: - { - int i; - int field_count = bt_ctf_field_type_common_get_field_count(type); - - if (field_count < 0) { - ret = -1; - goto end; - } - - for (i = 0; i < field_count; ++i) { - struct bt_ctf_field_type_common *child_type = - bt_ctf_field_type_common_borrow_field_at_index( - type, i); - - ret = field_type_contains_sequence_or_variant_ft( - child_type); - if (ret != 0) { - goto end; - } - } - break; - } - default: - break; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type, - struct bt_ctf_field_type_common *event_context_type, - struct bt_ctf_field_type_common *event_payload_type, - int trace_valid, int stream_class_valid, int event_class_valid, - struct bt_ctf_validation_output *output, - enum bt_ctf_validation_flag validate_flags, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) -{ - int ret = 0; - int contains_seq_var; - int valid_ret; - - BT_LOGV("Validating field types: " - "packet-header-ft-addr=%p, " - "packet-context-ft-addr=%p, " - "event-header-ft-addr=%p, " - "stream-event-context-ft-addr=%p, " - "event-context-ft-addr=%p, " - "event-payload-ft-addr=%p, " - "trace-is-valid=%d, stream-class-is-valid=%d, " - "event-class-is-valid=%d, validation-flags=%x", - packet_header_type, packet_context_type, event_header_type, - stream_event_ctx_type, event_context_type, event_payload_type, - trace_valid, stream_class_valid, event_class_valid, - (unsigned int) validate_flags); - - /* Clean output values */ - memset(output, 0, sizeof(*output)); - - /* Set initial valid flags according to valid parameters */ - if (trace_valid) { - output->valid_flags |= BT_CTF_VALIDATION_FLAG_TRACE; - } - - if (stream_class_valid) { - output->valid_flags |= BT_CTF_VALIDATION_FLAG_STREAM; - } - - if (event_class_valid) { - output->valid_flags |= BT_CTF_VALIDATION_FLAG_EVENT; - } - - /* Own the type parameters */ - bt_ctf_object_get_ref(packet_header_type); - bt_ctf_object_get_ref(packet_context_type); - bt_ctf_object_get_ref(event_header_type); - bt_ctf_object_get_ref(stream_event_ctx_type); - bt_ctf_object_get_ref(event_context_type); - bt_ctf_object_get_ref(event_payload_type); - - /* Validate trace */ - if ((validate_flags & BT_CTF_VALIDATION_FLAG_TRACE) && !trace_valid) { - struct bt_ctf_field_type_common *packet_header_type_copy = NULL; - - /* Create field type copies */ - if (packet_header_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - packet_header_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - packet_header_type_copy = packet_header_type; - bt_ctf_object_get_ref(packet_header_type_copy); - goto skip_packet_header_type_copy; - } - - BT_LOGV_STR("Copying packet header field type because it contains at least one sequence or variant field type."); - packet_header_type_copy = - copy_field_type_func(packet_header_type); - if (!packet_header_type_copy) { - ret = -1; - BT_LOGE_STR("Cannot copy packet header field type."); - goto error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(packet_header_type_copy); - } - -skip_packet_header_type_copy: - /* Put original reference and move copy */ - BT_CTF_OBJECT_MOVE_REF(packet_header_type, packet_header_type_copy); - - /* Validate trace field types */ - valid_ret = validate_trace_types(environment, - packet_header_type); - if (valid_ret == 0) { - /* Trace is valid */ - output->valid_flags |= BT_CTF_VALIDATION_FLAG_TRACE; - } - } - - /* Validate stream class */ - if ((validate_flags & BT_CTF_VALIDATION_FLAG_STREAM) && - !stream_class_valid) { - struct bt_ctf_field_type_common *packet_context_type_copy = NULL; - struct bt_ctf_field_type_common *event_header_type_copy = NULL; - struct bt_ctf_field_type_common *stream_event_ctx_type_copy = NULL; - - if (packet_context_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - packet_context_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - packet_context_type_copy = packet_context_type; - bt_ctf_object_get_ref(packet_context_type_copy); - goto skip_packet_context_type_copy; - } - - BT_LOGV_STR("Copying packet context field type because it contains at least one sequence or variant field type."); - packet_context_type_copy = - copy_field_type_func(packet_context_type); - if (!packet_context_type_copy) { - BT_LOGE_STR("Cannot copy packet context field type."); - goto sc_validation_error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(packet_context_type_copy); - } - -skip_packet_context_type_copy: - if (event_header_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - event_header_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - event_header_type_copy = event_header_type; - bt_ctf_object_get_ref(event_header_type_copy); - goto skip_event_header_type_copy; - } - - BT_LOGV_STR("Copying event header field type because it contains at least one sequence or variant field type."); - event_header_type_copy = - copy_field_type_func(event_header_type); - if (!event_header_type_copy) { - BT_LOGE_STR("Cannot copy event header field type."); - goto sc_validation_error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(event_header_type_copy); - } - -skip_event_header_type_copy: - if (stream_event_ctx_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - stream_event_ctx_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - stream_event_ctx_type_copy = - stream_event_ctx_type; - bt_ctf_object_get_ref(stream_event_ctx_type_copy); - goto skip_stream_event_ctx_type_copy; - } - - BT_LOGV_STR("Copying stream event context field type because it contains at least one sequence or variant field type."); - stream_event_ctx_type_copy = - copy_field_type_func(stream_event_ctx_type); - if (!stream_event_ctx_type_copy) { - BT_LOGE_STR("Cannot copy stream event context field type."); - goto sc_validation_error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(stream_event_ctx_type_copy); - } - -skip_stream_event_ctx_type_copy: - /* Put original references and move copies */ - BT_CTF_OBJECT_MOVE_REF(packet_context_type, packet_context_type_copy); - BT_CTF_OBJECT_MOVE_REF(event_header_type, event_header_type_copy); - BT_CTF_OBJECT_MOVE_REF(stream_event_ctx_type, stream_event_ctx_type_copy); - - /* Validate stream class field types */ - valid_ret = validate_stream_class_types(environment, - packet_header_type, packet_context_type, - event_header_type, stream_event_ctx_type); - if (valid_ret == 0) { - /* Stream class is valid */ - output->valid_flags |= BT_CTF_VALIDATION_FLAG_STREAM; - } - - goto sc_validation_done; - -sc_validation_error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_header_type_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_type_copy); - ret = -1; - goto error; - } - -sc_validation_done: - /* Validate event class */ - if ((validate_flags & BT_CTF_VALIDATION_FLAG_EVENT) && - !event_class_valid) { - struct bt_ctf_field_type_common *event_context_type_copy = NULL; - struct bt_ctf_field_type_common *event_payload_type_copy = NULL; - - if (event_context_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - event_context_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - event_context_type_copy = event_context_type; - bt_ctf_object_get_ref(event_context_type_copy); - goto skip_event_context_type_copy; - } - - BT_LOGV_STR("Copying event context field type because it contains at least one sequence or variant field type."); - event_context_type_copy = - copy_field_type_func(event_context_type); - if (!event_context_type_copy) { - BT_LOGE_STR("Cannot copy event context field type."); - goto ec_validation_error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(event_context_type_copy); - } - -skip_event_context_type_copy: - if (event_payload_type) { - contains_seq_var = - field_type_contains_sequence_or_variant_ft( - event_payload_type); - if (contains_seq_var < 0) { - ret = contains_seq_var; - goto error; - } else if (!contains_seq_var) { - /* No copy is needed */ - event_payload_type_copy = event_payload_type; - bt_ctf_object_get_ref(event_payload_type_copy); - goto skip_event_payload_type_copy; - } - - BT_LOGV_STR("Copying event payload field type because it contains at least one sequence or variant field type."); - event_payload_type_copy = - copy_field_type_func(event_payload_type); - if (!event_payload_type_copy) { - BT_LOGE_STR("Cannot copy event payload field type."); - goto ec_validation_error; - } - - /* - * Freeze this copy: if it's returned to the - * caller, it cannot be modified any way since - * it will be resolved. - */ - bt_ctf_field_type_common_freeze(event_payload_type_copy); - } - -skip_event_payload_type_copy: - /* Put original references and move copies */ - BT_CTF_OBJECT_MOVE_REF(event_context_type, event_context_type_copy); - BT_CTF_OBJECT_MOVE_REF(event_payload_type, event_payload_type_copy); - - /* Validate event class field types */ - valid_ret = validate_event_class_types(environment, - packet_header_type, packet_context_type, - event_header_type, stream_event_ctx_type, - event_context_type, event_payload_type); - if (valid_ret == 0) { - /* Event class is valid */ - output->valid_flags |= BT_CTF_VALIDATION_FLAG_EVENT; - } - - goto ec_validation_done; - -ec_validation_error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type_copy); - ret = -1; - goto error; - } - -ec_validation_done: - /* - * Validation is complete. Move the field types that were used - * to validate (and that were possibly altered by the validation - * process) to the output values. - */ - BT_CTF_OBJECT_MOVE_REF(output->packet_header_type, packet_header_type); - BT_CTF_OBJECT_MOVE_REF(output->packet_context_type, packet_context_type); - BT_CTF_OBJECT_MOVE_REF(output->event_header_type, event_header_type); - BT_CTF_OBJECT_MOVE_REF(output->stream_event_ctx_type, stream_event_ctx_type); - BT_CTF_OBJECT_MOVE_REF(output->event_context_type, event_context_type); - BT_CTF_OBJECT_MOVE_REF(output->event_payload_type, event_payload_type); - return ret; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_header_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_header_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type); - return ret; -} - -BT_HIDDEN -void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_validation_output *output, - enum bt_ctf_validation_flag replace_flags) -{ - if ((replace_flags & BT_CTF_VALIDATION_FLAG_TRACE) && trace) { - bt_ctf_field_type_common_freeze(trace->packet_header_field_type); - BT_CTF_OBJECT_MOVE_REF(trace->packet_header_field_type, - output->packet_header_type); - } - - if ((replace_flags & BT_CTF_VALIDATION_FLAG_STREAM) && stream_class) { - bt_ctf_field_type_common_freeze(stream_class->packet_context_field_type); - bt_ctf_field_type_common_freeze(stream_class->event_header_field_type); - bt_ctf_field_type_common_freeze(stream_class->event_context_field_type); - BT_CTF_OBJECT_MOVE_REF(stream_class->packet_context_field_type, - output->packet_context_type); - BT_CTF_OBJECT_MOVE_REF(stream_class->event_header_field_type, - output->event_header_type); - BT_CTF_OBJECT_MOVE_REF(stream_class->event_context_field_type, - output->stream_event_ctx_type); - } - - if ((replace_flags & BT_CTF_VALIDATION_FLAG_EVENT) && event_class) { - bt_ctf_field_type_common_freeze(event_class->context_field_type); - bt_ctf_field_type_common_freeze(event_class->payload_field_type); - BT_CTF_OBJECT_MOVE_REF(event_class->context_field_type, output->event_context_type); - BT_CTF_OBJECT_MOVE_REF(event_class->payload_field_type, output->event_payload_type); - } -} - -BT_HIDDEN -void bt_ctf_validation_output_put_types( - struct bt_ctf_validation_output *output) -{ - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->packet_header_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->packet_context_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_header_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->stream_event_ctx_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_context_type); - BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_payload_type); -} diff --git a/ctf-writer/values.c b/ctf-writer/values.c deleted file mode 100644 index dd8ae949..00000000 --- a/ctf-writer/values.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER-VALUES" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_CTF_VALUE_FROM_CONCRETE(_concrete) ((struct bt_ctf_value *) (_concrete)) -#define BT_CTF_VALUE_TO_BOOL(_base) ((struct bt_ctf_value_bool *) (_base)) -#define BT_CTF_VALUE_TO_INTEGER(_base) ((struct bt_ctf_value_integer *) (_base)) -#define BT_CTF_VALUE_TO_REAL(_base) ((struct bt_ctf_value_real *) (_base)) -#define BT_CTF_VALUE_TO_STRING(_base) ((struct bt_ctf_value_string *) (_base)) -#define BT_CTF_VALUE_TO_ARRAY(_base) ((struct bt_ctf_value_array *) (_base)) -#define BT_CTF_VALUE_TO_MAP(_base) ((struct bt_ctf_value_map *) (_base)) - -#define BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(_value, _type) \ - BT_CTF_ASSERT_PRE(((struct bt_ctf_value *) (_value))->type == (_type), \ - "Value has the wrong type ID: expected-type=%d", (_type)) - -#define BT_CTF_ASSERT_PRE_VALUE_HOT(_value, _name) \ - BT_CTF_ASSERT_PRE_HOT(((struct bt_ctf_value *) (_value)), (_name), "") - -#define BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(_index, _count) \ - BT_CTF_ASSERT_PRE((_index) < (_count), \ - "Index is out of bound: " \ - "index=%" PRIu64 ", count=%u", (_index), (_count)); - -struct bt_ctf_value { - struct bt_ctf_object base; - enum bt_ctf_value_type type; - bt_bool frozen; -}; - -static -void bt_ctf_value_null_instance_release_func(struct bt_ctf_object *obj) -{ - BT_LOGW("Releasing the null value singleton: addr=%p", obj); -} - -static -struct bt_ctf_value bt_ctf_value_null_instance = { - .base = { - .is_shared = true, - .ref_count = 1, - .release_func = bt_ctf_value_null_instance_release_func, - .spec_release_func = NULL, - .parent_is_owner_listener_func = NULL, - .parent = NULL, - }, - .type = BT_CTF_VALUE_TYPE_NULL, - .frozen = BT_TRUE, -}; - -struct bt_ctf_value *const bt_ctf_value_null = &bt_ctf_value_null_instance; -struct bt_ctf_private_value *const bt_ctf_private_value_null = - (void *) &bt_ctf_value_null_instance; - -struct bt_ctf_value_bool { - struct bt_ctf_value base; - bt_bool value; -}; - -struct bt_ctf_value_integer { - struct bt_ctf_value base; - int64_t value; -}; - -struct bt_ctf_value_real { - struct bt_ctf_value base; - double value; -}; - -struct bt_ctf_value_string { - struct bt_ctf_value base; - GString *gstr; -}; - -struct bt_ctf_value_array { - struct bt_ctf_value base; - GPtrArray *garray; -}; - -struct bt_ctf_value_map { - struct bt_ctf_value base; - GHashTable *ght; -}; - -static -void bt_ctf_value_destroy(struct bt_ctf_object *obj); - -static -void bt_ctf_value_string_destroy(struct bt_ctf_value *object) -{ - g_string_free(BT_CTF_VALUE_TO_STRING(object)->gstr, TRUE); - BT_CTF_VALUE_TO_STRING(object)->gstr = NULL; -} - -static -void bt_ctf_value_array_destroy(struct bt_ctf_value *object) -{ - /* - * Pointer array's registered value destructor will take care - * of putting each contained object. - */ - g_ptr_array_free(BT_CTF_VALUE_TO_ARRAY(object)->garray, TRUE); - BT_CTF_VALUE_TO_ARRAY(object)->garray = NULL; -} - -static -void bt_ctf_value_map_destroy(struct bt_ctf_value *object) -{ - /* - * Hash table's registered value destructor will take care of - * putting each contained object. Keys are GQuarks and cannot - * be destroyed anyway. - */ - g_hash_table_destroy(BT_CTF_VALUE_TO_MAP(object)->ght); - BT_CTF_VALUE_TO_MAP(object)->ght = NULL; -} - -static -void (* const destroy_funcs[])(struct bt_ctf_value *) = { - [BT_CTF_VALUE_TYPE_NULL] = NULL, - [BT_CTF_VALUE_TYPE_BOOL] = NULL, - [BT_CTF_VALUE_TYPE_INTEGER] = NULL, - [BT_CTF_VALUE_TYPE_REAL] = NULL, - [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_destroy, - [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_destroy, - [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_destroy, -}; - -static -struct bt_ctf_private_value *bt_ctf_value_null_copy(const struct bt_ctf_value *null_obj) -{ - return (void *) bt_ctf_value_null; -} - -static -struct bt_ctf_private_value *bt_ctf_value_bool_copy(const struct bt_ctf_value *bool_obj) -{ - return bt_ctf_private_value_bool_create_init( - BT_CTF_VALUE_TO_BOOL(bool_obj)->value); -} - -static -struct bt_ctf_private_value *bt_ctf_value_integer_copy( - const struct bt_ctf_value *integer_obj) -{ - return bt_ctf_private_value_integer_create_init( - BT_CTF_VALUE_TO_INTEGER(integer_obj)->value); -} - -static -struct bt_ctf_private_value *bt_ctf_value_real_copy(const struct bt_ctf_value *real_obj) -{ - return bt_ctf_private_value_real_create_init( - BT_CTF_VALUE_TO_REAL(real_obj)->value); -} - -static -struct bt_ctf_private_value *bt_ctf_value_string_copy(const struct bt_ctf_value *string_obj) -{ - return bt_ctf_private_value_string_create_init( - BT_CTF_VALUE_TO_STRING(string_obj)->gstr->str); -} - -static -struct bt_ctf_private_value *bt_ctf_value_array_copy(const struct bt_ctf_value *array_obj) -{ - int i; - int ret; - struct bt_ctf_private_value *copy_obj; - struct bt_ctf_value_array *typed_array_obj; - - BT_LOGD("Copying array value: addr=%p", array_obj); - typed_array_obj = BT_CTF_VALUE_TO_ARRAY(array_obj); - copy_obj = bt_ctf_private_value_array_create(); - if (!copy_obj) { - BT_LOGE_STR("Cannot create empty array value."); - goto end; - } - - for (i = 0; i < typed_array_obj->garray->len; ++i) { - struct bt_ctf_private_value *element_obj_copy = NULL; - struct bt_ctf_value *element_obj = - bt_ctf_value_array_borrow_element_by_index( - array_obj, i); - - BT_ASSERT(element_obj); - BT_LOGD("Copying array value's element: element-addr=%p, " - "index=%d", element_obj, i); - ret = bt_ctf_value_copy(&element_obj_copy, element_obj); - if (ret) { - BT_LOGE("Cannot copy array value's element: " - "array-addr=%p, index=%d", - array_obj, i); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - - BT_ASSERT(element_obj_copy); - ret = bt_ctf_private_value_array_append_element(copy_obj, - (void *) element_obj_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(element_obj_copy); - if (ret) { - BT_LOGE("Cannot append to array value: addr=%p", - array_obj); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - } - - BT_LOGD("Copied array value: original-addr=%p, copy-addr=%p", - array_obj, copy_obj); - -end: - return copy_obj; -} - -static -struct bt_ctf_private_value *bt_ctf_value_map_copy(const struct bt_ctf_value *map_obj) -{ - int ret; - GHashTableIter iter; - gpointer key, element_obj; - struct bt_ctf_private_value *copy_obj; - struct bt_ctf_private_value *element_obj_copy = NULL; - struct bt_ctf_value_map *typed_map_obj; - - BT_LOGD("Copying map value: addr=%p", map_obj); - typed_map_obj = BT_CTF_VALUE_TO_MAP(map_obj); - copy_obj = bt_ctf_private_value_map_create(); - if (!copy_obj) { - goto end; - } - - g_hash_table_iter_init(&iter, typed_map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - BT_ASSERT(key_str); - BT_LOGD("Copying map value's element: element-addr=%p, " - "key=\"%s\"", element_obj, key_str); - ret = bt_ctf_value_copy(&element_obj_copy, element_obj); - if (ret) { - BT_LOGE("Cannot copy map value's element: " - "map-addr=%p, key=\"%s\"", - map_obj, key_str); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - - BT_ASSERT(element_obj_copy); - ret = bt_ctf_private_value_map_insert_entry(copy_obj, key_str, - (void *) element_obj_copy); - BT_CTF_OBJECT_PUT_REF_AND_RESET(element_obj_copy); - if (ret) { - BT_LOGE("Cannot insert into map value: addr=%p, key=\"%s\"", - map_obj, key_str); - BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - } - - BT_LOGD("Copied map value: addr=%p", map_obj); - -end: - return copy_obj; -} - -static -struct bt_ctf_private_value *(* const copy_funcs[])(const struct bt_ctf_value *) = { - [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_copy, - [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_bool_copy, - [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_integer_copy, - [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_real_copy, - [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_copy, - [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_copy, - [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_copy, -}; - -static -bt_bool bt_ctf_value_null_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - /* - * Always BT_TRUE since bt_ctf_value_compare() already checks if both - * object_a and object_b have the same type, and in the case of - * null value objects, they're always the same if it is so. - */ - return BT_TRUE; -} - -static -bt_bool bt_ctf_value_bool_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - if (BT_CTF_VALUE_TO_BOOL(object_a)->value != - BT_CTF_VALUE_TO_BOOL(object_b)->value) { - BT_LOGV("Boolean value objects are different: " - "bool-a-val=%d, bool-b-val=%d", - BT_CTF_VALUE_TO_BOOL(object_a)->value, - BT_CTF_VALUE_TO_BOOL(object_b)->value); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_ctf_value_integer_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - if (BT_CTF_VALUE_TO_INTEGER(object_a)->value != - BT_CTF_VALUE_TO_INTEGER(object_b)->value) { - BT_LOGV("Integer value objects are different: " - "int-a-val=%" PRId64 ", int-b-val=%" PRId64, - BT_CTF_VALUE_TO_INTEGER(object_a)->value, - BT_CTF_VALUE_TO_INTEGER(object_b)->value); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_ctf_value_real_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - if (BT_CTF_VALUE_TO_REAL(object_a)->value != - BT_CTF_VALUE_TO_REAL(object_b)->value) { - BT_LOGV("Real number value objects are different: " - "real-a-val=%f, real-b-val=%f", - BT_CTF_VALUE_TO_REAL(object_a)->value, - BT_CTF_VALUE_TO_REAL(object_b)->value); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_ctf_value_string_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - if (strcmp(BT_CTF_VALUE_TO_STRING(object_a)->gstr->str, - BT_CTF_VALUE_TO_STRING(object_b)->gstr->str) != 0) { - BT_LOGV("String value objects are different: " - "string-a-val=\"%s\", string-b-val=\"%s\"", - BT_CTF_VALUE_TO_STRING(object_a)->gstr->str, - BT_CTF_VALUE_TO_STRING(object_b)->gstr->str); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_ctf_value_array_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - int i; - bt_bool ret = BT_TRUE; - const struct bt_ctf_value_array *array_obj_a = - BT_CTF_VALUE_TO_ARRAY(object_a); - - if (bt_ctf_value_array_get_size(object_a) != - bt_ctf_value_array_get_size(object_b)) { - BT_LOGV("Array values are different: size mismatch " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-size=%" PRId64 ", value-b-size=%" PRId64, - object_a, object_b, - bt_ctf_value_array_get_size(object_a), - bt_ctf_value_array_get_size(object_b)); - ret = BT_FALSE; - goto end; - } - - for (i = 0; i < array_obj_a->garray->len; ++i) { - struct bt_ctf_value *element_obj_a; - struct bt_ctf_value *element_obj_b; - - element_obj_a = bt_ctf_value_array_borrow_element_by_index( - object_a, i); - element_obj_b = bt_ctf_value_array_borrow_element_by_index( - object_b, i); - - if (!bt_ctf_value_compare(element_obj_a, element_obj_b)) { - BT_LOGV("Array values's elements are different: " - "value-a-addr=%p, value-b-addr=%p, index=%d", - element_obj_a, element_obj_b, i); - ret = BT_FALSE; - goto end; - } - } - -end: - return ret; -} - -static -bt_bool bt_ctf_value_map_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - bt_bool ret = BT_TRUE; - GHashTableIter iter; - gpointer key, element_obj_a; - const struct bt_ctf_value_map *map_obj_a = BT_CTF_VALUE_TO_MAP(object_a); - - if (bt_ctf_value_map_get_size(object_a) != - bt_ctf_value_map_get_size(object_b)) { - BT_LOGV("Map values are different: size mismatch " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-size=%" PRId64 ", value-b-size=%" PRId64, - object_a, object_b, - bt_ctf_value_map_get_size(object_a), - bt_ctf_value_map_get_size(object_b)); - ret = BT_FALSE; - goto end; - } - - g_hash_table_iter_init(&iter, map_obj_a->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj_a)) { - struct bt_ctf_value *element_obj_b; - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - element_obj_b = bt_ctf_value_map_borrow_entry_value(object_b, - key_str); - - if (!bt_ctf_value_compare(element_obj_a, element_obj_b)) { - BT_LOGV("Map values's elements are different: " - "value-a-addr=%p, value-b-addr=%p, key=\"%s\"", - element_obj_a, element_obj_b, key_str); - ret = BT_FALSE; - goto end; - } - } - -end: - return ret; -} - -static -bt_bool (* const compare_funcs[])(const struct bt_ctf_value *, - const struct bt_ctf_value *) = { - [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_compare, - [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_bool_compare, - [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_integer_compare, - [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_real_compare, - [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_compare, - [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_compare, - [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_compare, -}; - -static -void bt_ctf_value_null_freeze(struct bt_ctf_value *object) -{ -} - -static -void bt_ctf_value_generic_freeze(struct bt_ctf_value *object) -{ - object->frozen = BT_TRUE; -} - -static -void bt_ctf_value_array_freeze(struct bt_ctf_value *object) -{ - int i; - struct bt_ctf_value_array *typed_array_obj = - BT_CTF_VALUE_TO_ARRAY(object); - - for (i = 0; i < typed_array_obj->garray->len; ++i) { - bt_ctf_value_freeze(g_ptr_array_index(typed_array_obj->garray, i)); - } - - bt_ctf_value_generic_freeze(object); -} - -static -void bt_ctf_value_map_freeze(struct bt_ctf_value *object) -{ - GHashTableIter iter; - gpointer key, element_obj; - const struct bt_ctf_value_map *map_obj = BT_CTF_VALUE_TO_MAP(object); - - g_hash_table_iter_init(&iter, map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - bt_ctf_value_freeze(element_obj); - } - - bt_ctf_value_generic_freeze(object); -} - -static -void (* const freeze_funcs[])(struct bt_ctf_value *) = { - [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_freeze, - [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_generic_freeze, - [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_generic_freeze, - [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_generic_freeze, - [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_generic_freeze, - [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_freeze, - [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_freeze, -}; - -static -void bt_ctf_value_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_value *value; - - value = container_of(obj, struct bt_ctf_value, base); - BT_LOGD("Destroying value: addr=%p", value); - - if (bt_ctf_value_is_null(value)) { - BT_LOGD_STR("Not destroying the null value singleton."); - return; - } - - if (destroy_funcs[value->type]) { - destroy_funcs[value->type](value); - } - - g_free(value); -} - -BT_HIDDEN -enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object) -{ - enum bt_ctf_value_status ret = BT_CTF_VALUE_STATUS_OK; - - BT_ASSERT(object); - - if (object->frozen) { - goto end; - } - - BT_LOGD("Freezing value: addr=%p", object); - freeze_funcs[object->type](object); - -end: - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object) -{ - BT_CTF_ASSERT_PRE_NON_NULL(object, "Value object"); - return object->type; -} - -static -struct bt_ctf_value bt_ctf_value_create_base(enum bt_ctf_value_type type) -{ - struct bt_ctf_value value; - - value.type = type; - value.frozen = BT_FALSE; - bt_ctf_object_init_shared(&value.base, bt_ctf_value_destroy); - return value; -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_bool val) -{ - struct bt_ctf_value_bool *bool_obj; - - BT_LOGD("Creating boolean value object: val=%d", val); - bool_obj = g_new0(struct bt_ctf_value_bool, 1); - if (!bool_obj) { - BT_LOGE_STR("Failed to allocate one boolean value object."); - goto end; - } - - bool_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_BOOL); - bool_obj->value = val; - BT_LOGD("Created boolean value object: addr=%p", bool_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(bool_obj); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void) -{ - return bt_ctf_private_value_bool_create_init(BT_FALSE); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init(int64_t val) -{ - struct bt_ctf_value_integer *integer_obj; - - BT_LOGD("Creating integer value object: val=%" PRId64, val); - integer_obj = g_new0(struct bt_ctf_value_integer, 1); - if (!integer_obj) { - BT_LOGE_STR("Failed to allocate one integer value object."); - goto end; - } - - integer_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_INTEGER); - integer_obj->value = val; - BT_LOGD("Created integer value object: addr=%p", - integer_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(integer_obj); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void) -{ - return bt_ctf_private_value_integer_create_init(0); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val) -{ - struct bt_ctf_value_real *real_obj; - - BT_LOGD("Creating real number value object: val=%f", val); - real_obj = g_new0(struct bt_ctf_value_real, 1); - if (!real_obj) { - BT_LOGE_STR("Failed to allocate one real number value object."); - goto end; - } - - real_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_REAL); - real_obj->value = val; - BT_LOGD("Created real number value object: addr=%p", - real_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(real_obj); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_real_create(void) -{ - return bt_ctf_private_value_real_create_init(0.); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_string_create_init(const char *val) -{ - struct bt_ctf_value_string *string_obj = NULL; - - if (!val) { - BT_LOGW_STR("Invalid parameter: value is NULL."); - goto end; - } - - BT_LOGD("Creating string value object: val-len=%zu", strlen(val)); - string_obj = g_new0(struct bt_ctf_value_string, 1); - if (!string_obj) { - BT_LOGE_STR("Failed to allocate one string object."); - goto end; - } - - string_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_STRING); - string_obj->gstr = g_string_new(val); - if (!string_obj->gstr) { - BT_LOGE_STR("Failed to allocate a GString."); - g_free(string_obj); - string_obj = NULL; - goto end; - } - - BT_LOGD("Created string value object: addr=%p", - string_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(string_obj); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_string_create(void) -{ - return bt_ctf_private_value_string_create_init(""); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_array_create(void) -{ - struct bt_ctf_value_array *array_obj; - - BT_LOGD_STR("Creating empty array value object."); - array_obj = g_new0(struct bt_ctf_value_array, 1); - if (!array_obj) { - BT_LOGE_STR("Failed to allocate one array object."); - goto end; - } - - array_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_ARRAY); - array_obj->garray = bt_g_ptr_array_new_full(0, - (GDestroyNotify) bt_ctf_object_put_ref); - if (!array_obj->garray) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - g_free(array_obj); - array_obj = NULL; - goto end; - } - - BT_LOGD("Created array value object: addr=%p", - array_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(array_obj); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_map_create(void) -{ - struct bt_ctf_value_map *map_obj; - - BT_LOGD_STR("Creating empty map value object."); - map_obj = g_new0(struct bt_ctf_value_map, 1); - if (!map_obj) { - BT_LOGE_STR("Failed to allocate one map object."); - goto end; - } - - map_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_MAP); - map_obj->ght = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) bt_ctf_object_put_ref); - if (!map_obj->ght) { - BT_LOGE_STR("Failed to allocate a GHashTable."); - g_free(map_obj); - map_obj = NULL; - goto end; - } - - BT_LOGD("Created map value object: addr=%p", - map_obj); - -end: - return (void *) BT_CTF_VALUE_FROM_CONCRETE(map_obj); -} - -BT_HIDDEN -bt_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_CTF_VALUE_TYPE_BOOL); - return BT_CTF_VALUE_TO_BOOL(bool_obj)->value; -} - -BT_HIDDEN -void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, bt_bool val) -{ - BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_CTF_VALUE_TYPE_BOOL); - BT_CTF_ASSERT_PRE_VALUE_HOT(bool_obj, "Value object"); - BT_CTF_VALUE_TO_BOOL(bool_obj)->value = val; - BT_LOGV("Set boolean value's raw value: value-addr=%p, value=%d", - bool_obj, val); -} - -BT_HIDDEN -int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, BT_CTF_VALUE_TYPE_INTEGER); - return BT_CTF_VALUE_TO_INTEGER(integer_obj)->value; -} - -BT_HIDDEN -void bt_ctf_private_value_integer_set(struct bt_ctf_private_value *integer_obj, - int64_t val) -{ - BT_CTF_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, BT_CTF_VALUE_TYPE_INTEGER); - BT_CTF_ASSERT_PRE_VALUE_HOT(integer_obj, "Value object"); - BT_CTF_VALUE_TO_INTEGER(integer_obj)->value = val; - BT_LOGV("Set integer value's raw value: value-addr=%p, value=%" PRId64, - integer_obj, val); -} - -BT_HIDDEN -double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_CTF_VALUE_TYPE_REAL); - return BT_CTF_VALUE_TO_REAL(real_obj)->value; -} - -BT_HIDDEN -void bt_ctf_private_value_real_set(struct bt_ctf_private_value *real_obj, double val) -{ - BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_CTF_VALUE_TYPE_REAL); - BT_CTF_ASSERT_PRE_VALUE_HOT(real_obj, "Value object"); - BT_CTF_VALUE_TO_REAL(real_obj)->value = val; - BT_LOGV("Set real number value's raw value: value-addr=%p, value=%f", - real_obj, val); -} - -BT_HIDDEN -const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(string_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_CTF_VALUE_TYPE_STRING); - return BT_CTF_VALUE_TO_STRING(string_obj)->gstr->str; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_string_set( - struct bt_ctf_private_value *string_obj, const char *val) -{ - BT_CTF_ASSERT_PRE_NON_NULL(string_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_CTF_VALUE_TYPE_STRING); - BT_CTF_ASSERT_PRE_VALUE_HOT(string_obj, "Value object"); - g_string_assign(BT_CTF_VALUE_TO_STRING(string_obj)->gstr, val); - BT_LOGV("Set string value's raw value: value-addr=%p, raw-value-addr=%p", - string_obj, val); - return BT_CTF_VALUE_STATUS_OK; -} - -BT_HIDDEN -uint64_t bt_ctf_value_array_get_size(const struct bt_ctf_value *array_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); - return (uint64_t) BT_CTF_VALUE_TO_ARRAY(array_obj)->garray->len; -} - -BT_HIDDEN -struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index( - const struct bt_ctf_value *array_obj, - uint64_t index) -{ - struct bt_ctf_value_array *typed_array_obj = - BT_CTF_VALUE_TO_ARRAY(array_obj); - - BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); - BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, - typed_array_obj->garray->len); - return g_ptr_array_index(typed_array_obj->garray, index); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index( - const struct bt_ctf_private_value *array_obj, - uint64_t index) -{ - return (void *) bt_ctf_value_array_borrow_element_by_index( - (void *) array_obj, index); -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_element( - struct bt_ctf_private_value *array_obj, - struct bt_ctf_value *element_obj) -{ - struct bt_ctf_value_array *typed_array_obj = - BT_CTF_VALUE_TO_ARRAY(array_obj); - - BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); - BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); - BT_CTF_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); - g_ptr_array_add(typed_array_obj->garray, element_obj); - bt_ctf_object_get_ref(element_obj); - BT_LOGV("Appended element to array value: array-value-addr=%p, " - "element-value-addr=%p, new-size=%u", - array_obj, element_obj, typed_array_obj->garray->len); - return BT_CTF_VALUE_STATUS_OK; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element( - struct bt_ctf_private_value *array_obj, bt_bool val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *bool_obj = NULL; - - bool_obj = bt_ctf_private_value_bool_create_init(val); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) bool_obj); - bt_ctf_object_put_ref(bool_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element( - struct bt_ctf_private_value *array_obj, int64_t val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *integer_obj = NULL; - - integer_obj = bt_ctf_private_value_integer_create_init(val); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) integer_obj); - bt_ctf_object_put_ref(integer_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element( - struct bt_ctf_private_value *array_obj, double val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *real_obj = NULL; - - real_obj = bt_ctf_private_value_real_create_init(val); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) real_obj); - bt_ctf_object_put_ref(real_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element( - struct bt_ctf_private_value *array_obj, const char *val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *string_obj = NULL; - - string_obj = bt_ctf_private_value_string_create_init(val); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) string_obj); - bt_ctf_object_put_ref(string_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element( - struct bt_ctf_private_value *array_obj) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *empty_array_obj = NULL; - - empty_array_obj = bt_ctf_private_value_array_create(); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) empty_array_obj); - bt_ctf_object_put_ref(empty_array_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element( - struct bt_ctf_private_value *array_obj) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *map_obj = NULL; - - map_obj = bt_ctf_private_value_map_create(); - ret = bt_ctf_private_value_array_append_element(array_obj, - (void *) map_obj); - bt_ctf_object_put_ref(map_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index( - struct bt_ctf_private_value *array_obj, uint64_t index, - struct bt_ctf_value *element_obj) -{ - struct bt_ctf_value_array *typed_array_obj = - BT_CTF_VALUE_TO_ARRAY(array_obj); - - BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); - BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); - BT_CTF_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); - BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, - typed_array_obj->garray->len); - bt_ctf_object_put_ref(g_ptr_array_index(typed_array_obj->garray, index)); - g_ptr_array_index(typed_array_obj->garray, index) = element_obj; - bt_ctf_object_get_ref(element_obj); - BT_LOGV("Set array value's element: array-value-addr=%p, " - "index=%" PRIu64 ", element-value-addr=%p", - array_obj, index, element_obj); - return BT_CTF_VALUE_STATUS_OK; -} - -BT_HIDDEN -uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); - return (uint64_t) g_hash_table_size(BT_CTF_VALUE_TO_MAP(map_obj)->ght); -} - -BT_HIDDEN -struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value(const struct bt_ctf_value *map_obj, - const char *key) -{ - BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); - return g_hash_table_lookup(BT_CTF_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key))); -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value( - const struct bt_ctf_private_value *map_obj, const char *key) -{ - return (void *) bt_ctf_value_map_borrow_entry_value((void *) map_obj, key); -} - -BT_HIDDEN -bt_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, const char *key) -{ - BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); - return bt_g_hash_table_contains(BT_CTF_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key))); -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry( - struct bt_ctf_private_value *map_obj, - const char *key, struct bt_ctf_value *element_obj) -{ - BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Map value object"); - BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); - BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); - BT_CTF_ASSERT_PRE_VALUE_HOT(map_obj, "Map value object"); - g_hash_table_insert(BT_CTF_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key)), element_obj); - bt_ctf_object_get_ref(element_obj); - BT_LOGV("Inserted value into map value: map-value-addr=%p, " - "key=\"%s\", element-value-addr=%p", - map_obj, key, element_obj); - return BT_CTF_VALUE_STATUS_OK; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry( - struct bt_ctf_private_value *map_obj, const char *key, bt_bool val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *bool_obj = NULL; - - bool_obj = bt_ctf_private_value_bool_create_init(val); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) bool_obj); - bt_ctf_object_put_ref(bool_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry( - struct bt_ctf_private_value *map_obj, const char *key, int64_t val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *integer_obj = NULL; - - integer_obj = bt_ctf_private_value_integer_create_init(val); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) integer_obj); - bt_ctf_object_put_ref(integer_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry( - struct bt_ctf_private_value *map_obj, const char *key, double val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *real_obj = NULL; - - real_obj = bt_ctf_private_value_real_create_init(val); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) real_obj); - bt_ctf_object_put_ref(real_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry( - struct bt_ctf_private_value *map_obj, const char *key, - const char *val) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *string_obj = NULL; - - string_obj = bt_ctf_private_value_string_create_init(val); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) string_obj); - bt_ctf_object_put_ref(string_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry( - struct bt_ctf_private_value *map_obj, const char *key) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *array_obj = NULL; - - array_obj = bt_ctf_private_value_array_create(); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) array_obj); - bt_ctf_object_put_ref(array_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry( - struct bt_ctf_private_value *map_obj, const char *key) -{ - enum bt_ctf_value_status ret; - struct bt_ctf_private_value *empty_map_obj = NULL; - - empty_map_obj = bt_ctf_private_value_map_create(); - ret = bt_ctf_private_value_map_insert_entry(map_obj, key, - (void *) empty_map_obj); - bt_ctf_object_put_ref(empty_map_obj); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_map_foreach_entry(const struct bt_ctf_value *map_obj, - bt_ctf_value_map_foreach_entry_cb cb, void *data) -{ - enum bt_ctf_value_status ret = BT_CTF_VALUE_STATUS_OK; - gpointer key, element_obj; - GHashTableIter iter; - struct bt_ctf_value_map *typed_map_obj = BT_CTF_VALUE_TO_MAP(map_obj); - - BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_CTF_ASSERT_PRE_NON_NULL(cb, "Callback"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); - g_hash_table_iter_init(&iter, typed_map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - if (!cb(key_str, element_obj, data)) { - BT_LOGV("User canceled the loop: key=\"%s\", " - "value-addr=%p, data=%p", - key_str, element_obj, data); - ret = BT_CTF_VALUE_STATUS_CANCELED; - break; - } - } - - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry( - const struct bt_ctf_private_value *map_obj, - bt_ctf_private_value_map_foreach_entry_cb cb, void *data) -{ - return bt_ctf_value_map_foreach_entry((void *) map_obj, - (bt_ctf_value_map_foreach_entry_cb) cb, data); -} - -struct extend_map_element_data { - struct bt_ctf_private_value *extended_obj; - enum bt_ctf_value_status status; -}; - -static -bt_bool extend_map_element(const char *key, - struct bt_ctf_value *extension_obj_elem, void *data) -{ - bt_bool ret = BT_TRUE; - struct extend_map_element_data *extend_data = data; - struct bt_ctf_private_value *extension_obj_elem_copy = NULL; - - /* Copy object which is to replace the current one */ - extend_data->status = bt_ctf_value_copy(&extension_obj_elem_copy, - extension_obj_elem); - if (extend_data->status) { - BT_LOGE("Cannot copy map element: addr=%p", - extension_obj_elem); - goto error; - } - - BT_ASSERT(extension_obj_elem_copy); - - /* Replace in extended object */ - extend_data->status = bt_ctf_private_value_map_insert_entry( - extend_data->extended_obj, key, - (void *) extension_obj_elem_copy); - if (extend_data->status) { - BT_LOGE("Cannot replace value in extended value: key=\"%s\", " - "extended-value-addr=%p, element-value-addr=%p", - key, extend_data->extended_obj, - extension_obj_elem_copy); - goto error; - } - - goto end; - -error: - BT_ASSERT(extend_data->status != BT_CTF_VALUE_STATUS_OK); - ret = BT_FALSE; - -end: - BT_CTF_OBJECT_PUT_REF_AND_RESET(extension_obj_elem_copy); - return ret; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_map_extend( - struct bt_ctf_private_value **extended_map_obj, - const struct bt_ctf_value *base_map_obj, - const struct bt_ctf_value *extension_obj) -{ - struct extend_map_element_data extend_data = { - .extended_obj = NULL, - .status = BT_CTF_VALUE_STATUS_OK, - }; - - BT_CTF_ASSERT_PRE_NON_NULL(base_map_obj, "Base value object"); - BT_CTF_ASSERT_PRE_NON_NULL(extension_obj, "Extension value object"); - BT_CTF_ASSERT_PRE_NON_NULL(extended_map_obj, - "Extended value object (output)"); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(base_map_obj, BT_CTF_VALUE_TYPE_MAP); - BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(extension_obj, BT_CTF_VALUE_TYPE_MAP); - BT_LOGD("Extending map value: base-value-addr=%p, extension-value-addr=%p", - base_map_obj, extension_obj); - *extended_map_obj = NULL; - - /* Create copy of base map object to start with */ - extend_data.status = bt_ctf_value_copy(extended_map_obj, base_map_obj); - if (extend_data.status) { - BT_LOGE("Cannot copy base value: base-value-addr=%p", - base_map_obj); - goto error; - } - - BT_ASSERT(extended_map_obj); - - /* - * For each key in the extension map object, replace this key - * in the copied map object. - */ - extend_data.extended_obj = *extended_map_obj; - - if (bt_ctf_value_map_foreach_entry(extension_obj, extend_map_element, - &extend_data)) { - BT_LOGE("Cannot iterate on the extension object's elements: " - "extension-value-addr=%p", extension_obj); - goto error; - } - - if (extend_data.status) { - BT_LOGE("Failed to successfully iterate on the extension object's elements: " - "extension-value-addr=%p", extension_obj); - goto error; - } - - BT_LOGD("Extended map value: extended-value-addr=%p", - *extended_map_obj); - goto end; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(*extended_map_obj); - *extended_map_obj = NULL; - -end: - return extend_data.status; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy_obj, - const struct bt_ctf_value *object) -{ - enum bt_ctf_value_status status = BT_CTF_VALUE_STATUS_OK; - - BT_CTF_ASSERT_PRE_NON_NULL(object, "Value object"); - BT_CTF_ASSERT_PRE_NON_NULL(copy_obj, "Value object copy (output)"); - BT_LOGD("Copying value object: addr=%p", object); - *copy_obj = copy_funcs[object->type](object); - if (*copy_obj) { - BT_LOGD("Copied value object: copy-value-addr=%p", - copy_obj); - } else { - status = BT_CTF_VALUE_STATUS_NOMEM; - *copy_obj = NULL; - BT_LOGE_STR("Failed to copy value object."); - } - - return status; -} - -BT_HIDDEN -bt_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b) -{ - bt_bool ret = BT_FALSE; - - BT_CTF_ASSERT_PRE_NON_NULL(object_a, "Value object A"); - BT_CTF_ASSERT_PRE_NON_NULL(object_b, "Value object B"); - - if (object_a->type != object_b->type) { - BT_LOGV("Values are different: type mismatch: " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-type=%d, value-b-type=%d", - object_a, object_b, object_a->type, object_b->type); - goto end; - } - - ret = compare_funcs[object_a->type](object_a, object_b); - -end: - return ret; -} diff --git a/ctf-writer/visitor.c b/ctf-writer/visitor.c deleted file mode 100644 index 707cc7a8..00000000 --- a/ctf-writer/visitor.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * visitor.c - * - * Babeltrace CTF writer - Visitor - * - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -BT_HIDDEN -int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root, - bt_ctf_child_count_accessor child_counter, - bt_ctf_child_accessor child_accessor, - bt_ctf_child_visitor child_visitor, - bt_ctf_visitor visitor, - void *data) -{ - int ret, child_count, i; - - ret = visitor(root, data); - if (ret) { - goto end; - } - - child_count = child_counter(root->object); - if (child_count < 0) { - ret = child_count; - goto end; - } - - for (i = 0; i < child_count; i++) { - void *child; - - child = child_accessor(root->object, i); - if (!child) { - ret = -1; - goto end; - } - ret = child_visitor(child, visitor, data); - BT_CTF_OBJECT_PUT_REF_AND_RESET(child); - if (ret) { - goto end; - } - } -end: - return ret; -} - -enum bt_ctf_visitor_object_type bt_ctf_visitor_object_get_type( - struct bt_ctf_visitor_object *object) -{ - enum bt_ctf_visitor_object_type ret = BT_CTF_VISITOR_OBJECT_TYPE_UNKNOWN; - - if (!object) { - goto end; - } - - ret = object->type; -end: - return ret; -} - -void *bt_ctf_visitor_object_get_object(struct bt_ctf_visitor_object *object) -{ - void *ret = NULL; - - if (!object) { - goto end; - } - - ret = object->object; -end: - return ret; -} diff --git a/ctf-writer/writer.c b/ctf-writer/writer.c deleted file mode 100644 index 949bbea2..00000000 --- a/ctf-writer/writer.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * writer.c - * - * Babeltrace CTF Writer - * - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTF-WRITER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void bt_ctf_writer_destroy(struct bt_ctf_object *obj); - -static -int init_trace_packet_header(struct bt_ctf_trace *trace) -{ - int ret = 0; - struct bt_ctf_field_type *_uint32_t = - get_field_type(FIELD_TYPE_ALIAS_UINT32_T); - struct bt_ctf_field_type *_uint8_t = - get_field_type(FIELD_TYPE_ALIAS_UINT8_T); - struct bt_ctf_field_type *trace_packet_header_type = - bt_ctf_field_type_structure_create(); - struct bt_ctf_field_type *uuid_array_type = - bt_ctf_field_type_array_create(_uint8_t, 16); - - if (!trace_packet_header_type || !uuid_array_type) { - ret = -1; - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - _uint32_t, "magic"); - if (ret) { - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - uuid_array_type, "uuid"); - if (ret) { - goto end; - } - - ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, - _uint32_t, "stream_id"); - if (ret) { - goto end; - } - - ret = bt_ctf_trace_set_packet_header_field_type(trace, - trace_packet_header_type); - if (ret) { - goto end; - } -end: - bt_ctf_object_put_ref(uuid_array_type); - bt_ctf_object_put_ref(_uint32_t); - bt_ctf_object_put_ref(_uint8_t); - bt_ctf_object_put_ref(trace_packet_header_type); - return ret; -} - -struct bt_ctf_writer *bt_ctf_writer_create(const char *path) -{ - int ret; - struct bt_ctf_writer *writer = NULL; - unsigned char uuid[16]; - char *metadata_path = NULL; - - if (!path) { - goto error; - } - - writer = g_new0(struct bt_ctf_writer, 1); - if (!writer) { - goto error; - } - - metadata_path = g_build_filename(path, "metadata", NULL); - - bt_ctf_object_init_shared(&writer->base, bt_ctf_writer_destroy); - writer->path = g_string_new(path); - if (!writer->path) { - goto error_destroy; - } - - writer->trace = bt_ctf_trace_create(); - if (!writer->trace) { - goto error_destroy; - } - - ret = init_trace_packet_header(writer->trace); - if (ret) { - goto error_destroy; - } - - /* Generate a UUID for this writer's trace */ - ret = bt_uuid_generate(uuid); - if (ret) { - BT_LOGE_STR("Cannot generate UUID for CTF writer's trace."); - goto error_destroy; - } - - ret = bt_ctf_trace_set_uuid(writer->trace, uuid); - if (ret) { - goto error_destroy; - } - - bt_ctf_object_set_parent(&writer->trace->common.base, &writer->base); - bt_ctf_object_put_ref(writer->trace); - - /* Default to little-endian */ - ret = bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_NATIVE); - BT_ASSERT(ret == 0); - - /* Create trace directory if necessary and open a metadata file */ - if (g_mkdir_with_parents(path, S_IRWXU | S_IRWXG)) { - perror("g_mkdir_with_parents"); - goto error_destroy; - } - - writer->metadata_fd = open(metadata_path, - O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (writer->metadata_fd < 0) { - perror("open"); - goto error_destroy; - } - - g_free(metadata_path); - return writer; - -error_destroy: - BT_CTF_OBJECT_PUT_REF_AND_RESET(writer); -error: - g_free(metadata_path); - return writer; -} - -void bt_ctf_writer_destroy(struct bt_ctf_object *obj) -{ - struct bt_ctf_writer *writer; - - writer = container_of(obj, struct bt_ctf_writer, base); - bt_ctf_writer_flush_metadata(writer); - if (writer->path) { - g_string_free(writer->path, TRUE); - } - - if (writer->metadata_fd > 0) { - if (close(writer->metadata_fd)) { - perror("close"); - } - } - - bt_ctf_object_try_spec_release(&writer->trace->common.base); - g_free(writer); -} - -struct bt_ctf_trace *bt_ctf_writer_get_trace(struct bt_ctf_writer *writer) -{ - struct bt_ctf_trace *trace = NULL; - - if (!writer) { - goto end; - } - - trace = writer->trace; - bt_ctf_object_get_ref(trace); -end: - return trace; -} - -struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, - struct bt_ctf_stream_class *stream_class) -{ - struct bt_ctf_stream *stream = NULL; - int stream_class_count; - bt_bool stream_class_found = BT_FALSE; - int i; - - if (!writer || !stream_class) { - goto error; - } - - /* Make sure the stream class is part of the writer's trace */ - stream_class_count = bt_ctf_trace_get_stream_class_count(writer->trace); - if (stream_class_count < 0) { - goto error; - } - - for (i = 0; i < stream_class_count; i++) { - struct bt_ctf_stream_class *existing_stream_class = - bt_ctf_trace_get_stream_class_by_index( - writer->trace, i); - - if (existing_stream_class == stream_class) { - stream_class_found = BT_TRUE; - } - - BT_CTF_OBJECT_PUT_REF_AND_RESET(existing_stream_class); - - if (stream_class_found) { - break; - } - } - - if (!stream_class_found) { - int ret = bt_ctf_trace_add_stream_class(writer->trace, - stream_class); - - if (ret) { - goto error; - } - } - - stream = bt_ctf_stream_create_with_id(stream_class, NULL, -1ULL); - if (!stream) { - goto error; - } - - return stream; - -error: - BT_CTF_OBJECT_PUT_REF_AND_RESET(stream); - return stream; -} - -int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer, - const char *name, - const char *value) -{ - int ret = -1; - - if (!writer || !name || !value) { - goto end; - } - - ret = bt_ctf_trace_set_environment_field_string(writer->trace, - name, value); -end: - return ret; -} - -int bt_ctf_writer_add_environment_field_int64(struct bt_ctf_writer *writer, - const char *name, int64_t value) -{ - int ret = -1; - - if (!writer || !name) { - goto end; - } - - ret = bt_ctf_trace_set_environment_field_integer(writer->trace, name, - value); -end: - return ret; -} - -int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer, - struct bt_ctf_clock *clock) -{ - int ret = -1; - - if (!writer || !clock) { - goto end; - } - - ret = bt_ctf_trace_add_clock_class(writer->trace, clock->clock_class); -end: - return ret; -} - -char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer) -{ - char *metadata_string = NULL; - - if (!writer) { - goto end; - } - - metadata_string = bt_ctf_trace_get_metadata_string( - writer->trace); -end: - return metadata_string; -} - -void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer) -{ - int ret; - char *metadata_string = NULL; - - if (!writer) { - goto end; - } - - metadata_string = bt_ctf_trace_get_metadata_string( - writer->trace); - if (!metadata_string) { - goto end; - } - - if (lseek(writer->metadata_fd, 0, SEEK_SET) == (off_t)-1) { - perror("lseek"); - goto end; - } - - if (ftruncate(writer->metadata_fd, 0)) { - perror("ftruncate"); - goto end; - } - - ret = write(writer->metadata_fd, metadata_string, - strlen(metadata_string)); - if (ret < 0) { - perror("write"); - goto end; - } -end: - g_free(metadata_string); -} - -int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer, - enum bt_ctf_byte_order byte_order) -{ - int ret = 0; - - if (!writer || writer->frozen) { - ret = -1; - goto end; - } - - if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { - if (BYTE_ORDER == LITTLE_ENDIAN) { - byte_order = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN; - } else { - byte_order = BT_CTF_BYTE_ORDER_BIG_ENDIAN; - } - } - - ret = bt_ctf_trace_set_native_byte_order(writer->trace, - byte_order); -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_writer_freeze(struct bt_ctf_writer *writer) -{ - writer->frozen = 1; -} - -static -const unsigned int field_type_aliases_alignments[] = { - [FIELD_TYPE_ALIAS_UINT5_T] = 1, - [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8, - [FIELD_TYPE_ALIAS_UINT27_T] = 1, - [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8, -}; - -static -const unsigned int field_type_aliases_sizes[] = { - [FIELD_TYPE_ALIAS_UINT5_T] = 5, - [FIELD_TYPE_ALIAS_UINT8_T] = 8, - [FIELD_TYPE_ALIAS_UINT16_T] = 16, - [FIELD_TYPE_ALIAS_UINT27_T] = 27, - [FIELD_TYPE_ALIAS_UINT32_T] = 32, - [FIELD_TYPE_ALIAS_UINT64_T] = 64, -}; - -BT_HIDDEN -struct bt_ctf_field_type *get_field_type(enum field_type_alias alias) -{ - int ret; - unsigned int alignment, size; - struct bt_ctf_field_type *field_type = NULL; - - if (alias >= NR_FIELD_TYPE_ALIAS) { - goto end; - } - - alignment = field_type_aliases_alignments[alias]; - size = field_type_aliases_sizes[alias]; - field_type = bt_ctf_field_type_integer_create(size); - ret = bt_ctf_field_type_set_alignment(field_type, alignment); - if (ret) { - BT_CTF_OBJECT_PUT_REF_AND_RESET(field_type); - } -end: - return field_type; -} - -BT_HIDDEN -const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order) -{ - const char *string; - - switch (byte_order) { - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - string = "le"; - break; - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - string = "be"; - break; - case BT_CTF_BYTE_ORDER_NATIVE: - string = "native"; - break; - default: - abort(); - } - - return string; -} diff --git a/ctfser/Makefile.am b/ctfser/Makefile.am deleted file mode 100644 index 717621ac..00000000 --- a/ctfser/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" - -noinst_LTLIBRARIES = libbabeltrace2-ctfser.la - -libbabeltrace2_ctfser_la_SOURCES = ctfser.c logging.c logging.h diff --git a/ctfser/ctfser.c b/ctfser/ctfser.c deleted file mode 100644 index 32cc9d0b..00000000 --- a/ctfser/ctfser.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CTFSER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline -uint64_t get_packet_size_increment_bytes(void) -{ - return bt_common_get_page_size() * 8; -} - -static inline -void mmap_align_ctfser(struct bt_ctfser *ctfser) -{ - ctfser->base_mma = mmap_align(ctfser->cur_packet_size_bytes, - PROT_READ | PROT_WRITE, - MAP_SHARED, ctfser->fd, ctfser->mmap_offset); -} - -BT_HIDDEN -int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser) -{ - int ret; - - BT_ASSERT(ctfser); - BT_LOGV("Increasing stream file's current packet size: " - "path=\"%s\", fd=%d, " - "offset-in-cur-packet-bits=%" PRIu64 ", " - "cur-packet-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->offset_in_cur_packet_bits, - ctfser->cur_packet_size_bytes); - ret = munmap_align(ctfser->base_mma); - if (ret) { - BT_LOGE_ERRNO("Failed to perform an aligned memory unmapping", - ": ret=%d", ret); - goto end; - } - - ctfser->cur_packet_size_bytes += get_packet_size_increment_bytes(); - - do { - ret = bt_posix_fallocate(ctfser->fd, ctfser->mmap_offset, - ctfser->cur_packet_size_bytes); - } while (ret == EINTR); - - if (ret) { - BT_LOGE("Failed to preallocate memory space: ret=%d", ret); - goto end; - } - - mmap_align_ctfser(ctfser); - if (ctfser->base_mma == MAP_FAILED) { - BT_LOGE_ERRNO("Failed to perform an aligned memory mapping", - ": ret=%d", ret); - ret = -1; - goto end; - } - - BT_LOGV("Increased packet size: " - "path=\"%s\", fd=%d, " - "offset-in-cur-packet-bits=%" PRIu64 ", " - "new-packet-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->offset_in_cur_packet_bits, - ctfser->cur_packet_size_bytes); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path) -{ - int ret = 0; - - BT_ASSERT(ctfser); - memset(ctfser, 0, sizeof(*ctfser)); - ctfser->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (ctfser->fd < 0) { - BT_LOGW_ERRNO("Failed to open stream file for writing", - ": path=\"%s\", ret=%d", - path, ctfser->fd); - ret = -1; - goto end; - } - - ctfser->path = g_string_new(path); - -end: - return ret; -} - -BT_HIDDEN -int bt_ctfser_fini(struct bt_ctfser *ctfser) -{ - int ret = 0; - - if (ctfser->fd == -1) { - goto free_path; - } - - /* - * Truncate the stream file's size to the minimum required to - * fit the last packet as we might have grown it too much during - * the last memory map. - */ - do { - ret = ftruncate(ctfser->fd, ctfser->stream_size_bytes); - } while (ret == -1 && errno == EINTR); - - if (ret) { - BT_LOGE_ERRNO("Failed to truncate stream file", - ": ret=%d, size-bytes=%" PRIu64, - ret, ctfser->stream_size_bytes); - goto end; - } - - if (ctfser->base_mma) { - /* Unmap old base */ - ret = munmap_align(ctfser->base_mma); - if (ret) { - BT_LOGE_ERRNO("Failed to unmap stream file", - ": ret=%d, size-bytes=%" PRIu64, - ret, ctfser->stream_size_bytes); - goto end; - } - - ctfser->base_mma = NULL; - } - - ret = close(ctfser->fd); - if (ret) { - BT_LOGE_ERRNO("Failed to close stream file", - ": ret=%d", ret); - goto end; - } - - ctfser->fd = -1; - -free_path: - if (ctfser->path) { - g_string_free(ctfser->path, TRUE); - ctfser->path = NULL; - } - -end: - return ret; -} - -BT_HIDDEN -int bt_ctfser_open_packet(struct bt_ctfser *ctfser) -{ - int ret = 0; - - BT_LOGV("Opening packet: path=\"%s\", fd=%d, " - "prev-packet-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->prev_packet_size_bytes); - - if (ctfser->base_mma) { - /* Unmap old base (previous packet) */ - ret = munmap_align(ctfser->base_mma); - if (ret) { - BT_LOGE_ERRNO("Failed to unmap stream file", - ": ret=%d, size-bytes=%" PRIu64, - ret, ctfser->stream_size_bytes); - goto end; - } - - ctfser->base_mma = NULL; - } - - /* - * Add the previous packet's size to the memory map address - * offset to start writing immediately after it. - */ - ctfser->mmap_offset += ctfser->prev_packet_size_bytes; - ctfser->prev_packet_size_bytes = 0; - - /* Make initial space for the current packet */ - ctfser->cur_packet_size_bytes = get_packet_size_increment_bytes(); - - do { - ret = bt_posix_fallocate(ctfser->fd, ctfser->mmap_offset, - ctfser->cur_packet_size_bytes); - } while (ret == EINTR); - - if (ret) { - BT_LOGE("Failed to preallocate memory space: ret=%d", ret); - goto end; - } - - /* Start writing at the beginning of the current packet */ - ctfser->offset_in_cur_packet_bits = 0; - - /* Get new base address */ - mmap_align_ctfser(ctfser); - if (ctfser->base_mma == MAP_FAILED) { - BT_LOGE_ERRNO("Failed to perform an aligned memory mapping", - ": ret=%d", ret); - ret = -1; - goto end; - } - - BT_LOGV("Opened packet: path=\"%s\", fd=%d, " - "cur-packet-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->cur_packet_size_bytes); - -end: - return ret; -} - -BT_HIDDEN -void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser, - uint64_t packet_size_bytes) -{ - BT_LOGV("Closing packet: path=\"%s\", fd=%d, " - "offset-in-cur-packet-bits=%" PRIu64 - "cur-packet-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->offset_in_cur_packet_bits, - ctfser->cur_packet_size_bytes); - - /* - * This will be used during the next call to - * bt_ctfser_open_packet(): we add - * `ctfser->prev_packet_size_bytes` to the current memory map - * address offset (first byte of _this_ packet), effectively - * making _this_ packet the required size. - */ - ctfser->prev_packet_size_bytes = packet_size_bytes; - ctfser->stream_size_bytes += packet_size_bytes; - BT_LOGV("Closed packet: path=\"%s\", fd=%d, " - "stream-file-size-bytes=%" PRIu64, - ctfser->path->str, ctfser->fd, - ctfser->stream_size_bytes); -} diff --git a/ctfser/logging.c b/ctfser/logging.c deleted file mode 100644 index 249c3ca0..00000000 --- a/ctfser/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_ctfser_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_ctfser_log_level, "BABELTRACE_CTFSER_LOG_LEVEL"); diff --git a/ctfser/logging.h b/ctfser/logging.h deleted file mode 100644 index f18e7265..00000000 --- a/ctfser/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef COMMON_LOGGING_H -#define COMMON_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_ctfser_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_ctfser_log_level); - -#endif /* COMMON_LOGGING_H */ diff --git a/doc/api/Doxyfile.in b/doc/api/Doxyfile.in index 9715ca6b..376888ce 100644 --- a/doc/api/Doxyfile.in +++ b/doc/api/Doxyfile.in @@ -164,7 +164,7 @@ FILE_PATTERNS = *.h \ RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = *-internal.h +EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * diff --git a/doc/bindings/python/Makefile.am b/doc/bindings/python/Makefile.am index aa94c271..71252a02 100644 --- a/doc/bindings/python/Makefile.am +++ b/doc/bindings/python/Makefile.am @@ -9,9 +9,9 @@ EXTRA_DIST = $(srcdir)/source all-local: $(SPHINX_HTML_TARGET) $(SPHINX_DIRHTML_TARGET) -PYTHON_BT2_BUILD_LIB_DIR = $(abs_top_builddir)/bindings/python/bt2/build/build_lib +PYTHON_BT2_BUILD_LIB_DIR = $(abs_top_builddir)/src/bindings/python/bt2/build/build_lib PP = $(PYTHON_BT2_BUILD_LIB_DIR) -LLP = $(abs_top_builddir)/lib/.libs +LLP = $(abs_top_builddir)/src/lib/.libs SPHINXBUILD = PYTHONPATH="$(PP)" LD_LIBRARY_PATH="$(LLP)" $(PYTHON) -m sphinx SPHINX_SRC = $(wildcard $(SPHINX_SOURCE_DIR)/*.rst) diff --git a/fd-cache/Makefile.am b/fd-cache/Makefile.am deleted file mode 100644 index 9bc55df5..00000000 --- a/fd-cache/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" - -noinst_LTLIBRARIES = libbabeltrace2-fd-cache.la - -libbabeltrace2_fd_cache_la_SOURCES = \ - fd-cache.c \ - logging.c \ - logging.h diff --git a/fd-cache/fd-cache.c b/fd-cache/fd-cache.c deleted file mode 100644 index fcd4f4c2..00000000 --- a/fd-cache/fd-cache.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * fd-cache.c - * - * Babeltrace - File descriptor cache - * - * Copyright 2019 Francis Deslauriers - * - * Author: Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "FD-CACHE" -#include "logging.h" - -#include -#include -#include -#include -#include - -#include -#include - -struct file_key { - uint64_t dev; - uint64_t ino; -}; - -struct fd_handle_internal { - struct bt_fd_cache_handle fd_handle; - uint64_t ref_count; - struct file_key *key; -}; - -static -void fd_cache_handle_internal_destroy( - struct fd_handle_internal *internal_fd) -{ - if (!internal_fd) { - goto end; - } - - if (internal_fd->fd_handle.fd >= 0) { - close(internal_fd->fd_handle.fd); - internal_fd->fd_handle.fd = -1; - } - -end: - g_free(internal_fd); -} - -/* - * Using simple hash algorithm found on stackoverflow: - * https://stackoverflow.com/questions/664014/ - */ -static inline -uint64_t hash_uint64_t(uint64_t x) { - x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); - x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); - x = x ^ (x >> 31); - return x; -} - -static -guint file_key_hash(gconstpointer v) -{ - const struct file_key *fk = v; - return hash_uint64_t(fk->dev) ^ hash_uint64_t(fk->ino); -} - -static -gboolean file_key_equal(gconstpointer v1, gconstpointer v2) -{ - const struct file_key *fk1 = v1; - const struct file_key *fk2 = v2; - - return (fk1->dev == fk2->dev) && (fk1->ino == fk2->ino); -} - -static -void file_key_destroy(gpointer data) -{ - struct file_key *fk = data; - g_free(fk); -} - -BT_HIDDEN -int bt_fd_cache_init(struct bt_fd_cache *fdc) -{ - int ret = 0; - - fdc->cache = g_hash_table_new_full(file_key_hash, file_key_equal, - file_key_destroy, (GDestroyNotify) fd_cache_handle_internal_destroy); - if (!fdc->cache) { - ret = -1; - } - - return ret; -} - -BT_HIDDEN -void bt_fd_cache_fini(struct bt_fd_cache *fdc) -{ - BT_ASSERT(fdc->cache); - /* - * All handle should have been removed for the hashtable at this point. - */ - BT_ASSERT(g_hash_table_size(fdc->cache) == 0); - g_hash_table_destroy(fdc->cache); - - return; -} - -BT_HIDDEN -struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc, - const char *path) -{ - struct fd_handle_internal *fd_internal = NULL; - struct stat statbuf; - struct file_key fk; - int ret, fd = -1; - - ret = stat(path, &statbuf); - if (ret < 0) { - /* - * This is not necessarily an error as we sometimes try to open - * files to see if they exist. Log the error as DEBUG severity - * level. - */ - BT_LOGD_ERRNO("Failed to stat file", ": path=%s", path); - goto end; - } - - /* - * Use the device number and inode number to uniquely identify a file. - * Even if the file has the same path, it may have been replaced so we - * must open a new FD for it. This replacement of file is more likely - * to happen with a lttng-live source component. - */ - fk.dev = statbuf.st_dev; - fk.ino = statbuf.st_ino; - - fd_internal = g_hash_table_lookup(fdc->cache, &fk); - if (!fd_internal) { - struct file_key *file_key; - - fd = open(path, O_RDONLY); - if (fd < 0) { - BT_LOGE_ERRNO("Failed to open file", "path=%s", path); - goto error; - } - - fd_internal = g_new0(struct fd_handle_internal, 1); - if (!fd_internal) { - BT_LOGE("Failed to allocate fd internal handle"); - goto error; - } - - file_key = g_new0(struct file_key, 1); - if (!fd_internal) { - BT_LOGE("Failed to allocate file key"); - goto error; - } - - *file_key = fk; - - fd_internal->fd_handle.fd = fd; - fd_internal->ref_count = 0; - fd_internal->key = file_key; - - /* Insert the newly created fd handle. */ - g_hash_table_insert(fdc->cache, fd_internal->key, fd_internal); - } - - fd_internal->ref_count++; - goto end; - -error: - /* - * Close file descriptor if it was open() and we are currently on error - * path. - */ - if (fd != -1) { - ret = close(fd); - if (ret) { - BT_LOGE_ERRNO("Failed to close file descriptor", - ": fd=%i, path=%s", fd, path); - } - } - - fd_cache_handle_internal_destroy(fd_internal); - fd_internal = NULL; -end: - return (struct bt_fd_cache_handle *) fd_internal; -} - -BT_HIDDEN -void bt_fd_cache_put_handle(struct bt_fd_cache *fdc, - struct bt_fd_cache_handle *handle) -{ - struct fd_handle_internal *fd_internal; - - if (!handle) { - goto end; - } - - fd_internal = (struct fd_handle_internal *) handle; - - BT_ASSERT(fd_internal->ref_count > 0); - - if (fd_internal->ref_count > 1) { - fd_internal->ref_count--; - } else { - gboolean ret; - int close_ret; - - close_ret = close(fd_internal->fd_handle.fd); - if (close_ret == -1) { - BT_LOGW_ERRNO("Failed to close file descriptor", - ": fd=%d", fd_internal->fd_handle.fd); - } - ret = g_hash_table_remove(fdc->cache, fd_internal->key); - BT_ASSERT(ret); - } - -end: - return; -} diff --git a/fd-cache/logging.c b/fd-cache/logging.c deleted file mode 100644 index e203ee74..00000000 --- a/fd-cache/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_fd_cache_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_fd_cache_log_level, "BABELTRACE_FD_CACHE_LOG_LEVEL"); diff --git a/fd-cache/logging.h b/fd-cache/logging.h deleted file mode 100644 index ae851e71..00000000 --- a/fd-cache/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef FD_CACHE_LOGGING_H -#define FD_CACHE_LOGGING_H - -/* - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_fd_cache_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_fd_cache_log_level); - -#endif /* FD_CACHE_LOGGING_H */ diff --git a/include/Makefile.am b/include/Makefile.am index a340b805..9b4f0530 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,73 +1,3 @@ -## -## This target generates an include file that contains the git version -## string of the current branch, it must be continuously updated when -## we build in the git repo and shipped in dist tarballs to reflect the -## status of the tree when it was generated. If the tree is clean and -## the current commit is tag a starting with "v", consider this a -## release version and set an empty git version. -## -## Here is what the inline script does: -## -## First, delete any stale "version.i.tmp" file. -## -## If "bootstrap" and ".git" exists in the top source directory and the git -## executable is available, get the current git version string in the form: -## -## "latest_tag"(-"number_of_commits_on_top")(-g"latest_commit_hash")(-dirty) -## -## And store it in "version.i.tmp", if the current commit is tagged, the tag -## starts with "v" and the tree is clean, consider this a release version and -## overwrite the git version with an empty string in "version.i.tmp". -## -## If we don't have a "version.i.tmp" nor a "version.i", generate an empty -## string as a failover. -## -## If we don't have a "version.i" or we have both files and they are different, -## copy "version.i.tmp" over "version.i". This way the dependent targets are -## only rebuilt when the version string changes. -## - -version_verbose = $(version_verbose_@AM_V@) -version_verbose_ = $(version_verbose_@AM_DEFAULT_V@) -version_verbose_0 = @echo " GEN " $@; - -version.i: - $(version_verbose)rm -f version.i.tmp; \ - if (test -r "$(top_srcdir)/bootstrap" && test -r "$(top_srcdir)/.git") && \ - test -x "`which git 2>&1;true`"; then \ - GIT_VERSION_STR="`cd "$(top_srcdir)" && git describe --tags --dirty`"; \ - GIT_CURRENT_TAG="`cd "$(top_srcdir)" && git describe --tags --exact-match --match="v[0-9]*" HEAD 2> /dev/null`"; \ - echo "#define GIT_VERSION \"$$GIT_VERSION_STR\"" > version.i.tmp; \ - if ! $(GREP) -- "-dirty" version.i.tmp > /dev/null && \ - test "x$$GIT_CURRENT_TAG" != "x"; then \ - echo "#define GIT_VERSION \"\"" > version.i.tmp; \ - fi; \ - fi; \ - if test ! -f version.i.tmp; then \ - if test ! -f version.i; then \ - echo '#define GIT_VERSION ""' > version.i; \ - fi; \ - elif test ! -f version.i || \ - test x"`cat version.i.tmp`" != x"`cat version.i`"; then \ - mv version.i.tmp version.i; \ - fi; \ - rm -f version.i.tmp; \ - true - -## -## version.i is defined as a .PHONY target even if it's a real file, -## we want the target to be re-run on every make. -## -.PHONY: version.i - -CLEANFILES = version.i.tmp - -## -## Only clean "version.i" on dist-clean, we need to keep it on regular -## clean when it's part of a dist tarball. -## -DISTCLEANFILES = version.i - # Core API babeltrace2includedir = "$(includedir)/babeltrace2" babeltrace2include_HEADERS = \ @@ -210,99 +140,3 @@ babeltrace2graphinclude_HEADERS = \ babeltrace2/graph/self-component-source.h \ babeltrace2/graph/self-component.h \ babeltrace2/graph/self-message-iterator.h - -noinst_HEADERS = \ - babeltrace2/compat/stdlib-internal.h \ - babeltrace2/compat/fcntl-internal.h \ - babeltrace2/compat/glib-internal.h \ - babeltrace2/compat/uuid-internal.h \ - babeltrace2/compat/unistd-internal.h \ - babeltrace2/compat/stdio-internal.h \ - babeltrace2/compat/time-internal.h \ - babeltrace2/compat/utc-internal.h \ - babeltrace2/compat/memstream-internal.h \ - babeltrace2/compat/string-internal.h \ - babeltrace2/compat/limits-internal.h \ - babeltrace2/compat/mman-internal.h \ - babeltrace2/compat/socket-internal.h \ - babeltrace2/common-internal.h \ - babeltrace2/ctfser-internal.h \ - babeltrace2/bitfield-internal.h \ - babeltrace2/object-internal.h \ - babeltrace2/object-pool-internal.h \ - babeltrace2/plugin/plugin-internal.h \ - babeltrace2/plugin/plugin-so-internal.h \ - babeltrace2/plugin/python-plugin-provider-internal.h \ - babeltrace2/assert-internal.h \ - babeltrace2/value-internal.h \ - babeltrace2/ctf-writer/assert-pre-internal.h \ - babeltrace2/ctf-writer/attributes-internal.h \ - babeltrace2/ctf-writer/clock-class-internal.h \ - babeltrace2/ctf-writer/clock-internal.h \ - babeltrace2/ctf-writer/event-class-internal.h \ - babeltrace2/ctf-writer/event-internal.h \ - babeltrace2/ctf-writer/field-path-internal.h \ - babeltrace2/ctf-writer/fields-internal.h \ - babeltrace2/ctf-writer/field-types-internal.h \ - babeltrace2/ctf-writer/field-wrapper-internal.h \ - babeltrace2/ctf-writer/functor-internal.h \ - babeltrace2/ctf-writer/object-internal.h \ - babeltrace2/ctf-writer/object-pool-internal.h \ - babeltrace2/ctf-writer/resolve-internal.h \ - babeltrace2/ctf-writer/stream-class-internal.h \ - babeltrace2/ctf-writer/stream-internal.h \ - babeltrace2/ctf-writer/trace-internal.h \ - babeltrace2/ctf-writer/utils-internal.h \ - babeltrace2/ctf-writer/validation-internal.h \ - babeltrace2/ctf-writer/values-internal.h \ - babeltrace2/ctf-writer/visitor-internal.h \ - babeltrace2/ctf-writer/writer-internal.h \ - babeltrace2/mmap-align-internal.h \ - babeltrace2/align-internal.h \ - babeltrace2/logging-internal.h \ - babeltrace2/endian-internal.h \ - babeltrace2/trace-ir/attributes-internal.h \ - babeltrace2/trace-ir/clock-class-internal.h \ - babeltrace2/trace-ir/clock-snapshot-internal.h \ - babeltrace2/trace-ir/clock-snapshot-set-internal.h \ - babeltrace2/trace-ir/event-class-internal.h \ - babeltrace2/trace-ir/event-internal.h \ - babeltrace2/trace-ir/field-class-internal.h \ - babeltrace2/trace-ir/field-path-internal.h \ - babeltrace2/trace-ir/field-internal.h \ - babeltrace2/trace-ir/field-wrapper-internal.h \ - babeltrace2/trace-ir/packet-internal.h \ - babeltrace2/trace-ir/resolve-field-path-internal.h \ - babeltrace2/trace-ir/stream-class-internal.h \ - babeltrace2/trace-ir/stream-internal.h \ - babeltrace2/trace-ir/trace-class-internal.h \ - babeltrace2/trace-ir/trace-internal.h \ - babeltrace2/trace-ir/utils-internal.h \ - babeltrace2/prio-heap-internal.h \ - babeltrace2/lib-logging-internal.h \ - babeltrace2/compiler-internal.h \ - babeltrace2/babeltrace-internal.h \ - babeltrace2/assert-pre-internal.h \ - babeltrace2/graph/component-class-internal.h \ - babeltrace2/graph/component-class-sink-colander-internal.h \ - babeltrace2/graph/component-filter-internal.h \ - babeltrace2/graph/component-internal.h \ - babeltrace2/graph/component-sink-internal.h \ - babeltrace2/graph/component-source-internal.h \ - babeltrace2/graph/connection-internal.h \ - babeltrace2/graph/graph-internal.h \ - babeltrace2/graph/message-discarded-items-internal.h \ - babeltrace2/graph/message-event-internal.h \ - babeltrace2/graph/message-message-iterator-inactivity-internal.h \ - babeltrace2/graph/message-internal.h \ - babeltrace2/graph/message-iterator-internal.h \ - babeltrace2/graph/message-packet-internal.h \ - babeltrace2/graph/message-stream-activity-internal.h \ - babeltrace2/graph/message-stream-internal.h \ - babeltrace2/graph/port-internal.h \ - babeltrace2/graph/query-executor-internal.h \ - babeltrace2/list-internal.h \ - babeltrace2/fd-cache-internal.h \ - babeltrace2/property-internal.h \ - version.h \ - version.i diff --git a/include/babeltrace2/align-internal.h b/include/babeltrace2/align-internal.h deleted file mode 100644 index 4e76cd01..00000000 --- a/include/babeltrace2/align-internal.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef _BABELTRACE_ALIGN_H -#define _BABELTRACE_ALIGN_H - -/* - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) -#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) -#define PTR_ALIGN(p, a) ((typeof(p)) ALIGN((unsigned long) (p), a)) -#define ALIGN_FLOOR(x, a) __ALIGN_FLOOR_MASK(x, (typeof(x)) (a) - 1) -#define __ALIGN_FLOOR_MASK(x, mask) ((x) & ~(mask)) -#define PTR_ALIGN_FLOOR(p, a) \ - ((typeof(p)) ALIGN_FLOOR((unsigned long) (p), a)) -#define IS_ALIGNED(x, a) (((x) & ((typeof(x)) (a) - 1)) == 0) - -/* - * Align pointer on natural object alignment. - */ -#define object_align(obj) PTR_ALIGN(obj, __alignof__(*(obj))) -#define object_align_floor(obj) PTR_ALIGN_FLOOR(obj, __alignof__(*(obj))) - -/** - * offset_align - Calculate the offset needed to align an object on its natural - * alignment towards higher addresses. - * @align_drift: object offset from an "alignment"-aligned address. - * @alignment: natural object alignment. Must be non-zero, power of 2. - * - * Returns the offset that must be added to align towards higher - * addresses. - */ -#define offset_align(align_drift, alignment) \ - ({ \ - MAYBE_BUILD_BUG_ON((alignment) == 0 \ - || ((alignment) & ((alignment) - 1))); \ - (((alignment) - (align_drift)) & ((alignment) - 1)); \ - }) - -/** - * offset_align_floor - Calculate the offset needed to align an object - * on its natural alignment towards lower addresses. - * @align_drift: object offset from an "alignment"-aligned address. - * @alignment: natural object alignment. Must be non-zero, power of 2. - * - * Returns the offset that must be substracted to align towards lower addresses. - */ -#define offset_align_floor(align_drift, alignment) \ - ({ \ - MAYBE_BUILD_BUG_ON((alignment) == 0 \ - || ((alignment) & ((alignment) - 1))); \ - (((align_drift) - (alignment)) & ((alignment) - 1)); \ - }) - -#endif /* _BABELTRACE_ALIGN_H */ diff --git a/include/babeltrace2/assert-internal.h b/include/babeltrace2/assert-internal.h deleted file mode 100644 index 2df876d6..00000000 --- a/include/babeltrace2/assert-internal.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef BABELTRACE_ASSERT_INTERNAL_H -#define BABELTRACE_ASSERT_INTERNAL_H - -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#ifdef BT_DEBUG_MODE - -extern void bt_common_assert_failed(const char *file, int line, - const char *func, const char *assertion) __attribute__((noreturn)); - -/* - * Internal assertion (to detect logic errors on which the library user - * has no influence). Use BT_ASSERT_PRE() to check a precondition which - * must be directly or indirectly satisfied by the library user. - */ -#define BT_ASSERT(_cond) \ - do { \ - if (!(_cond)) { \ - bt_common_assert_failed(__FILE__, __LINE__, __func__, \ - TOSTRING(_cond)); \ - } \ - } while (0) - -/* - * Marks a function as being only used within a BT_ASSERT() context. - */ -# define BT_ASSERT_FUNC -#else -/* - * When BT_DEBUG_MODE is not defined, define BT_ASSERT() macro to the following - * to trick the compiler into thinking that the variable passed as condition to - * the assertion is used. This is to prevent set-but-not-used warnings from the - * compiler when assertions are disabled. The `sizeof` operator also makes sure - * that the `_cond` expression is not evaluated, thus preventing unwanted side - * effects. - * - * In-depth explanation: https://stackoverflow.com/questions/37411809/how-to-elegantly-fix-this-unused-variable-warning/37412551#37412551 - */ -# define BT_ASSERT(_cond) ((void) sizeof((void) (_cond), 0)) -# define BT_ASSERT_FUNC BT_UNUSED -#endif /* BT_DEBUG_MODE */ - -#endif /* BABELTRACE_ASSERT_INTERNAL_H */ diff --git a/include/babeltrace2/assert-pre-internal.h b/include/babeltrace2/assert-pre-internal.h deleted file mode 100644 index cb503665..00000000 --- a/include/babeltrace2/assert-pre-internal.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef BABELTRACE_ASSERT_PRE_INTERNAL_H -#define BABELTRACE_ASSERT_PRE_INTERNAL_H - -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * The macros in this header use macros defined in - * . We don't want this header to - * automatically include because you - * need to manually define BT_LOG_TAG before including - * and it is unexpected that you - * also need to define it before including this header. - * - * This is a reminder that in order to use - * , you also need to use logging - * explicitly. - */ - -#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H -# error Include before this header. -#endif - -#include -#include -#include - -#ifdef BT_DEV_MODE -/* - * Asserts that the library precondition _cond is satisfied. - * - * If _cond is false, log a fatal statement using _fmt and the optional - * arguments using BT_LIB_LOGF(), and abort. - * - * To assert that a postcondition is satisfied or that some internal - * object/context/value is in the expected state, use BT_ASSERT(). - */ -# define BT_ASSERT_PRE(_cond, _fmt, ...) \ - do { \ - if (!(_cond)) { \ - BT_LOGF_STR("Library precondition not satisfied; error is:"); \ - BT_LIB_LOGF((_fmt), ##__VA_ARGS__); \ - BT_LOGF_STR("Aborting..."); \ - abort(); \ - } \ - } while (0) - -/* - * Marks a function as being only used within a BT_ASSERT_PRE() context. - */ -# define BT_ASSERT_PRE_FUNC - -/* - * Prints the details of an unsatisfied precondition without immediately - * aborting. You should use this within a function which checks - * preconditions, but which is called from a BT_ASSERT_PRE() context, so - * that the function can still return its result for BT_ASSERT_PRE() to - * evaluate it. - * - * Example: - * - * BT_ASSERT_PRE_FUNC - * static inline bool check_complex_precond(...) - * { - * ... - * - * if (...) { - * BT_ASSERT_PRE_MSG("Invalid object: ...", ...); - * return false; - * } - * - * ... - * } - * - * ... - * - * BT_ASSERT_PRE(check_complex_precond(...), - * "Precondition is not satisfied: ...", ...); - */ -# define BT_ASSERT_PRE_MSG BT_LIB_LOGF -#else -# define BT_ASSERT_PRE(_cond, _fmt, ...) ((void) sizeof((void) (_cond), 0)) -# define BT_ASSERT_PRE_FUNC BT_UNUSED -# define BT_ASSERT_PRE_MSG(_fmt, ...) -#endif /* BT_DEV_MODE */ - -/* - * Developer mode: asserts that a given variable is not NULL. - */ -#define BT_ASSERT_PRE_NON_NULL(_obj, _obj_name) \ - BT_ASSERT_PRE((_obj) != NULL, "%s is NULL: ", _obj_name) - -/* - * Developer mode: asserts that a given object is NOT frozen. This macro - * checks the `frozen` field of _obj. - */ -#define BT_ASSERT_PRE_HOT(_obj, _obj_name, _fmt, ...) \ - BT_ASSERT_PRE(!(_obj)->frozen, "%s is frozen" _fmt, _obj_name, \ - ##__VA_ARGS__) - -/* - * Developer mode: asserts that a given index is less than a given size. - */ -#define BT_ASSERT_PRE_VALID_INDEX(_index, _length) \ - BT_ASSERT_PRE((_index) < (_length), \ - "Index is out of bounds: index=%" PRIu64 ", " \ - "count=%" PRIu64, (uint64_t) (_index), (uint64_t) (_length)) - -#endif /* BABELTRACE_ASSERT_PRE_INTERNAL_H */ diff --git a/include/babeltrace2/babeltrace-internal.h b/include/babeltrace2/babeltrace-internal.h deleted file mode 100644 index c30725d4..00000000 --- a/include/babeltrace2/babeltrace-internal.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef _BABELTRACE_INTERNAL_H -#define _BABELTRACE_INTERNAL_H - -/* - * Copyright 2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define PERROR_BUFLEN 200 - -#ifndef likely -# ifdef __GNUC__ -# define likely(x) __builtin_expect(!!(x), 1) -# else -# define likely(x) (!!(x)) -# endif -#endif - -#ifndef unlikely -# ifdef __GNUC__ -# define unlikely(x) __builtin_expect(!!(x), 0) -# else -# define unlikely(x) (!!(x)) -# endif -#endif - -#ifndef min -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef max -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef max_t -#define max_t(type, a, b) \ - ((type) (a) > (type) (b) ? (type) (a) : (type) (b)) -#endif - -static inline -bool bt_safe_to_mul_int64(int64_t a, int64_t b) -{ - if (a == 0 || b == 0) { - return true; - } - - return a < INT64_MAX / b; -} - -static inline -bool bt_safe_to_mul_uint64(uint64_t a, uint64_t b) -{ - if (a == 0 || b == 0) { - return true; - } - - return a < UINT64_MAX / b; -} - -static inline -bool bt_safe_to_add_int64(int64_t a, int64_t b) -{ - return a <= INT64_MAX - b; -} - -static inline -bool bt_safe_to_add_uint64(uint64_t a, uint64_t b) -{ - return a <= UINT64_MAX - b; -} - -/* - * Memory allocation zeroed - */ -#define zmalloc(x) calloc(1, x) - -/* - * BT_HIDDEN: set the hidden attribute for internal functions - * On Windows, symbols are local unless explicitly exported, - * see https://gcc.gnu.org/wiki/Visibility - */ -#if defined(_WIN32) || defined(__CYGWIN__) -#define BT_HIDDEN -#else -#define BT_HIDDEN __attribute__((visibility("hidden"))) -#endif - -#ifndef __STRINGIFY -#define __STRINGIFY(x) #x -#endif - -#define TOSTRING(x) __STRINGIFY(x) - -#define BT_UNUSED __attribute__((unused)) - -#endif diff --git a/include/babeltrace2/bitfield-internal.h b/include/babeltrace2/bitfield-internal.h deleted file mode 100644 index 1835b9d9..00000000 --- a/include/babeltrace2/bitfield-internal.h +++ /dev/null @@ -1,551 +0,0 @@ -#ifndef _BABELTRACE_BITFIELD_H -#define _BABELTRACE_BITFIELD_H - -/* - * Copyright 2010-2019 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include /* C99 5.2.4.2 Numerical limits */ -#include /* C99 7.16 bool type */ -#include /* C99 5.2.4.2 Numerical limits */ -#include /* Non-standard BIG_ENDIAN, LITTLE_ENDIAN, BYTE_ORDER */ - -/* - * This header strictly follows the C99 standard, except for use of the - * compiler-specific __typeof__. - */ - -/* - * This bitfield header requires the compiler representation of signed - * integers to be two's complement. - */ -#if (-1 != ~0) -#error "bitfield.h requires the compiler representation of signed integers to be two's complement." -#endif - -/* - * _bt_is_signed_type() willingly generates comparison of unsigned - * expression < 0, which is always false. Silence compiler warnings. - */ -#ifdef __GNUC__ -# define _BT_DIAG_PUSH _Pragma("GCC diagnostic push") -# define _BT_DIAG_POP _Pragma("GCC diagnostic pop") - -# define _BT_DIAG_STRINGIFY_1(x) #x -# define _BT_DIAG_STRINGIFY(x) _BT_DIAG_STRINGIFY_1(x) - -# define _BT_DIAG_IGNORE(option) \ - _Pragma(_BT_DIAG_STRINGIFY(GCC diagnostic ignored option)) -# define _BT_DIAG_IGNORE_TYPE_LIMITS _BT_DIAG_IGNORE("-Wtype-limits") -#else -# define _BT_DIAG_PUSH -# define _BT_DIAG_POP -# define _BT_DIAG_IGNORE -#endif - -#define _bt_is_signed_type(type) ((type) -1 < (type) 0) - -/* - * Produce a build-time error if the condition `cond` is non-zero. - * Evaluates as a size_t expression. - */ -#define _BT_BUILD_ASSERT(cond) \ - sizeof(struct { int f:(2 * !!(cond) - 1); }) - -/* - * Cast value `v` to an unsigned integer of the same size as `v`. - */ -#define _bt_cast_value_to_unsigned(v) \ - (sizeof(v) == sizeof(uint8_t) ? (uint8_t) (v) : \ - sizeof(v) == sizeof(uint16_t) ? (uint16_t) (v) : \ - sizeof(v) == sizeof(uint32_t) ? (uint32_t) (v) : \ - sizeof(v) == sizeof(uint64_t) ? (uint64_t) (v) : \ - _BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t))) - -/* - * Cast value `v` to an unsigned integer type of the size of type `type` - * *without* sign-extension. - * - * The unsigned cast ensures that we're not shifting a negative value, - * which is undefined in C. However, this limits the maximum type size - * of `type` to 64-bit. Generate a compile-time error if the size of - * `type` is larger than 64-bit. - */ -#define _bt_cast_value_to_unsigned_type(type, v) \ - (sizeof(type) == sizeof(uint8_t) ? \ - (uint8_t) _bt_cast_value_to_unsigned(v) : \ - sizeof(type) == sizeof(uint16_t) ? \ - (uint16_t) _bt_cast_value_to_unsigned(v) : \ - sizeof(type) == sizeof(uint32_t) ? \ - (uint32_t) _bt_cast_value_to_unsigned(v) : \ - sizeof(type) == sizeof(uint64_t) ? \ - (uint64_t) _bt_cast_value_to_unsigned(v) : \ - _BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t))) - -/* - * _bt_fill_mask evaluates to a "type" integer with all bits set. - */ -#define _bt_fill_mask(type) ((type) ~(type) 0) - -/* - * Left shift a value `v` of `shift` bits. - * - * The type of `v` can be signed or unsigned integer. - * The value of `shift` must be less than the size of `v` (in bits), - * otherwise the behavior is undefined. - * Evaluates to the result of the shift operation. - * - * According to the C99 standard, left shift of a left hand-side signed - * type is undefined if it has a negative value or if the result cannot - * be represented in the result type. This bitfield header discards the - * bits that are left-shifted beyond the result type representation, - * which is the behavior of an unsigned type left shift operation. - * Therefore, always perform left shift on an unsigned type. - * - * This macro should not be used if `shift` can be greater or equal than - * the bitwidth of `v`. See `_bt_safe_lshift`. - */ -#define _bt_lshift(v, shift) \ - ((__typeof__(v)) (_bt_cast_value_to_unsigned(v) << (shift))) - -/* - * Generate a mask of type `type` with the `length` least significant bits - * cleared, and the most significant bits set. - */ -#define _bt_make_mask_complement(type, length) \ - _bt_lshift(_bt_fill_mask(type), length) - -/* - * Generate a mask of type `type` with the `length` least significant bits - * set, and the most significant bits cleared. - */ -#define _bt_make_mask(type, length) \ - ((type) ~_bt_make_mask_complement(type, length)) - -/* - * Right shift a value `v` of `shift` bits. - * - * The type of `v` can be signed or unsigned integer. - * The value of `shift` must be less than the size of `v` (in bits), - * otherwise the behavior is undefined. - * Evaluates to the result of the shift operation. - * - * According to the C99 standard, right shift of a left hand-side signed - * type which has a negative value is implementation defined. This - * bitfield header relies on the right shift implementation carrying the - * sign bit. If the compiler implementation has a different behavior, - * emulate carrying the sign bit. - * - * This macro should not be used if `shift` can be greater or equal than - * the bitwidth of `v`. See `_bt_safe_rshift`. - */ -#if ((-1 >> 1) == -1) -#define _bt_rshift(v, shift) ((v) >> (shift)) -#else -#define _bt_rshift(v, shift) \ - ((__typeof__(v)) ((_bt_cast_value_to_unsigned(v) >> (shift)) | \ - ((v) < 0 ? _bt_make_mask_complement(__typeof__(v), \ - sizeof(v) * CHAR_BIT - (shift)) : 0))) -#endif - -/* - * Right shift a signed or unsigned integer with `shift` value being an - * arbitrary number of bits. `v` is modified by this macro. The shift - * is transformed into a sequence of `_nr_partial_shifts` consecutive - * shift operations, each of a number of bits smaller than the bitwidth - * of `v`, ending with a shift of the number of left over bits. - */ -#define _bt_safe_rshift(v, shift) \ -do { \ - unsigned long _nr_partial_shifts = (shift) / (sizeof(v) * CHAR_BIT - 1); \ - unsigned long _leftover_bits = (shift) % (sizeof(v) * CHAR_BIT - 1); \ - \ - for (; _nr_partial_shifts; _nr_partial_shifts--) \ - (v) = _bt_rshift(v, sizeof(v) * CHAR_BIT - 1); \ - (v) = _bt_rshift(v, _leftover_bits); \ -} while (0) - -/* - * Left shift a signed or unsigned integer with `shift` value being an - * arbitrary number of bits. `v` is modified by this macro. The shift - * is transformed into a sequence of `_nr_partial_shifts` consecutive - * shift operations, each of a number of bits smaller than the bitwidth - * of `v`, ending with a shift of the number of left over bits. - */ -#define _bt_safe_lshift(v, shift) \ -do { \ - unsigned long _nr_partial_shifts = (shift) / (sizeof(v) * CHAR_BIT - 1); \ - unsigned long _leftover_bits = (shift) % (sizeof(v) * CHAR_BIT - 1); \ - \ - for (; _nr_partial_shifts; _nr_partial_shifts--) \ - (v) = _bt_lshift(v, sizeof(v) * CHAR_BIT - 1); \ - (v) = _bt_lshift(v, _leftover_bits); \ -} while (0) - -/* - * bt_bitfield_write - write integer to a bitfield in native endianness - * - * Save integer to the bitfield, which starts at the "start" bit, has "len" - * bits. - * The inside of a bitfield is from high bits to low bits. - * Uses native endianness. - * For unsigned "v", pad MSB with 0 if bitfield is larger than v. - * For signed "v", sign-extend v if bitfield is larger than v. - * - * On little endian, bytes are placed from the less significant to the most - * significant. Also, consecutive bitfields are placed from lower bits to higher - * bits. - * - * On big endian, bytes are places from most significant to less significant. - * Also, consecutive bitfields are placed from higher to lower bits. - */ - -#define _bt_bitfield_write_le(ptr, type, start, length, v) \ -do { \ - __typeof__(v) _v = (v); \ - type *_ptr = (void *) (ptr); \ - unsigned long _start = (start), _length = (length); \ - type _mask, _cmask; \ - unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ - unsigned long _start_unit, _end_unit, _this_unit; \ - unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ - \ - if (!_length) \ - break; \ - \ - _end = _start + _length; \ - _start_unit = _start / _ts; \ - _end_unit = (_end + (_ts - 1)) / _ts; \ - \ - /* Trim v high bits */ \ - if (_length < sizeof(_v) * CHAR_BIT) \ - _v &= _bt_make_mask(__typeof__(_v), _length); \ - \ - /* We can now append v with a simple "or", shift it piece-wise */ \ - _this_unit = _start_unit; \ - if (_start_unit == _end_unit - 1) { \ - _mask = _bt_make_mask(type, _start % _ts); \ - if (_end % _ts) \ - _mask |= _bt_make_mask_complement(type, _end % _ts); \ - _cmask = _bt_lshift((type) (_v), _start % _ts); \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - break; \ - } \ - if (_start % _ts) { \ - _cshift = _start % _ts; \ - _mask = _bt_make_mask(type, _cshift); \ - _cmask = _bt_lshift((type) (_v), _cshift); \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - _bt_safe_rshift(_v, _ts - _cshift); \ - _start += _ts - _cshift; \ - _this_unit++; \ - } \ - for (; _this_unit < _end_unit - 1; _this_unit++) { \ - _ptr[_this_unit] = (type) _v; \ - _bt_safe_rshift(_v, _ts); \ - _start += _ts; \ - } \ - if (_end % _ts) { \ - _mask = _bt_make_mask_complement(type, _end % _ts); \ - _cmask = (type) _v; \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - } else \ - _ptr[_this_unit] = (type) _v; \ -} while (0) - -#define _bt_bitfield_write_be(ptr, type, start, length, v) \ -do { \ - __typeof__(v) _v = (v); \ - type *_ptr = (void *) (ptr); \ - unsigned long _start = (start), _length = (length); \ - type _mask, _cmask; \ - unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ - unsigned long _start_unit, _end_unit, _this_unit; \ - unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ - \ - if (!_length) \ - break; \ - \ - _end = _start + _length; \ - _start_unit = _start / _ts; \ - _end_unit = (_end + (_ts - 1)) / _ts; \ - \ - /* Trim v high bits */ \ - if (_length < sizeof(_v) * CHAR_BIT) \ - _v &= _bt_make_mask(__typeof__(_v), _length); \ - \ - /* We can now append v with a simple "or", shift it piece-wise */ \ - _this_unit = _end_unit - 1; \ - if (_start_unit == _end_unit - 1) { \ - _mask = _bt_make_mask(type, (_ts - (_end % _ts)) % _ts); \ - if (_start % _ts) \ - _mask |= _bt_make_mask_complement(type, _ts - (_start % _ts)); \ - _cmask = _bt_lshift((type) (_v), (_ts - (_end % _ts)) % _ts); \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - break; \ - } \ - if (_end % _ts) { \ - _cshift = _end % _ts; \ - _mask = _bt_make_mask(type, _ts - _cshift); \ - _cmask = _bt_lshift((type) (_v), _ts - _cshift); \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - _bt_safe_rshift(_v, _cshift); \ - _end -= _cshift; \ - _this_unit--; \ - } \ - for (; (long) _this_unit >= (long) _start_unit + 1; _this_unit--) { \ - _ptr[_this_unit] = (type) _v; \ - _bt_safe_rshift(_v, _ts); \ - _end -= _ts; \ - } \ - if (_start % _ts) { \ - _mask = _bt_make_mask_complement(type, _ts - (_start % _ts)); \ - _cmask = (type) _v; \ - _cmask &= ~_mask; \ - _ptr[_this_unit] &= _mask; \ - _ptr[_this_unit] |= _cmask; \ - } else \ - _ptr[_this_unit] = (type) _v; \ -} while (0) - -/* - * bt_bitfield_write - write integer to a bitfield in native endianness - * bt_bitfield_write_le - write integer to a bitfield in little endian - * bt_bitfield_write_be - write integer to a bitfield in big endian - */ - -#if (BYTE_ORDER == LITTLE_ENDIAN) - -#define bt_bitfield_write(ptr, type, start, length, v) \ - _bt_bitfield_write_le(ptr, type, start, length, v) - -#define bt_bitfield_write_le(ptr, type, start, length, v) \ - _bt_bitfield_write_le(ptr, type, start, length, v) - -#define bt_bitfield_write_be(ptr, type, start, length, v) \ - _bt_bitfield_write_be(ptr, unsigned char, start, length, v) - -#elif (BYTE_ORDER == BIG_ENDIAN) - -#define bt_bitfield_write(ptr, type, start, length, v) \ - _bt_bitfield_write_be(ptr, type, start, length, v) - -#define bt_bitfield_write_le(ptr, type, start, length, v) \ - _bt_bitfield_write_le(ptr, unsigned char, start, length, v) - -#define bt_bitfield_write_be(ptr, type, start, length, v) \ - _bt_bitfield_write_be(ptr, type, start, length, v) - -#else /* (BYTE_ORDER == PDP_ENDIAN) */ - -#error "Byte order not supported" - -#endif - -#define _bt_bitfield_read_le(ptr, type, start, length, vptr) \ -do { \ - __typeof__(*(vptr)) *_vptr = (vptr); \ - __typeof__(*_vptr) _v; \ - type *_ptr = (void *) (ptr); \ - unsigned long _start = (start), _length = (length); \ - type _mask, _cmask; \ - unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ - unsigned long _start_unit, _end_unit, _this_unit; \ - unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ - bool _is_signed_type; \ - \ - if (!_length) { \ - *_vptr = 0; \ - break; \ - } \ - \ - _end = _start + _length; \ - _start_unit = _start / _ts; \ - _end_unit = (_end + (_ts - 1)) / _ts; \ - \ - _this_unit = _end_unit - 1; \ - _BT_DIAG_PUSH \ - _BT_DIAG_IGNORE_TYPE_LIMITS \ - _is_signed_type = _bt_is_signed_type(__typeof__(_v)); \ - _BT_DIAG_POP \ - if (_is_signed_type \ - && (_ptr[_this_unit] & _bt_lshift((type) 1, (_end % _ts ? _end % _ts : _ts) - 1))) \ - _v = ~(__typeof__(_v)) 0; \ - else \ - _v = 0; \ - if (_start_unit == _end_unit - 1) { \ - _cmask = _ptr[_this_unit]; \ - _cmask = _bt_rshift(_cmask, _start % _ts); \ - if ((_end - _start) % _ts) { \ - _mask = _bt_make_mask(type, _end - _start); \ - _cmask &= _mask; \ - } \ - _bt_safe_lshift(_v, _end - _start); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - *_vptr = _v; \ - break; \ - } \ - if (_end % _ts) { \ - _cshift = _end % _ts; \ - _mask = _bt_make_mask(type, _cshift); \ - _cmask = _ptr[_this_unit]; \ - _cmask &= _mask; \ - _bt_safe_lshift(_v, _cshift); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - _end -= _cshift; \ - _this_unit--; \ - } \ - for (; (long) _this_unit >= (long) _start_unit + 1; _this_unit--) { \ - _bt_safe_lshift(_v, _ts); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ - _end -= _ts; \ - } \ - if (_start % _ts) { \ - _mask = _bt_make_mask(type, _ts - (_start % _ts)); \ - _cmask = _ptr[_this_unit]; \ - _cmask = _bt_rshift(_cmask, _start % _ts); \ - _cmask &= _mask; \ - _bt_safe_lshift(_v, _ts - (_start % _ts)); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - } else { \ - _bt_safe_lshift(_v, _ts); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ - } \ - *_vptr = _v; \ -} while (0) - -#define _bt_bitfield_read_be(ptr, type, start, length, vptr) \ -do { \ - __typeof__(*(vptr)) *_vptr = (vptr); \ - __typeof__(*_vptr) _v; \ - type *_ptr = (void *) (ptr); \ - unsigned long _start = (start), _length = (length); \ - type _mask, _cmask; \ - unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ - unsigned long _start_unit, _end_unit, _this_unit; \ - unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ - bool _is_signed_type; \ - \ - if (!_length) { \ - *_vptr = 0; \ - break; \ - } \ - \ - _end = _start + _length; \ - _start_unit = _start / _ts; \ - _end_unit = (_end + (_ts - 1)) / _ts; \ - \ - _this_unit = _start_unit; \ - _BT_DIAG_PUSH \ - _BT_DIAG_IGNORE_TYPE_LIMITS \ - _is_signed_type = _bt_is_signed_type(__typeof__(_v)); \ - _BT_DIAG_POP \ - if (_is_signed_type \ - && (_ptr[_this_unit] & _bt_lshift((type) 1, _ts - (_start % _ts) - 1))) \ - _v = ~(__typeof__(_v)) 0; \ - else \ - _v = 0; \ - if (_start_unit == _end_unit - 1) { \ - _cmask = _ptr[_this_unit]; \ - _cmask = _bt_rshift(_cmask, (_ts - (_end % _ts)) % _ts); \ - if ((_end - _start) % _ts) { \ - _mask = _bt_make_mask(type, _end - _start); \ - _cmask &= _mask; \ - } \ - _bt_safe_lshift(_v, _end - _start); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - *_vptr = _v; \ - break; \ - } \ - if (_start % _ts) { \ - _cshift = _start % _ts; \ - _mask = _bt_make_mask(type, _ts - _cshift); \ - _cmask = _ptr[_this_unit]; \ - _cmask &= _mask; \ - _bt_safe_lshift(_v, _ts - _cshift); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - _start += _ts - _cshift; \ - _this_unit++; \ - } \ - for (; _this_unit < _end_unit - 1; _this_unit++) { \ - _bt_safe_lshift(_v, _ts); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ - _start += _ts; \ - } \ - if (_end % _ts) { \ - _mask = _bt_make_mask(type, _end % _ts); \ - _cmask = _ptr[_this_unit]; \ - _cmask = _bt_rshift(_cmask, _ts - (_end % _ts)); \ - _cmask &= _mask; \ - _bt_safe_lshift(_v, _end % _ts); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ - } else { \ - _bt_safe_lshift(_v, _ts); \ - _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ - } \ - *_vptr = _v; \ -} while (0) - -/* - * bt_bitfield_read - read integer from a bitfield in native endianness - * bt_bitfield_read_le - read integer from a bitfield in little endian - * bt_bitfield_read_be - read integer from a bitfield in big endian - */ - -#if (BYTE_ORDER == LITTLE_ENDIAN) - -#define bt_bitfield_read(ptr, type, start, length, vptr) \ - _bt_bitfield_read_le(ptr, type, start, length, vptr) - -#define bt_bitfield_read_le(ptr, type, start, length, vptr) \ - _bt_bitfield_read_le(ptr, type, start, length, vptr) - -#define bt_bitfield_read_be(ptr, type, start, length, vptr) \ - _bt_bitfield_read_be(ptr, unsigned char, start, length, vptr) - -#elif (BYTE_ORDER == BIG_ENDIAN) - -#define bt_bitfield_read(ptr, type, start, length, vptr) \ - _bt_bitfield_read_be(ptr, type, start, length, vptr) - -#define bt_bitfield_read_le(ptr, type, start, length, vptr) \ - _bt_bitfield_read_le(ptr, unsigned char, start, length, vptr) - -#define bt_bitfield_read_be(ptr, type, start, length, vptr) \ - _bt_bitfield_read_be(ptr, type, start, length, vptr) - -#else /* (BYTE_ORDER == PDP_ENDIAN) */ - -#error "Byte order not supported" - -#endif - -#endif /* _BABELTRACE_BITFIELD_H */ diff --git a/include/babeltrace2/common-internal.h b/include/babeltrace2/common-internal.h deleted file mode 100644 index d30da975..00000000 --- a/include/babeltrace2/common-internal.h +++ /dev/null @@ -1,641 +0,0 @@ -#ifndef BABELTRACE_COMMON_INTERNAL_H -#define BABELTRACE_COMMON_INTERNAL_H - -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_COMMON_COLOR_RESET "\033[0m" -#define BT_COMMON_COLOR_BOLD "\033[1m" -#define BT_COMMON_COLOR_FG_DEFAULT "\033[39m" -#define BT_COMMON_COLOR_FG_RED "\033[31m" -#define BT_COMMON_COLOR_FG_GREEN "\033[32m" -#define BT_COMMON_COLOR_FG_YELLOW "\033[33m" -#define BT_COMMON_COLOR_FG_BLUE "\033[34m" -#define BT_COMMON_COLOR_FG_MAGENTA "\033[35m" -#define BT_COMMON_COLOR_FG_CYAN "\033[36m" -#define BT_COMMON_COLOR_FG_LIGHT_GRAY "\033[37m" -#define BT_COMMON_COLOR_BG_DEFAULT "\033[49m" -#define BT_COMMON_COLOR_BG_RED "\033[41m" -#define BT_COMMON_COLOR_BG_GREEN "\033[42m" -#define BT_COMMON_COLOR_BG_YELLOW "\033[43m" -#define BT_COMMON_COLOR_BG_BLUE "\033[44m" -#define BT_COMMON_COLOR_BG_MAGENTA "\033[45m" -#define BT_COMMON_COLOR_BG_CYAN "\033[46m" -#define BT_COMMON_COLOR_BG_LIGHT_GRAY "\033[47m" - -struct bt_common_lttng_live_url_parts { - GString *proto; - GString *hostname; - GString *target_hostname; - GString *session_name; - - /* -1 means default port */ - int port; -}; - -/* - * Checks if the current process has setuid or setgid access rights. - * Returns `true` if so. - */ -BT_HIDDEN -bool bt_common_is_setuid_setgid(void); - -/* - * Returns the system-wide plugin path, e.g. - * `/usr/lib/babeltrace2/plugins`. Do not free the return value. - */ -BT_HIDDEN -const char *bt_common_get_system_plugin_path(void); - -/* - * Returns the user plugin path, e.g. - * `/home/user/.local/lib/babeltrace2/plugins`. You need to free the - * return value. - */ -BT_HIDDEN -char *bt_common_get_home_plugin_path(void); - -/* - * Appends the list of directories in `paths` to the array `dirs`. - * `paths` is a list of directories separated by `:`. Returns 0 on - * success. - */ -BT_HIDDEN -int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs); - -/* - * Returns `true` if terminal color codes are supported for this - * process. - */ -BT_HIDDEN -bool bt_common_colors_supported(void); - -BT_HIDDEN -const char *bt_common_color_reset(void); - -BT_HIDDEN -const char *bt_common_color_bold(void); - -BT_HIDDEN -const char *bt_common_color_fg_default(void); - -BT_HIDDEN -const char *bt_common_color_fg_red(void); - -BT_HIDDEN -const char *bt_common_color_fg_green(void); - -BT_HIDDEN -const char *bt_common_color_fg_yellow(void); - -BT_HIDDEN -const char *bt_common_color_fg_blue(void); - -BT_HIDDEN -const char *bt_common_color_fg_magenta(void); - -BT_HIDDEN -const char *bt_common_color_fg_cyan(void); - -BT_HIDDEN -const char *bt_common_color_fg_light_gray(void); - -BT_HIDDEN -const char *bt_common_color_bg_default(void); - -BT_HIDDEN -const char *bt_common_color_bg_red(void); - -BT_HIDDEN -const char *bt_common_color_bg_green(void); - -BT_HIDDEN -const char *bt_common_color_bg_yellow(void); - -BT_HIDDEN -const char *bt_common_color_bg_blue(void); - -BT_HIDDEN -const char *bt_common_color_bg_magenta(void); - -BT_HIDDEN -const char *bt_common_color_bg_cyan(void); - -BT_HIDDEN -const char *bt_common_color_bg_light_gray(void); - -/* - * Returns the substring from `input` to the first character found - * in the list of characters `end_chars`, unescaping any character - * found in `escapable_chars`, and sets `*end_pos` to the position of - * the end (from `input`). The caller owns the returned GString. - */ -BT_HIDDEN -GString *bt_common_string_until(const char *input, const char *escapable_chars, - const char *end_chars, size_t *end_pos); - -/* - * Returns the quoted version of `input` for a shell. If - * `with_single_quotes` is `true`, prepends and appends the `'` prefix - * and suffix to the returned string; otherwise the caller should - * prepend and append them manually, although they are not always - * required. The caller owns the returned GString. - */ -BT_HIDDEN -GString *bt_common_shell_quote(const char *input, bool with_single_quotes); - -/* - * Returns `true` if `input` is a string made only of printable - * characters. - */ -BT_HIDDEN -bool bt_common_string_is_printable(const char *input); - -/* - * Destroys the parts of an LTTng live URL as returned by - * bt_common_parse_lttng_live_url(). - */ -BT_HIDDEN -void bt_common_destroy_lttng_live_url_parts( - struct bt_common_lttng_live_url_parts *parts); - -/* - * Parses the LTTng live URL `url` and returns its different parts. - * If there's an error, writes the error message into `*error_buf` - * up to `error_buf_size` bytes. You must destroy the returned value - * with bt_common_destroy_lttng_live_url_parts(). - */ -BT_HIDDEN -struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url( - const char *url, char *error_buf, size_t error_buf_size); - -/* - * Normalizes (in place) a star globbing pattern to be used with - * bt_common_star_glob_match(). This function always succeeds. - */ -BT_HIDDEN -void bt_common_normalize_star_glob_pattern(char *pattern); - -/* - * Returns `true` if `candidate` (of size `candidate_len`) matches - * the star globbing pattern `pattern` (of size `pattern_len`). - */ -BT_HIDDEN -bool bt_common_star_glob_match(const char *pattern, size_t pattern_len, - const char *candidate, size_t candidate_len); - -/* - * Normalizes the path `path`: - * - * * If it's a relative path, converts it to an absolute path using - * `wd` as the working directory (or the current working directory - * if `wd` is NULL). - * * Removes consecutive and trailing slashes. - * * Resolves `..` and `.` in the path (both in `path` and in `wd`). - * * Does NOT resolve symbolic links. - * - * The caller owns the returned GString. - */ -BT_HIDDEN -GString *bt_common_normalize_path(const char *path, const char *wd); - -typedef void (* bt_common_handle_custom_specifier_func)(void *priv_data, - char **buf, size_t avail_size, const char **fmt, va_list *args); - -/* - * This is a custom vsnprintf() which handles the standard conversion - * specifier as well as custom ones. - * - * `fmt` is a typical printf()-style format string, with the following - * limitations: - * - * * The `*` width specifier is not accepted. - * * The `*` precision specifier is not accepted. - * * The `j` and `t` length modifiers are not accepted. - * * The `n` format specifier is not accepted. - * * The format specifiers defined in are not accepted - * except for `PRId64`, `PRIu64`, `PRIx64`, `PRIX64`, `PRIo64`, and - * `PRIi64`. - * - * `intro` specifies which special character immediately following an - * introductory `%` character in `fmt` is used to indicate a custom - * conversion specifier. For example, if `intro` is '@', then any `%@` - * sequence in `fmt` is the beginning of a custom conversion specifier. - * - * When a custom conversion specifier is encountered in `fmt`, - * the function calls `handle_specifier`. This callback receives: - * - * `priv_data`: - * Custom, private data. - * - * `buf`: - * Address of the current buffer pointer. `*buf` is the position to - * append new data. The callback must update `*buf` when appending - * new data. The callback must ensure not to write passed the whole - * buffer passed to bt_common_custom_vsnprintf(). - * - * `avail_size`: - * Number of bytes left in whole buffer from the `*buf` point. - * - * `fmt`: - * Address of the current format string pointer. `*fmt` points to - * the introductory `%` character, which is followed by the - * character `intro`. The callback must update `*fmt` so that it - * points after the whole custom conversion specifier. - * - * `args`: - * Variable argument list. Use va_arg() to get new arguments from - * this list and update it at the same time. - * - * Because this is an internal utility, this function and its callback - * do not return error codes: they abort when there's any error (bad - * format string, for example). - */ -BT_HIDDEN -void bt_common_custom_vsnprintf(char *buf, size_t buf_size, - char intro, - bt_common_handle_custom_specifier_func handle_specifier, - void *priv_data, const char *fmt, va_list *args); - -/* - * Variadic form of bt_common_custom_vsnprintf(). - */ -BT_HIDDEN -void bt_common_custom_snprintf(char *buf, size_t buf_size, - char intro, - bt_common_handle_custom_specifier_func handle_specifier, - void *priv_data, const char *fmt, ...); - -/* - * Returns the system page size. - */ -BT_HIDDEN -size_t bt_common_get_page_size(void); - -/* - * Wraps read() function to handle EINTR and partial reads. - * On success, it returns `count` received as parameter. On error, it returns a - * value smaller than the requested `count`. - */ -static inline -ssize_t bt_common_read(int fd, void *buf, size_t count) -{ - size_t i = 0; - ssize_t ret; - - BT_ASSERT(buf); - - /* Never return an overflow value. */ - BT_ASSERT(count <= SSIZE_MAX); - - do { - ret = read(fd, buf + i, count - i); - if (ret < 0) { - if (errno == EINTR) { -#ifdef BT_LOGD_STR - BT_LOGD_STR("read() call interrupted. Retrying..."); -#endif - /* retry operation */ - continue; - } else { -#ifdef BT_LOGE_ERRNO - BT_LOGE_ERRNO("Error while reading", ": fd=%d", - fd); -#endif - goto end; - } - } - i += ret; - BT_ASSERT(i <= count); - } while (count - i > 0 && ret > 0); - -end: - if (ret >= 0) { - if (i == 0) { - ret = -1; - } else { - ret = i; - } - } - - return ret; -} - -static inline -const char *bt_common_field_class_type_string(enum bt_field_class_type class_type) -{ - switch (class_type) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - return "BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER"; - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - return "BT_FIELD_CLASS_TYPE_SIGNED_INTEGER"; - case BT_FIELD_CLASS_TYPE_REAL: - return "BT_FIELD_CLASS_TYPE_REAL"; - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - return "BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION"; - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - return "BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION"; - case BT_FIELD_CLASS_TYPE_STRING: - return "BT_FIELD_CLASS_TYPE_STRING"; - case BT_FIELD_CLASS_TYPE_STRUCTURE: - return "BT_FIELD_CLASS_TYPE_STRUCTURE"; - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - return "BT_FIELD_CLASS_TYPE_STATIC_ARRAY"; - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - return "BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY"; - case BT_FIELD_CLASS_TYPE_VARIANT: - return "BT_FIELD_CLASS_TYPE_VARIANT"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_common_field_class_integer_preferred_display_base_string(enum bt_field_class_integer_preferred_display_base base) -{ - switch (base) { - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: - return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY"; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: - return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL"; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL: - return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL"; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: - return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_common_scope_string(enum bt_scope scope) -{ - switch (scope) { - case BT_SCOPE_PACKET_CONTEXT: - return "BT_SCOPE_PACKET_CONTEXT"; - case BT_SCOPE_EVENT_COMMON_CONTEXT: - return "BT_SCOPE_EVENT_COMMON_CONTEXT"; - case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: - return "BT_SCOPE_EVENT_SPECIFIC_CONTEXT"; - case BT_SCOPE_EVENT_PAYLOAD: - return "BT_SCOPE_EVENT_PAYLOAD"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_common_event_class_log_level_string( - enum bt_event_class_log_level level) -{ - switch (level) { - case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY: - return "BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY"; - case BT_EVENT_CLASS_LOG_LEVEL_ALERT: - return "BT_EVENT_CLASS_LOG_LEVEL_ALERT"; - case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL: - return "BT_EVENT_CLASS_LOG_LEVEL_CRITICAL"; - case BT_EVENT_CLASS_LOG_LEVEL_ERROR: - return "BT_EVENT_CLASS_LOG_LEVEL_ERROR"; - case BT_EVENT_CLASS_LOG_LEVEL_WARNING: - return "BT_EVENT_CLASS_LOG_LEVEL_WARNING"; - case BT_EVENT_CLASS_LOG_LEVEL_NOTICE: - return "BT_EVENT_CLASS_LOG_LEVEL_NOTICE"; - case BT_EVENT_CLASS_LOG_LEVEL_INFO: - return "BT_EVENT_CLASS_LOG_LEVEL_INFO"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE"; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG: - return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_common_value_type_string(enum bt_value_type type) -{ - switch (type) { - case BT_VALUE_TYPE_NULL: - return "BT_VALUE_TYPE_NULL"; - case BT_VALUE_TYPE_BOOL: - return "BT_VALUE_TYPE_BOOL"; - case BT_VALUE_TYPE_UNSIGNED_INTEGER: - return "BT_VALUE_TYPE_UNSIGNED_INTEGER"; - case BT_VALUE_TYPE_SIGNED_INTEGER: - return "BT_VALUE_TYPE_SIGNED_INTEGER"; - case BT_VALUE_TYPE_REAL: - return "BT_VALUE_TYPE_REAL"; - case BT_VALUE_TYPE_STRING: - return "BT_VALUE_TYPE_STRING"; - case BT_VALUE_TYPE_ARRAY: - return "BT_VALUE_TYPE_ARRAY"; - case BT_VALUE_TYPE_MAP: - return "BT_VALUE_TYPE_MAP"; - default: - return "(unknown)"; - } -}; - -static inline -GString *bt_common_field_path_string(struct bt_field_path *path) -{ - GString *str = g_string_new(NULL); - uint64_t i; - - BT_ASSERT(path); - - if (!str) { - goto end; - } - - g_string_append_printf(str, "[%s", bt_common_scope_string( - bt_field_path_get_root_scope(path))); - - for (i = 0; i < bt_field_path_get_item_count(path); i++) { - const struct bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_const(path, i); - - switch (bt_field_path_item_get_type(fp_item)) { - case BT_FIELD_PATH_ITEM_TYPE_INDEX: - g_string_append_printf(str, ", %" PRIu64, - bt_field_path_item_index_get_index(fp_item)); - break; - case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: - g_string_append(str, ", "); - break; - default: - abort(); - } - } - - g_string_append(str, "]"); - -end: - return str; -} - -static inline -const char *bt_common_self_message_iterator_status_string( - enum bt_self_message_iterator_status status) -{ - switch (status) { - case BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN: - return "BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN"; - case BT_SELF_MESSAGE_ITERATOR_STATUS_END: - return "BT_SELF_MESSAGE_ITERATOR_STATUS_END"; - case BT_SELF_MESSAGE_ITERATOR_STATUS_OK: - return "BT_SELF_MESSAGE_ITERATOR_STATUS_OK"; - case BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR: - return "BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR"; - case BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM: - return "BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM"; - default: - return "(unknown)"; - } -}; - -#define NS_PER_S_I INT64_C(1000000000) -#define NS_PER_S_U UINT64_C(1000000000) - -static inline -int bt_common_clock_value_from_ns_from_origin( - int64_t cc_offset_seconds, uint64_t cc_offset_cycles, - uint64_t cc_freq, int64_t ns_from_origin, - uint64_t *raw_value) -{ - int ret = 0; - int64_t offset_in_ns; - uint64_t value_in_ns; - uint64_t rem_value_in_ns; - uint64_t value_periods; - uint64_t value_period_cycles; - int64_t ns_to_add; - - BT_ASSERT(raw_value); - - /* Compute offset part of requested value, in nanoseconds */ - if (!bt_safe_to_mul_int64(cc_offset_seconds, NS_PER_S_I)) { - ret = -1; - goto end; - } - - offset_in_ns = cc_offset_seconds * NS_PER_S_I; - - if (cc_freq == NS_PER_S_U) { - ns_to_add = (int64_t) cc_offset_cycles; - } else { - if (!bt_safe_to_mul_int64((int64_t) cc_offset_cycles, - NS_PER_S_I)) { - ret = -1; - goto end; - } - - ns_to_add = ((int64_t) cc_offset_cycles * NS_PER_S_I) / - (int64_t) cc_freq; - } - - if (!bt_safe_to_add_int64(offset_in_ns, ns_to_add)) { - ret = -1; - goto end; - } - - offset_in_ns += ns_to_add; - - /* Value part in nanoseconds */ - if (ns_from_origin < offset_in_ns) { - ret = -1; - goto end; - } - - value_in_ns = (uint64_t) (ns_from_origin - offset_in_ns); - - /* Number of whole clock periods in `value_in_ns` */ - value_periods = value_in_ns / NS_PER_S_U; - - /* Remaining nanoseconds in cycles + whole clock periods in cycles */ - rem_value_in_ns = value_in_ns - value_periods * NS_PER_S_U; - - if (value_periods > UINT64_MAX / cc_freq) { - ret = -1; - goto end; - } - - if (!bt_safe_to_mul_uint64(value_periods, cc_freq)) { - ret = -1; - goto end; - } - - value_period_cycles = value_periods * cc_freq; - - if (!bt_safe_to_mul_uint64(cc_freq, rem_value_in_ns)) { - ret = -1; - goto end; - } - - if (!bt_safe_to_add_uint64(cc_freq * rem_value_in_ns / NS_PER_S_U, - value_period_cycles)) { - ret = -1; - goto end; - } - - *raw_value = cc_freq * rem_value_in_ns / NS_PER_S_U + - value_period_cycles; - -end: - return ret; -} - -static inline -enum bt_self_message_iterator_status bt_common_message_iterator_status_to_self( - enum bt_message_iterator_status status) -{ - return (int) status; -} -#endif /* BABELTRACE_COMMON_INTERNAL_H */ diff --git a/include/babeltrace2/compat/fcntl-internal.h b/include/babeltrace2/compat/fcntl-internal.h deleted file mode 100644 index c5289430..00000000 --- a/include/babeltrace2/compat/fcntl-internal.h +++ /dev/null @@ -1,233 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_FCNTL_H -#define _BABELTRACE_COMPAT_FCNTL_H - -/* - * Copyright 2015 (c) - Jérémie Galarneau - * - * fcntl compatibility layer. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef BABELTRACE_HAVE_POSIX_FALLOCATE - -#include - -static inline -int bt_posix_fallocate(int fd, off_t offset, off_t len) -{ - return posix_fallocate(fd, offset, len); -} - -#elif defined(__MINGW32__) /* #ifdef BABELTRACE_HAVE_POSIX_FALLOCATE */ - -#include -#include -#include - -static inline -int bt_posix_fallocate(int fd, off_t offset, off_t len) -{ - HANDLE handle; - LARGE_INTEGER pos, file_pos, orig_end_offset, range_end; - int ret = 0; - char zero = 0; - DWORD byteswritten; - - if (offset < 0 || len <= 0) { - ret = EINVAL; - goto end; - } - - range_end.QuadPart = (__int64) offset + (__int64) len; - - /* Get a handle from the fd */ - handle = (HANDLE) _get_osfhandle(fd); - if (handle == INVALID_HANDLE_VALUE) { - ret = EBADF; - goto end; - } - - /* Get the file original end offset */ - ret = GetFileSizeEx(handle, &orig_end_offset); - if (ret == 0) { - ret = EBADF; - goto end; - } - - /* Make sure we don't truncate the file */ - if (orig_end_offset.QuadPart >= range_end.QuadPart) { - ret = 0; - goto end; - } - - /* Get the current file pointer position */ - pos.QuadPart = 0; - ret = SetFilePointerEx(handle, pos, &file_pos, FILE_CURRENT); - if (ret == 0) { - ret = EBADF; - goto end; - } - - /* Move the file pointer to the new end offset */ - ret = SetFilePointerEx(handle, range_end, NULL, FILE_BEGIN); - if (ret == 0) { - ret = EBADF; - goto end; - } - - /* Sets the physical file size to the current position */ - ret = SetEndOfFile(handle); - if (ret == 0) { - ret = EINVAL; - goto restore; - } - - /* - * Move the file pointer back 1 byte, and write a single 0 at the - * last byte of the new end offset, the operating system will zero - * fill the file. - */ - pos.QuadPart = -1; - ret = SetFilePointerEx(handle, pos, NULL, FILE_END); - if (ret == 0) { - ret = EBADF; - goto end; - } - - ret = WriteFile(handle, &zero, 1, &byteswritten, NULL); - if (ret == 0 || byteswritten != 1) { - ret = ENOSPC; - } else { - ret = 0; - } - -restore: - /* Restore the original file pointer position */ - if (!SetFilePointerEx(handle, file_pos, NULL, FILE_BEGIN)) { - /* We moved the file pointer but failed to restore it. */ - abort(); - } - -end: - return ret; -} - -#else - -#include -#include -#include - -#define BABELTRACE_FALLOCATE_BUFLEN 256 - -#ifndef min_t -#define min_t(type, a, b) \ - ((type) (a) < (type) (b) ? (type) (a) : (type) (b)) -#endif - -static inline -int bt_posix_fallocate(int fd, off_t offset, off_t len) -{ - int ret = 0; - ssize_t copy_len; - char buf[BABELTRACE_FALLOCATE_BUFLEN]; - off_t i, file_pos, orig_end_offset, range_end; - - if (offset < 0 || len < 0) { - ret = EINVAL; - goto end; - } - - range_end = offset + len; - if (range_end < 0) { - ret = EFBIG; - goto end; - } - - file_pos = lseek(fd, 0, SEEK_CUR); - if (file_pos < 0) { - ret = errno; - goto end; - } - - orig_end_offset = lseek(fd, 0, SEEK_END); - if (orig_end_offset < 0) { - ret = errno; - goto end; - } - - /* Seek back to original position. */ - ret = lseek(fd, file_pos, SEEK_SET); - if (ret) { - ret = errno; - goto end; - } - - /* - * The file may not need to grow, but we want to ensure the - * space has actually been reserved by the file system. First, copy - * the "existing" region of the file, then grow the file if needed. - */ - for (i = file_pos; i < min_t(off_t, range_end, orig_end_offset); - i += copy_len) { - ssize_t copy_ret; - - copy_len = min_t(size_t, BABELTRACE_FALLOCATE_BUFLEN, - min_t(off_t, range_end - i, - orig_end_offset - i)); - copy_ret = pread(fd, &buf, copy_len, i); - if (copy_ret < copy_len) { - /* - * The caller must handle any EINTR. - * POSIX_FALLOCATE(3) does not mention EINTR. - * However, glibc does forward to fallocate() - * directly on Linux, which may be interrupted. - */ - ret = errno; - goto end; - } - - copy_ret = pwrite(fd, &buf, copy_len, i); - if (copy_ret < copy_len) { - /* Same caveat as noted at pread() */ - ret = errno; - goto end; - } - } - - /* Grow file, as necessary. */ - memset(&buf, 0, BABELTRACE_FALLOCATE_BUFLEN); - for (i = orig_end_offset; i < range_end; i += copy_len) { - ssize_t write_ret; - - copy_len = min_t(size_t, BABELTRACE_FALLOCATE_BUFLEN, - range_end - i); - write_ret = pwrite(fd, &buf, copy_len, i); - if (write_ret < copy_len) { - ret = errno; - goto end; - } - } -end: - return ret; -} -#endif /* #else #ifdef BABELTRACE_HAVE_POSIX_FALLOCATE */ - -#endif /* _BABELTRACE_COMPAT_FCNTL_H */ diff --git a/include/babeltrace2/compat/glib-internal.h b/include/babeltrace2/compat/glib-internal.h deleted file mode 100644 index e956280d..00000000 --- a/include/babeltrace2/compat/glib-internal.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_GLIB_H -#define _BABELTRACE_COMPAT_GLIB_H - -/* - * Copyright (C) 2015 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#if GLIB_CHECK_VERSION(2,31,8) - -static inline gboolean -bt_g_hash_table_contains(GHashTable *hash_table, gconstpointer key) -{ - return g_hash_table_contains(hash_table, key); -} - -#else - -static inline gboolean -bt_g_hash_table_contains(GHashTable *hash_table, gconstpointer key) -{ - gpointer orig_key; - gpointer value; - - return g_hash_table_lookup_extended(hash_table, key, &orig_key, - &value); -} - -#endif - - -#if GLIB_CHECK_VERSION(2,29,16) - -static inline GPtrArray * -bt_g_ptr_array_new_full(guint reserved_size, - GDestroyNotify element_free_func) -{ - return g_ptr_array_new_full(reserved_size, element_free_func); -} - -#else - -static inline GPtrArray * -bt_g_ptr_array_new_full(guint reserved_size, - GDestroyNotify element_free_func) -{ - GPtrArray *array; - - array = g_ptr_array_sized_new(reserved_size); - if (!array) { - goto end; - } - g_ptr_array_set_free_func(array, element_free_func); -end: - return array; -} -#endif - -#endif /* _BABELTRACE_COMPAT_GLIB_H */ diff --git a/include/babeltrace2/compat/limits-internal.h b/include/babeltrace2/compat/limits-internal.h deleted file mode 100644 index b2761837..00000000 --- a/include/babeltrace2/compat/limits-internal.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _BABELTRACE_LIMITS_H -#define _BABELTRACE_LIMITS_H - -/* - * Copyright (C) 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#ifdef __linux__ - -#define BABELTRACE_HOST_NAME_MAX HOST_NAME_MAX - -#elif defined(__FreeBSD__) - -#define BABELTRACE_HOST_NAME_MAX MAXHOSTNAMELEN - -#elif defined(_POSIX_HOST_NAME_MAX) - -#define BABELTRACE_HOST_NAME_MAX _POSIX_HOST_NAME_MAX - -#else - -#define BABELTRACE_HOST_NAME_MAX 256 - -#endif /* __linux__, __FreeBSD__, _POSIX_HOST_NAME_MAX */ - -#endif /* _BABELTRACE_LIMITS_H */ diff --git a/include/babeltrace2/compat/memstream-internal.h b/include/babeltrace2/compat/memstream-internal.h deleted file mode 100644 index 60ff60bd..00000000 --- a/include/babeltrace2/compat/memstream-internal.h +++ /dev/null @@ -1,356 +0,0 @@ -#ifndef _BABELTRACE_FORMAT_CTF_MEMSTREAM_H -#define _BABELTRACE_FORMAT_CTF_MEMSTREAM_H - -/* - * Copyright 2012 (c) - Mathieu Desnoyers - * - * memstream compatibility layer. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef BABELTRACE_HAVE_FMEMOPEN -#include - -static inline -FILE *bt_fmemopen(void *buf, size_t size, const char *mode) -{ - return fmemopen(buf, size, mode); -} - -#else /* BABELTRACE_HAVE_FMEMOPEN */ - -#include -#include -#include -#include -#include -#include - -#ifdef __MINGW32__ - -#include -#include - -/* - * Fallback for systems which don't have fmemopen. Copy buffer to a - * temporary file, and use that file as FILE * input. - */ -static inline -FILE *bt_fmemopen(void *buf, size_t size, const char *mode) -{ - char *tmpname; - size_t len; - FILE *fp; - int ret; - - /* - * Support reading only. - */ - if (strcmp(mode, "rb") != 0) { - return NULL; - } - - /* Build a temporary filename */ - tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); - if (_mktemp(tmpname) == NULL) { - goto error_free; - } - - /* - * Open as a read/write binary temporary deleted on close file. - * Will be deleted when the last file pointer is closed. - */ - fp = fopen(tmpname, "w+bTD"); - if (!fp) { - goto error_free; - } - - /* Copy the entire buffer to the file */ - len = fwrite(buf, sizeof(char), size, fp); - if (len != size) { - goto error_close; - } - - /* Set the file pointer to the start of file */ - ret = fseek(fp, 0L, SEEK_SET); - if (ret < 0) { - perror("fseek"); - goto error_close; - } - - g_free(tmpname); - return fp; - -error_close: - ret = fclose(fp); - if (ret < 0) { - perror("close"); - } -error_free: - g_free(tmpname); - return NULL; -} - -#else /* __MINGW32__ */ - -/* - * Fallback for systems which don't have fmemopen. Copy buffer to a - * temporary file, and use that file as FILE * input. - */ -static inline -FILE *bt_fmemopen(void *buf, size_t size, const char *mode) -{ - char *tmpname; - size_t len; - FILE *fp; - int ret; - - /* - * Support reading only. - */ - if (strcmp(mode, "rb") != 0) { - return NULL; - } - - tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); - ret = mkstemp(tmpname); - if (ret < 0) { - g_free(tmpname); - return NULL; - } - /* - * We need to write to the file. - */ - fp = fdopen(ret, "wb+"); - if (!fp) { - goto error_unlink; - } - /* Copy the entire buffer to the file */ - len = fwrite(buf, sizeof(char), size, fp); - if (len != size) { - goto error_close; - } - ret = fseek(fp, 0L, SEEK_SET); - if (ret < 0) { - perror("fseek"); - goto error_close; - } - /* We keep the handle open, but can unlink the file on the VFS. */ - ret = unlink(tmpname); - if (ret < 0) { - perror("unlink"); - } - g_free(tmpname); - return fp; - -error_close: - ret = fclose(fp); - if (ret < 0) { - perror("close"); - } -error_unlink: - ret = unlink(tmpname); - if (ret < 0) { - perror("unlink"); - } - g_free(tmpname); - return NULL; -} - -#endif /* __MINGW32__ */ - -#endif /* BABELTRACE_HAVE_FMEMOPEN */ - - -#ifdef BABELTRACE_HAVE_OPEN_MEMSTREAM - -#include - -static inline -FILE *bt_open_memstream(char **ptr, size_t *sizeloc) -{ - return open_memstream(ptr, sizeloc); -} - -static inline -int bt_close_memstream(char **buf, size_t *size, FILE *fp) -{ - return fclose(fp); -} - -#else /* BABELTRACE_HAVE_OPEN_MEMSTREAM */ - -#include -#include -#include - -#ifdef __MINGW32__ - -/* - * Fallback for systems which don't have open_memstream. Create FILE * - * with bt_open_memstream, but require call to - * bt_close_memstream to flush all data written to the FILE * - * into the buffer (which we allocate). - */ -static inline -FILE *bt_open_memstream(char **ptr, size_t *sizeloc) -{ - char *tmpname; - FILE *fp; - - tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); - - if (_mktemp(tmpname) == NULL) { - goto error_free; - } - - /* - * Open as a read/write binary temporary deleted on close file. - * Will be deleted when the last file pointer is closed. - */ - fp = fopen(tmpname, "w+bTD"); - if (!fp) { - goto error_free; - } - - g_free(tmpname); - return fp; - -error_free: - g_free(tmpname); - return NULL; -} - -#else /* __MINGW32__ */ - -/* - * Fallback for systems which don't have open_memstream. Create FILE * - * with bt_open_memstream, but require call to - * bt_close_memstream to flush all data written to the FILE * - * into the buffer (which we allocate). - */ -static inline -FILE *bt_open_memstream(char **ptr, size_t *sizeloc) -{ - char *tmpname; - int ret; - FILE *fp; - - tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); - - ret = mkstemp(tmpname); - if (ret < 0) { - perror("mkstemp"); - g_free(tmpname); - return NULL; - } - fp = fdopen(ret, "wb+"); - if (!fp) { - goto error_unlink; - } - /* - * babeltrace_flush_memstream will update the buffer content - * with read from fp. No need to keep the file around, just the - * handle. - */ - ret = unlink(tmpname); - if (ret < 0) { - perror("unlink"); - } - g_free(tmpname); - return fp; - -error_unlink: - ret = unlink(tmpname); - if (ret < 0) { - perror("unlink"); - } - g_free(tmpname); - return NULL; -} - -#endif /* __MINGW32__ */ - -/* Get file size, allocate buffer, copy. */ -static inline -int bt_close_memstream(char **buf, size_t *size, FILE *fp) -{ - size_t len, n; - long pos; - int ret; - - ret = fflush(fp); - if (ret < 0) { - perror("fflush"); - return ret; - } - ret = fseek(fp, 0L, SEEK_END); - if (ret < 0) { - perror("fseek"); - return ret; - } - pos = ftell(fp); - if (ret < 0) { - perror("ftell"); - return ret; - } - *size = pos; - /* add final \0 */ - *buf = calloc(pos + 1, sizeof(char)); - if (!*buf) { - return -ENOMEM; - } - ret = fseek(fp, 0L, SEEK_SET); - if (ret < 0) { - perror("fseek"); - goto error_free; - } - /* Copy the entire file into the buffer */ - n = 0; - clearerr(fp); - while (!feof(fp) && !ferror(fp) && (*size - n > 0)) { - len = fread(*buf, sizeof(char), *size - n, fp); - n += len; - } - if (n != *size) { - ret = -1; - goto error_close; - } - ret = fclose(fp); - if (ret < 0) { - perror("fclose"); - return ret; - } - return 0; - -error_close: - ret = fclose(fp); - if (ret < 0) { - perror("fclose"); - } -error_free: - free(*buf); - *buf = NULL; - return ret; -} - -#endif /* BABELTRACE_HAVE_OPEN_MEMSTREAM */ - -#endif /* _BABELTRACE_FORMAT_CTF_MEMSTREAM_H */ diff --git a/include/babeltrace2/compat/mman-internal.h b/include/babeltrace2/compat/mman-internal.h deleted file mode 100644 index 9f494526..00000000 --- a/include/babeltrace2/compat/mman-internal.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_MMAN_H -#define _BABELTRACE_COMPAT_MMAN_H - -/* - * Copyright (C) 2015-2016 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef __MINGW32__ - -#include - -#define PROT_NONE 0x0 -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -#define PROT_EXEC 0x4 - -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xF -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS -#define MAP_FAILED ((void *) -1) - -/* - * Note that some platforms (e.g. Windows) do not allow read-only - * mappings to exceed the file's size (even within a page). - */ -void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, - off_t offset); - -int bt_munmap(void *addr, size_t length); - -#else /* __MINGW32__ */ - -#include - -static inline -void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, - off_t offset) -{ - return (void *) mmap(addr, length, prot, flags, fd, offset); -} - -static inline -int bt_munmap(void *addr, size_t length) -{ - return munmap(addr, length); -} -#endif /* __MINGW32__ */ - -#ifndef MAP_ANONYMOUS -# ifdef MAP_ANON -# define MAP_ANONYMOUS MAP_ANON -# endif -#endif - -#endif /* _BABELTRACE_COMPAT_MMAN_H */ diff --git a/include/babeltrace2/compat/socket-internal.h b/include/babeltrace2/compat/socket-internal.h deleted file mode 100644 index b1fe1597..00000000 --- a/include/babeltrace2/compat/socket-internal.h +++ /dev/null @@ -1,419 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_SOCKET_H -#define _BABELTRACE_COMPAT_SOCKET_H - -/* - * Copyright (C) 2015-2017 Michael Jeanson - * 2015 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef __MINGW32__ - -#include - -#define BT_INVALID_SOCKET INVALID_SOCKET -#define BT_SOCKET_ERROR SOCKET_ERROR -#define BT_SOCKET SOCKET - -static inline -int bt_socket_init(void) -{ - WORD verreq; - WSADATA wsa; - int ret; - - /* Request winsock 2.2 support */ - verreq = MAKEWORD(2, 2); - - ret = WSAStartup(verreq, &wsa); - if (ret != 0) { -#ifdef BT_LOGE - BT_LOGE("Winsock init failed with error: %d", ret); -#endif - goto end; - } - - if (LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2) { -#ifdef BT_LOGE_STR - BT_LOGE_STR("Could not init winsock 2.2 support"); -#endif - WSACleanup(); - ret = -1; - } - -end: - return ret; -} - -static inline -int bt_socket_fini(void) -{ - return WSACleanup(); -} - -static inline -int bt_socket_send(int sockfd, const void *buf, size_t len, int flags) -{ - return send(sockfd, buf, len, flags); -} - -static inline -int bt_socket_recv(int sockfd, void *buf, size_t len, int flags) -{ - return recv(sockfd, buf, len, flags); -} - -static inline -int bt_socket_close(int fd) -{ - return closesocket(fd); -} - -static inline -bool bt_socket_interrupted(void) -{ - /* There is no equivalent to EINTR in winsock 2.2 */ - return false; -} - -static inline -const char *bt_socket_errormsg(void) -{ - const char *errstr; - int error = WSAGetLastError(); - - switch (error) { - case WSAEINTR: - errstr = "Call interrupted"; - break; - case WSAEBADF: - errstr = "Bad file"; - break; - case WSAEACCES: - errstr = "Bad access"; - break; - case WSAEFAULT: - errstr = "Bad argument"; - break; - case WSAEINVAL: - errstr = "Invalid arguments"; - break; - case WSAEMFILE: - errstr = "Out of file descriptors"; - break; - case WSAEWOULDBLOCK: - errstr = "Call would block"; - break; - case WSAEINPROGRESS: - case WSAEALREADY: - errstr = "Blocking call in progress"; - break; - case WSAENOTSOCK: - errstr = "Descriptor is not a socket"; - break; - case WSAEDESTADDRREQ: - errstr = "Need destination address"; - break; - case WSAEMSGSIZE: - errstr = "Bad message size"; - break; - case WSAEPROTOTYPE: - errstr = "Bad protocol"; - break; - case WSAENOPROTOOPT: - errstr = "Protocol option is unsupported"; - break; - case WSAEPROTONOSUPPORT: - errstr = "Protocol is unsupported"; - break; - case WSAESOCKTNOSUPPORT: - errstr = "Socket is unsupported"; - break; - case WSAEOPNOTSUPP: - errstr = "Operation not supported"; - break; - case WSAEAFNOSUPPORT: - errstr = "Address family not supported"; - break; - case WSAEPFNOSUPPORT: - errstr = "Protocol family not supported"; - break; - case WSAEADDRINUSE: - errstr = "Address already in use"; - break; - case WSAEADDRNOTAVAIL: - errstr = "Address not available"; - break; - case WSAENETDOWN: - errstr = "Network down"; - break; - case WSAENETUNREACH: - errstr = "Network unreachable"; - break; - case WSAENETRESET: - errstr = "Network has been reset"; - break; - case WSAECONNABORTED: - errstr = "Connection was aborted"; - break; - case WSAECONNRESET: - errstr = "Connection was reset"; - break; - case WSAENOBUFS: - errstr = "No buffer space"; - break; - case WSAEISCONN: - errstr = "Socket is already connected"; - break; - case WSAENOTCONN: - errstr = "Socket is not connected"; - break; - case WSAESHUTDOWN: - errstr = "Socket has been shut down"; - break; - case WSAETOOMANYREFS: - errstr = "Too many references"; - break; - case WSAETIMEDOUT: - errstr = "Timed out"; - break; - case WSAECONNREFUSED: - errstr = "Connection refused"; - break; - case WSAELOOP: - errstr = "Loop??"; - break; - case WSAENAMETOOLONG: - errstr = "Name too long"; - break; - case WSAEHOSTDOWN: - errstr = "Host down"; - break; - case WSAEHOSTUNREACH: - errstr = "Host unreachable"; - break; - case WSAENOTEMPTY: - errstr = "Not empty"; - break; - case WSAEPROCLIM: - errstr = "Process limit reached"; - break; - case WSAEUSERS: - errstr = "Too many users"; - break; - case WSAEDQUOT: - errstr = "Bad quota"; - break; - case WSAESTALE: - errstr = "Something is stale"; - break; - case WSAEREMOTE: - errstr = "Remote error"; - break; - case WSAEDISCON: - errstr = "Disconnected"; - break; - - /* Extended Winsock errors */ - case WSASYSNOTREADY: - errstr = "Winsock library is not ready"; - break; - case WSANOTINITIALISED: - errstr = "Winsock library not initialised"; - break; - case WSAVERNOTSUPPORTED: - errstr = "Winsock version not supported"; - break; - - /* getXbyY() errors (already handled in herrmsg): - * Authoritative Answer: Host not found */ - case WSAHOST_NOT_FOUND: - errstr = "Host not found"; - break; - - /* Non-Authoritative: Host not found, or SERVERFAIL */ - case WSATRY_AGAIN: - errstr = "Host not found, try again"; - break; - - /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ - case WSANO_RECOVERY: - errstr = "Unrecoverable error in call to nameserver"; - break; - - /* Valid name, no data record of requested type */ - case WSANO_DATA: - errstr = "No data record of requested type"; - break; - - default: - errstr = "Unknown error"; - } - - return errstr; -} - -#else /* __MINGW32__ */ - -#include -#include -#include -#include - -#define BT_INVALID_SOCKET -1 -#define BT_SOCKET_ERROR -1 -#define BT_SOCKET int - -static inline -int bt_socket_init(void) -{ - return 0; -} - -static inline -int bt_socket_fini(void) -{ - return 0; -} - -static inline -int bt_socket_send(int sockfd, const void *buf, size_t len, int flags) -{ - return send(sockfd, buf, len, flags); -} - -static inline -int bt_socket_recv(int sockfd, void *buf, size_t len, int flags) -{ - return recv(sockfd, buf, len, flags); -} - -static inline -int bt_socket_close(int fd) -{ - return close(fd); -} - -static inline -bool bt_socket_interrupted(void) -{ - return (errno == EINTR); -} - -static inline -const char *bt_socket_errormsg(void) -{ - return g_strerror(errno); -} -#endif - - -/* - * This wrapper is used on platforms that have no way of ignoring SIGPIPE - * during a send(). - */ - -#ifndef MSG_NOSIGNAL -# ifdef SO_NOSIGPIPE -# define MSG_NOSIGNAL SO_NOSIGPIPE -# elif defined(__MINGW32__) -# define MSG_NOSIGNAL 0 -# endif -#endif - -#if defined(MSG_NOSIGNAL) -static inline -ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size) -{ - return bt_socket_send(fd, buffer, size, MSG_NOSIGNAL); -} -#else - -#include - -static inline -ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size) -{ - ssize_t sent; - int saved_err; - sigset_t sigpipe_set, pending_set, old_set; - int sigpipe_was_pending; - - /* - * Discard the SIGPIPE from send(), not disturbing any SIGPIPE - * that might be already pending. If a bogus SIGPIPE is sent to - * the entire process concurrently by a malicious user, it may - * be simply discarded. - */ - if (sigemptyset(&pending_set)) { - return -1; - } - /* - * sigpending returns the mask of signals that are _both_ - * blocked for the thread _and_ pending for either the thread or - * the entire process. - */ - if (sigpending(&pending_set)) { - return -1; - } - sigpipe_was_pending = sigismember(&pending_set, SIGPIPE); - /* - * If sigpipe was pending, it means it was already blocked, so - * no need to block it. - */ - if (!sigpipe_was_pending) { - if (sigemptyset(&sigpipe_set)) { - return -1; - } - if (sigaddset(&sigpipe_set, SIGPIPE)) { - return -1; - } - if (pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set)) { - return -1; - } - } - - /* Send and save errno. */ - sent = bt_socket_send(fd, buffer, size, 0); - saved_err = errno; - - if (sent == -1 && errno == EPIPE && !sigpipe_was_pending) { - struct timespec timeout = { 0, 0 }; - int ret; - - do { - ret = sigtimedwait(&sigpipe_set, NULL, - &timeout); - } while (ret == -1 && errno == EINTR); - } - if (!sigpipe_was_pending) { - if (pthread_sigmask(SIG_SETMASK, &old_set, NULL)) { - return -1; - } - } - /* Restore send() errno */ - errno = saved_err; - - return sent; -} -#endif - - -#endif /* _BABELTRACE_COMPAT_SOCKET_H */ diff --git a/include/babeltrace2/compat/stdio-internal.h b/include/babeltrace2/compat/stdio-internal.h deleted file mode 100644 index 4c737639..00000000 --- a/include/babeltrace2/compat/stdio-internal.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_STDIO_H -#define _BABELTRACE_COMPAT_STDIO_H - -/* - * Copyright (C) 2015 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#define BT_GETLINE_MINBUFLEN 64 - -static inline -char * _bt_getline_bufalloc(char **lineptr, size_t *n, size_t linelen) -{ - size_t buflen = *n; - char *buf = *lineptr; - - if (buflen >= linelen && buf != NULL) { - return buf; - } - if (buf == NULL) { - buflen = BT_GETLINE_MINBUFLEN; - } else { - buflen = buflen << 1; - if (buflen < BT_GETLINE_MINBUFLEN) { - buflen = BT_GETLINE_MINBUFLEN; - } - } - /* Check below not strictly needed, extra safety. */ - if (buflen < linelen) { - buflen = linelen; - } - buf = realloc(buf, buflen); - if (!buf) { - errno = ENOMEM; - return NULL; - } - *n = buflen; - *lineptr = buf; - return buf; -} - -/* - * Returns line length (including possible final \n, excluding final - * \0). On end of file, returns -1 with nonzero feof(stream) and errno - * set to 0. On error, returns -1 with errno set. - * - * This interface is similar to the getline(3) man page part of the - * Linux man-pages project, release 3.74. One major difference from the - * Open Group POSIX specification is that this implementation does not - * necessarily set the ferror() flag on error (because it is internal to - * libc). - */ -static inline -ssize_t bt_getline(char **lineptr, size_t *n, FILE *stream) -{ - size_t linelen = 0; - char *buf; - int found_eof = 0; - - if (lineptr == NULL || n == NULL) { - errno = EINVAL; - return -1; - } - for (;;) { - char c; - int ret; - - ret = fgetc(stream); - if (ret == EOF) { - if (ferror(stream)) { - /* ferror() is set, errno set by fgetc(). */ - return -1; - } - BT_ASSERT(feof(stream)); - found_eof = 1; - break; - } - c = (char) ret; - if (linelen == SSIZE_MAX) { - errno = EOVERFLOW; - return -1; - } - buf = _bt_getline_bufalloc(lineptr, n, ++linelen); - if (!buf) { - return -1; - } - buf[linelen - 1] = c; - if (c == '\n') { - break; - } - } - if (!linelen && found_eof) { - /* feof() is set. */ - errno = 0; - return -1; - } - buf = _bt_getline_bufalloc(lineptr, n, ++linelen); - if (!buf) { - return -1; - } - buf[linelen - 1] = '\0'; - return linelen - 1; /* Count don't include final \0. */ -} - -#endif /* _BABELTRACE_COMPAT_STDIO_H */ diff --git a/include/babeltrace2/compat/stdlib-internal.h b/include/babeltrace2/compat/stdlib-internal.h deleted file mode 100644 index bd2a2c95..00000000 --- a/include/babeltrace2/compat/stdlib-internal.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_STDLIB_H -#define _BABELTRACE_COMPAT_STDLIB_H - -/* - * Copyright (C) 2015 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * This compat wrapper can be removed and replaced by g_mkdtemp() when we bump - * the requirement on glib to version 2.30. - */ - -#include -#include -#include -#include - -#ifdef HAVE_MKDTEMP - -static inline -char *bt_mkdtemp(char *template) -{ - return mkdtemp(template); -} - -#elif GLIB_CHECK_VERSION(2,30,0) - -#include -static inline -char *bt_mkdtemp(char *template) -{ - return g_mkdtemp(template); -} - -#else - -static inline -char *bt_mkdtemp(char *template) -{ - char *ret; - - ret = mktemp(template); - if (!ret) { - goto end; - } - - if(mkdir(template, 0700)) { - ret = NULL; - goto end; - } - - ret = template; -end: - return ret; -} - -#endif - -#endif /* _BABELTRACE_COMPAT_STDLIB_H */ diff --git a/include/babeltrace2/compat/string-internal.h b/include/babeltrace2/compat/string-internal.h deleted file mode 100644 index c3325a3b..00000000 --- a/include/babeltrace2/compat/string-internal.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_STRING_H -#define _BABELTRACE_COMPAT_STRING_H - -/* - * Copyright (C) 2013 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#ifdef HAVE_STRNLEN -static inline -size_t bt_strnlen(const char *str, size_t max) -{ - return strnlen(str, max); -} -#else -static inline -size_t bt_strnlen(const char *str, size_t max) -{ - size_t ret; - const char *end; - - end = memchr(str, 0, max); - - if (end) { - ret = (size_t) (end - str); - } else { - ret = max; - } - - return ret; -} -#endif /* HAVE_STRNLEN */ - -#ifdef HAVE_STRNDUP -static inline -char *bt_strndup(const char *s, size_t n) -{ - return strndup(s, n); -} -#else -static inline -char *bt_strndup(const char *s, size_t n) -{ - char *ret; - size_t navail; - - if (!s) { - ret = NULL; - goto end; - } - - /* min() */ - navail = strlen(s) + 1; - if ((n + 1) < navail) { - navail = n + 1; - } - - ret = malloc(navail); - if (!ret) { - goto end; - } - - memcpy(ret, s, navail); - ret[navail - 1] = '\0'; -end: - return ret; -} -#endif /* HAVE_STRNDUP */ - -#endif /* _BABELTRACE_COMPAT_STRING_H */ diff --git a/include/babeltrace2/compat/time-internal.h b/include/babeltrace2/compat/time-internal.h deleted file mode 100644 index f5c09a94..00000000 --- a/include/babeltrace2/compat/time-internal.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef _BABELTRACE_INCLUDE_COMPAT_TIME_H -#define _BABELTRACE_INCLUDE_COMPAT_TIME_H - -/* - * Copyright (C) 2013 JP Ikaheimonen - * 2016 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -#include -#include - -#ifdef __MINGW32__ - -#include - -/* - * The Windows version of the time functions use one common tm structure per - * thread which makes them thread-safe. Implement the POSIX _r variants by - * copying this to a user supplied struct. - */ - -static inline -struct tm *bt_gmtime_r(const time_t *timep, struct tm *result) -{ - struct tm *local_res; - - if (!result) { - goto error; - } - - local_res = gmtime(timep); - if (!local_res) { - result = NULL; - goto error; - } - - memcpy(result, local_res, sizeof(struct tm)); - -error: - return result; -} - -static inline -struct tm *bt_localtime_r(const time_t *timep, struct tm *result) -{ - struct tm *local_res; - - if (!result) { - goto error; - } - - local_res = localtime(timep); - if (!local_res) { - result = NULL; - goto error; - } - - memcpy(result, local_res, sizeof(struct tm)); - -error: - return result; -} - -#else /* __MINGW32__ */ - -static inline -struct tm *bt_gmtime_r(const time_t *timep, struct tm *result) -{ - return gmtime_r(timep, result); -} - -static inline -struct tm *bt_localtime_r(const time_t *timep, struct tm *result) -{ - return localtime_r(timep, result); -} - -#endif /* __MINGW32__ */ -#endif /* _BABELTRACE_INCLUDE_COMPAT_TIME_H */ diff --git a/include/babeltrace2/compat/unistd-internal.h b/include/babeltrace2/compat/unistd-internal.h deleted file mode 100644 index 502e87a5..00000000 --- a/include/babeltrace2/compat/unistd-internal.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_UNISTD_H -#define _BABELTRACE_COMPAT_UNISTD_H - -/* - * (C) Copyright 2016 - Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -#include - -#ifdef __MINGW32__ -#include -#include - -#define _SC_PAGESIZE 30 - -static inline -long bt_sysconf(int name) -{ - SYSTEM_INFO si; - - switch(name) { - case _SC_PAGESIZE: - GetNativeSystemInfo(&si); - return si.dwPageSize; - default: - errno = EINVAL; - return -1; - } -} - -#else - -static inline -long bt_sysconf(int name) -{ - return sysconf(name); -} - -#endif -#endif /* _BABELTRACE_COMPAT_UNISTD_H */ diff --git a/include/babeltrace2/compat/utc-internal.h b/include/babeltrace2/compat/utc-internal.h deleted file mode 100644 index 3660e08e..00000000 --- a/include/babeltrace2/compat/utc-internal.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef _BABELTRACE_UTC_H -#define _BABELTRACE_UTC_H - -/* - * Copyright (C) 2011-2013 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -/* If set, use GNU or BSD timegm(3) */ -#if defined(_BSD_SOURCE) || defined(_SVID_SOURCE) - -static inline -time_t bt_timegm(struct tm *tm) -{ - return timegm(tm); -} - -#elif defined(__MINGW32__) - -static inline -time_t bt_timegm(struct tm *tm) -{ - return _mkgmtime(tm); -} - -#else - -#include - -/* - * This is a simple implementation of timegm() it just turns the "struct tm" into - * a GMT time_t. It does not normalize any of the fields of the "struct tm", nor - * does it set tm_wday or tm_yday. - */ - -static inline -int bt_leapyear(int year) -{ - return ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)); -} - -static inline -time_t bt_timegm(struct tm *tm) -{ - int year, month, total_days; - - int monthlen[2][12] = { - /* Days per month for a regular year */ - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - /* Days per month for a leap year */ - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - }; - - if ((tm->tm_mon >= 12) || - (tm->tm_mday >= 32) || - (tm->tm_hour >= 24) || - (tm->tm_min >= 60) || - (tm->tm_sec >= 61)) { - errno = EOVERFLOW; - return (time_t) -1; - } - - /* Add 365 days for each year since 1970 */ - total_days = 365 * (tm->tm_year - 70); - - /* Add one day for each leap year since 1970 */ - for (year = 70; year < tm->tm_year; year++) { - if (bt_leapyear(1900 + year)) { - total_days++; - } - } - - /* Add days for each remaining month */ - for (month = 0; month < tm->tm_mon; month++) { - total_days += monthlen[bt_leapyear(1900 + year)][month]; - } - - /* Add remaining days */ - total_days += tm->tm_mday - 1; - - return ((((total_days * 24) + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec); -} - -#endif - -#endif /* _BABELTRACE_UTC_H */ diff --git a/include/babeltrace2/compat/uuid-internal.h b/include/babeltrace2/compat/uuid-internal.h deleted file mode 100644 index 2c2e9295..00000000 --- a/include/babeltrace2/compat/uuid-internal.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef _BABELTRACE_COMPAT_UUID_H -#define _BABELTRACE_COMPAT_UUID_H - -/* - * Copyright (C) 2011 Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Includes final \0. */ -#define BABELTRACE_UUID_STR_LEN 37 -#define BABELTRACE_UUID_LEN 16 - -#ifdef BABELTRACE_HAVE_LIBUUID -#include - -static inline -int bt_uuid_generate(unsigned char *uuid_out) -{ - uuid_generate(uuid_out); - return 0; -} - -/* Sun's libuuid lacks const qualifiers */ -#if defined(__sun__) -static inline -int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) -{ - uuid_unparse((unsigned char *) uuid_in, str_out); - return 0; -} - -static inline -int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) -{ - return uuid_parse((char *) str_in, uuid_out); -} - -static inline -int bt_uuid_compare(const unsigned char *uuid_a, - const unsigned char *uuid_b) -{ - return uuid_compare((unsigned char *) uuid_a, - (unsigned char *) uuid_b); -} -#else -static inline -int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) -{ - uuid_unparse(uuid_in, str_out); - return 0; -} - -static inline -int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) -{ - return uuid_parse(str_in, uuid_out); -} - -static inline -int bt_uuid_compare(const unsigned char *uuid_a, - const unsigned char *uuid_b) -{ - return uuid_compare(uuid_a, uuid_b); -} -#endif - -#elif defined(BABELTRACE_HAVE_LIBC_UUID) -#include -#include -#include -#include - -static inline -int bt_uuid_generate(unsigned char *uuid_out) -{ - uint32_t status; - - uuid_create((uuid_t *) uuid_out, &status); - if (status == uuid_s_ok) - return 0; - else - return -1; -} - -static inline -int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) -{ - uint32_t status; - char *alloc_str; - int ret; - - uuid_to_string((uuid_t *) uuid_in, &alloc_str, &status); - if (status == uuid_s_ok) { - strcpy(str_out, alloc_str); - ret = 0; - } else { - ret = -1; - } - free(alloc_str); - return ret; -} - -static inline -int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) -{ - uint32_t status; - - uuid_from_string(str_in, (uuid_t *) uuid_out, &status); - if (status == uuid_s_ok) - return 0; - else - return -1; -} - -static inline -int bt_uuid_compare(const unsigned char *uuid_a, - const unsigned char *uuid_b) -{ - uint32_t status; - - uuid_compare((uuid_t *) uuid_a, (uuid_t *) uuid_b, &status); - if (status == uuid_s_ok) - return 0; - else - return -1; -} - -#elif defined(__MINGW32__) - -int bt_uuid_generate(unsigned char *uuid_out); -int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out); -int bt_uuid_parse(const char *str_in, unsigned char *uuid_out); -int bt_uuid_compare(const unsigned char *uuid_a, - const unsigned char *uuid_b); - -#else -#error "Babeltrace needs to have a UUID generator configured." -#endif - -#endif /* _BABELTRACE_COMPAT_UUID_H */ diff --git a/include/babeltrace2/compiler-internal.h b/include/babeltrace2/compiler-internal.h deleted file mode 100644 index c4c72cc4..00000000 --- a/include/babeltrace2/compiler-internal.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _BABELTRACE_COMPILER_H -#define _BABELTRACE_COMPILER_H - -/* - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include /* for offsetof */ - -#define MAYBE_BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) - -#ifndef container_of -#define container_of(ptr, type, member) \ - ({ \ - const typeof(((type *)NULL)->member) * __ptr = (ptr); \ - (type *)((char *)__ptr - offsetof(type, member)); \ - }) -#endif - -#define BT_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -#endif /* _BABELTRACE_COMPILER_H */ diff --git a/include/babeltrace2/ctf-writer/assert-pre-internal.h b/include/babeltrace2/ctf-writer/assert-pre-internal.h deleted file mode 100644 index 3ac3af1c..00000000 --- a/include/babeltrace2/ctf-writer/assert-pre-internal.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H -#define BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H - -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * The macros in this header use macros defined in - * . We don't want this header to - * automatically include because you - * need to manually define BT_LOG_TAG before including - * and it is unexpected that you - * also need to define it before including this header. - * - * This is a reminder that in order to use - * , you also need to use logging - * explicitly. - */ - -#ifndef BABELTRACE_LOGGING_INTERNAL_H -# error Include before this header. -#endif - -#include -#include -#include - -#ifdef BT_DEV_MODE -/* - * Asserts that the library precondition _cond is satisfied. - * - * If _cond is false, log a fatal statement using _fmt and the optional - * arguments using BT_LOGF(), and abort. - * - * To assert that a postcondition is satisfied or that some internal - * object/context/value is in the expected state, use BT_ASSERT(). - */ -# define BT_CTF_ASSERT_PRE(_cond, _fmt, ...) \ - do { \ - if (!(_cond)) { \ - BT_LOGF_STR("Library precondition not satisfied; error is:"); \ - BT_LOGF((_fmt), ##__VA_ARGS__); \ - BT_LOGF_STR("Aborting..."); \ - abort(); \ - } \ - } while (0) - -/* - * Marks a function as being only used within a BT_CTF_ASSERT_PRE() context. - */ -# define BT_CTF_ASSERT_PRE_FUNC - -/* - * Prints the details of an unsatisfied precondition without immediately - * aborting. You should use this within a function which checks - * preconditions, but which is called from a BT_CTF_ASSERT_PRE() context, so - * that the function can still return its result for BT_CTF_ASSERT_PRE() to - * evaluate it. - * - * Example: - * - * BT_CTF_ASSERT_PRE_FUNC - * static inline bool check_complex_precond(...) - * { - * ... - * - * if (...) { - * BT_CTF_ASSERT_PRE_MSG("Invalid object: ...", ...); - * return false; - * } - * - * ... - * } - * - * ... - * - * BT_CTF_ASSERT_PRE(check_complex_precond(...), - * "Precondition is not satisfied: ...", ...); - */ -# define BT_CTF_ASSERT_PRE_MSG BT_LOGF -#else -# define BT_CTF_ASSERT_PRE(_cond, _fmt, ...) ((void) sizeof((void) (_cond), 0)) -# define BT_CTF_ASSERT_PRE_FUNC BT_UNUSED -# define BT_CTF_ASSERT_PRE_MSG(_fmt, ...) -#endif /* BT_DEV_MODE */ - -/* - * Developer mode: asserts that a given variable is not NULL. - */ -#define BT_CTF_ASSERT_PRE_NON_NULL(_obj, _obj_name) \ - BT_CTF_ASSERT_PRE((_obj) != NULL, "%s is NULL: ", _obj_name) - -/* - * Developer mode: asserts that a given object is NOT frozen. This macro - * checks the `frozen` field of _obj. - */ -#define BT_CTF_ASSERT_PRE_HOT(_obj, _obj_name, _fmt, ...) \ - BT_CTF_ASSERT_PRE(!(_obj)->frozen, "%s is frozen" _fmt, _obj_name, \ - ##__VA_ARGS__) - -/* - * Developer mode: asserts that a given index is less than a given size. - */ -#define BT_CTF_ASSERT_PRE_VALID_INDEX(_index, _length) \ - BT_CTF_ASSERT_PRE((_index) < (_length), \ - "Index is out of bounds: index=%" PRIu64 ", " \ - "count=%" PRIu64, (uint64_t) (_index), (uint64_t) (_length)) - -#endif /* BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/attributes-internal.h b/include/babeltrace2/ctf-writer/attributes-internal.h deleted file mode 100644 index cb365128..00000000 --- a/include/babeltrace2/ctf-writer/attributes-internal.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_ATTRIBUTES_H -#define BABELTRACE_CTF_WRITER_ATTRIBUTES_H - -/* - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_create(void); - -BT_HIDDEN -void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj); - -BT_HIDDEN -int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj); - -BT_HIDDEN -const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj, - uint64_t index); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj, - uint64_t index); - -BT_HIDDEN -int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj, - const char *name, struct bt_ctf_private_value *value_obj); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name( - struct bt_ctf_private_value *attr_obj, const char *name); - -BT_HIDDEN -int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj); - -#ifdef __cplusplus -} -#endif - -#endif /* BABELTRACE_CTF_WRITER_ATTRIBUTES_H */ diff --git a/include/babeltrace2/ctf-writer/clock-class-internal.h b/include/babeltrace2/ctf-writer/clock-class-internal.h deleted file mode 100644 index a9d96721..00000000 --- a/include/babeltrace2/ctf-writer/clock-class-internal.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H -#define BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_clock_class { - struct bt_ctf_object base; - GString *name; - GString *description; - uint64_t frequency; - uint64_t precision; - int64_t offset_s; /* Offset in seconds */ - int64_t offset; /* Offset in ticks */ - unsigned char uuid[BABELTRACE_UUID_LEN]; - int uuid_set; - int absolute; - - /* - * A clock's properties can't be modified once it is added to a stream - * class. - */ - int frozen; -}; - -BT_HIDDEN -void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -bt_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a, - struct bt_ctf_clock_class *clock_class_b); - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name, - uint64_t freq); -BT_HIDDEN -const char *bt_ctf_clock_class_get_name( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, - const char *name); -BT_HIDDEN -const char *bt_ctf_clock_class_get_description( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_description( - struct bt_ctf_clock_class *clock_class, - const char *desc); -BT_HIDDEN -uint64_t bt_ctf_clock_class_get_frequency( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_frequency( - struct bt_ctf_clock_class *clock_class, uint64_t freq); -BT_HIDDEN -uint64_t bt_ctf_clock_class_get_precision( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_precision( - struct bt_ctf_clock_class *clock_class, uint64_t precision); -BT_HIDDEN -int bt_ctf_clock_class_get_offset_s( - struct bt_ctf_clock_class *clock_class, int64_t *seconds); -BT_HIDDEN -int bt_ctf_clock_class_set_offset_s( - struct bt_ctf_clock_class *clock_class, int64_t seconds); -BT_HIDDEN -int bt_ctf_clock_class_get_offset_cycles( - struct bt_ctf_clock_class *clock_class, int64_t *cycles); -BT_HIDDEN -int bt_ctf_clock_class_set_offset_cycles( - struct bt_ctf_clock_class *clock_class, int64_t cycles); -BT_HIDDEN -bt_bool bt_ctf_clock_class_is_absolute( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_is_absolute( - struct bt_ctf_clock_class *clock_class, bt_bool is_absolute); -BT_HIDDEN -const unsigned char *bt_ctf_clock_class_get_uuid( - struct bt_ctf_clock_class *clock_class); -BT_HIDDEN -int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class, - const unsigned char *uuid); - -#endif /* BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/clock-internal.h b/include/babeltrace2/ctf-writer/clock-internal.h deleted file mode 100644 index 8d3e1418..00000000 --- a/include/babeltrace2/ctf-writer/clock-internal.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H -#define BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_clock { - struct bt_ctf_object base; - struct bt_ctf_clock_class *clock_class; - uint64_t value; /* Current clock value */ -}; - -struct metadata_context; - -BT_HIDDEN -int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value); - -BT_HIDDEN -void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class, - struct metadata_context *context); - -#endif /* BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/event-class-internal.h b/include/babeltrace2/ctf-writer/event-class-internal.h deleted file mode 100644 index c67184ae..00000000 --- a/include/babeltrace2/ctf-writer/event-class-internal.h +++ /dev/null @@ -1,395 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H -#define BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_event_class_common { - struct bt_ctf_object base; - struct bt_ctf_field_type_common *context_field_type; - struct bt_ctf_field_type_common *payload_field_type; - int frozen; - - /* - * This flag indicates if the event class is valid. A valid - * event class is _always_ frozen. However, an event class - * may be frozen, but not valid yet. This is okay, as long as - * no events are created out of this event class. - */ - int valid; - - /* Attributes */ - GString *name; - int64_t id; - int log_level; - GString *emf_uri; -}; - -BT_HIDDEN -void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class); - -BT_HIDDEN -void bt_ctf_event_class_common_set_native_byte_order( - struct bt_ctf_event_class_common *event_class, int byte_order); - -static inline -struct bt_ctf_stream_class_common *bt_ctf_event_class_common_borrow_stream_class( - struct bt_ctf_event_class_common *event_class) -{ - BT_ASSERT(event_class); - return (void *) bt_ctf_object_borrow_parent(&event_class->base); -} - -typedef struct bt_ctf_field_type_common *(*bt_ctf_field_type_structure_create_func)(); - -BT_HIDDEN -int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class, - const char *name, bt_ctf_object_release_func release_func, - bt_ctf_field_type_structure_create_func ft_struct_create_func); - -BT_HIDDEN -void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj); - -BT_HIDDEN -int bt_ctf_event_class_common_validate_single_clock_class( - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_clock_class **expected_clock_class); - -static inline -const char *bt_ctf_event_class_common_get_name( - struct bt_ctf_event_class_common *event_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT(event_class->name); - return event_class->name->str; -} - -static inline -int64_t bt_ctf_event_class_common_get_id( - struct bt_ctf_event_class_common *event_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->id; -} - -static inline -int bt_ctf_event_class_common_set_id( - struct bt_ctf_event_class_common *event_class, uint64_t id_param) -{ - int ret = 0; - int64_t id = (int64_t) id_param; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (event_class->frozen) { - BT_LOGW("Invalid parameter: event class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, - bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - ret = -1; - goto end; - } - - if (id < 0) { - BT_LOGW("Invalid parameter: invalid event class's ID: " - "addr=%p, name=\"%s\", id=%" PRIu64, - event_class, - bt_ctf_event_class_common_get_name(event_class), - id_param); - ret = -1; - goto end; - } - - event_class->id = id; - BT_LOGV("Set event class's ID: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), id); - -end: - return ret; -} - -static inline -int bt_ctf_event_class_common_get_log_level( - struct bt_ctf_event_class_common *event_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->log_level; -} - -static inline -int bt_ctf_event_class_common_set_log_level( - struct bt_ctf_event_class_common *event_class, int log_level) -{ - int ret = 0; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (event_class->frozen) { - BT_LOGW("Invalid parameter: event class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, - bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - ret = -1; - goto end; - } - - switch (log_level) { - case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG: - break; - default: - BT_LOGW("Invalid parameter: unknown event class log level: " - "addr=%p, name=\"%s\", id=%" PRId64 ", log-level=%d", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), log_level); - ret = -1; - goto end; - } - - event_class->log_level = log_level; - BT_LOGV("Set event class's log level: " - "addr=%p, name=\"%s\", id=%" PRId64 ", log-level=%s", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), - bt_ctf_event_class_log_level_string(log_level)); - -end: - return ret; -} - -static inline -const char *bt_ctf_event_class_common_get_emf_uri( - struct bt_ctf_event_class_common *event_class) -{ - const char *emf_uri = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - - if (event_class->emf_uri->len > 0) { - emf_uri = event_class->emf_uri->str; - } - - return emf_uri; -} - -static inline -int bt_ctf_event_class_common_set_emf_uri( - struct bt_ctf_event_class_common *event_class, - const char *emf_uri) -{ - int ret = 0; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (emf_uri && strlen(emf_uri) == 0) { - BT_LOGW_STR("Invalid parameter: EMF URI is empty."); - ret = -1; - goto end; - } - - if (event_class->frozen) { - BT_LOGW("Invalid parameter: event class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - ret = -1; - goto end; - } - - if (emf_uri) { - g_string_assign(event_class->emf_uri, emf_uri); - BT_LOGV("Set event class's EMF URI: " - "addr=%p, name=\"%s\", id=%" PRId64 ", emf-uri=\"%s\"", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), emf_uri); - } else { - g_string_assign(event_class->emf_uri, ""); - BT_LOGV("Reset event class's EMF URI: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - } - -end: - return ret; -} - -static inline -struct bt_ctf_field_type_common *bt_ctf_event_class_common_borrow_context_field_type( - struct bt_ctf_event_class_common *event_class) -{ - struct bt_ctf_field_type_common *context_ft = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - - if (!event_class->context_field_type) { - BT_LOGV("Event class has no context field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - goto end; - } - - context_ft = event_class->context_field_type; - -end: - return context_ft; -} - -static inline -int bt_ctf_event_class_common_set_context_field_type( - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_field_type_common *context_ft) -{ - int ret = 0; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (event_class->frozen) { - BT_LOGW("Invalid parameter: event class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class)); - ret = -1; - goto end; - } - - if (context_ft && bt_ctf_field_type_common_get_type_id(context_ft) != - BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid parameter: event class's context field type must be a structure: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "context-ft-id=%s", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), - bt_ctf_field_type_id_string( - bt_ctf_field_type_common_get_type_id(context_ft))); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(event_class->context_field_type); - event_class->context_field_type = context_ft; - bt_ctf_object_get_ref(event_class->context_field_type); - BT_LOGV("Set event class's context field type: " - "event-class-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", context-ft-addr=%p", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), context_ft); - -end: - return ret; -} - -static inline -struct bt_ctf_field_type_common *bt_ctf_event_class_common_borrow_payload_field_type( - struct bt_ctf_event_class_common *event_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->payload_field_type; -} - -static inline -int bt_ctf_event_class_common_set_payload_field_type( - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_field_type_common *payload_ft) -{ - int ret = 0; - - if (!event_class) { - BT_LOGW_STR("Invalid parameter: event class is NULL."); - ret = -1; - goto end; - } - - if (payload_ft && bt_ctf_field_type_common_get_type_id(payload_ft) != - BT_CTF_FIELD_TYPE_ID_STRUCT) { - BT_LOGW("Invalid parameter: event class's payload field type must be a structure: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "payload-ft-addr=%p, payload-ft-id=%s", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), payload_ft, - bt_ctf_field_type_id_string( - bt_ctf_field_type_common_get_type_id(payload_ft))); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(event_class->payload_field_type); - event_class->payload_field_type = payload_ft; - bt_ctf_object_get_ref(event_class->payload_field_type); - BT_LOGV("Set event class's payload field type: " - "event-class-addr=%p, event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", payload-ft-addr=%p", - event_class, bt_ctf_event_class_common_get_name(event_class), - bt_ctf_event_class_common_get_id(event_class), payload_ft); -end: - return ret; -} - -#endif /* BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/event-internal.h b/include/babeltrace2/ctf-writer/event-internal.h deleted file mode 100644 index 97039878..00000000 --- a/include/babeltrace2/ctf-writer/event-internal.h +++ /dev/null @@ -1,269 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H -#define BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_stream_class; -struct bt_ctf_stream_pos; -struct metadata_context; - -struct bt_ctf_event_common { - struct bt_ctf_object base; - struct bt_ctf_event_class_common *class; - struct bt_ctf_field_wrapper *header_field; - struct bt_ctf_field_common *stream_event_context_field; - struct bt_ctf_field_common *context_field; - struct bt_ctf_field_common *payload_field; - int frozen; -}; - -BT_HIDDEN -int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event); - -BT_HIDDEN -void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event, - bool is_frozen); - -#ifdef BT_DEV_MODE -# define bt_ctf_event_common_validate _bt_ctf_event_common_validate -# define bt_ctf_event_common_set_is_frozen _bt_ctf_event_common_set_is_frozen -#else -# define bt_ctf_event_common_validate(_event) 0 -# define bt_ctf_event_common_set_is_frozen(_event, _is_frozen) -#endif - -#define BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(_event, _name) \ - BT_CTF_ASSERT_PRE_HOT((_event), (_name), ": event-addr=%p", (_event)) - -static inline -struct bt_ctf_event_class_common *bt_ctf_event_common_borrow_class( - struct bt_ctf_event_common *event) -{ - BT_ASSERT(event); - return event->class; -} - -typedef void *(*create_field_func)(void *); -typedef void (*release_field_func)(void *); -typedef void *(*create_header_field_func)(void *, void *); -typedef void (*release_header_field_func)(void *, void *); - -BT_HIDDEN -int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event, - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_clock_class *init_expected_clock_class, - bool is_shared_with_parent, bt_ctf_object_release_func release_func, - bt_ctf_validation_flag_copy_field_type_func field_type_copy_func, - bool must_be_in_trace, - int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_field_type, - struct bt_ctf_field_type_common *event_header_field_type), - create_field_func create_field_func, - release_field_func release_field_func, - create_header_field_func create_header_field_func, - release_header_field_func release_header_field_func); - -static inline -struct bt_ctf_field_common *bt_ctf_event_common_borrow_payload( - struct bt_ctf_event_common *event) -{ - struct bt_ctf_field_common *payload = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - - if (!event->payload_field) { - BT_LOGV("Event has no current payload field: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - goto end; - } - - payload = event->payload_field; - -end: - return payload; -} - -static inline -struct bt_ctf_field_common *bt_ctf_event_common_borrow_header( - struct bt_ctf_event_common *event) -{ - struct bt_ctf_field_common *header = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - - if (!event->header_field) { - BT_LOGV("Event has no current header field: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - goto end; - } - - header = event->header_field->field; - -end: - return header; -} - -static inline -struct bt_ctf_field_common *bt_ctf_event_common_borrow_context( - struct bt_ctf_event_common *event) -{ - struct bt_ctf_field_common *context = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - - if (!event->context_field) { - BT_LOGV("Event has no current context field: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - goto end; - } - - context = event->context_field; - -end: - return context; -} - -static inline -struct bt_ctf_field_common *bt_ctf_event_common_borrow_stream_event_context( - struct bt_ctf_event_common *event) -{ - struct bt_ctf_field_common *stream_event_context = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); - - if (!event->stream_event_context_field) { - BT_LOGV("Event has no current stream event context field: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, bt_ctf_event_class_common_get_name(event->class), - bt_ctf_event_class_common_get_id(event->class)); - goto end; - } - - stream_event_context = event->stream_event_context_field; - -end: - return stream_event_context; -} - -static inline -void bt_ctf_event_common_finalize(struct bt_ctf_object *obj, - void (*field_release_func)(void *), - void (*header_field_release_func)(void *, struct bt_ctf_event_common *)) -{ - struct bt_ctf_event_common *event = (void *) obj; - - BT_LOGD("Destroying event: addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64, - event, - event->class ? bt_ctf_event_class_common_get_name(event->class) : NULL, - event->class ? bt_ctf_event_class_common_get_id(event->class) : INT64_C(-1)); - - if (event->header_field) { - BT_LOGD_STR("Releasing event's header field."); - header_field_release_func(event->header_field, event); - } - - if (event->stream_event_context_field) { - BT_LOGD_STR("Releasing event's stream event context field."); - field_release_func(event->stream_event_context_field); - } - - if (event->context_field) { - BT_LOGD_STR("Releasing event's context field."); - field_release_func(event->context_field); - } - - if (event->payload_field) { - BT_LOGD_STR("Releasing event's payload field."); - field_release_func(event->payload_field); - } - - /* - * Leave this after calling header_field_release_func() because - * this function receives the event object and could need its - * class to perform some cleanup. - */ - if (!event->base.parent) { - /* - * Event was keeping a reference to its class since it shared no - * common ancestor with it to guarantee they would both have the - * same lifetime. - */ - bt_ctf_object_put_ref(event->class); - } -} - -struct bt_ctf_event { - struct bt_ctf_event_common common; -}; - -struct bt_ctf_event_class { - struct bt_ctf_event_class_common common; -}; - -BT_HIDDEN -int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class, - struct metadata_context *context); - -BT_HIDDEN -int bt_ctf_event_serialize(struct bt_ctf_event *event, - struct bt_ctfser *pos, - enum bt_ctf_byte_order native_byte_order); - -static inline -struct bt_ctf_stream_class *bt_ctf_event_class_borrow_stream_class( - struct bt_ctf_event_class *event_class) -{ - return BT_CTF_FROM_COMMON(bt_ctf_event_class_common_borrow_stream_class( - BT_CTF_TO_COMMON(event_class))); -} - -#endif /* BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/field-path-internal.h b/include/babeltrace2/ctf-writer/field-path-internal.h deleted file mode 100644 index d018e409..00000000 --- a/include/babeltrace2/ctf-writer/field-path-internal.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL -#define BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL - -/* - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include - -struct bt_ctf_field_path { - struct bt_ctf_object base; - enum bt_ctf_scope root; - - /* - * Array of integers (int) indicating the index in either - * structures, variants, arrays, or sequences that make up - * the path to a field type. -1 means the "current element - * of an array or sequence type". - */ - GArray *indexes; -}; - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_path_create(void); - -BT_HIDDEN -void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path); - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_path_copy( - struct bt_ctf_field_path *path); - -BT_HIDDEN enum bt_ctf_scope bt_ctf_field_path_get_root_scope( - const struct bt_ctf_field_path *field_path); - -BT_HIDDEN int64_t bt_ctf_field_path_get_index_count( - const struct bt_ctf_field_path *field_path); - -BT_HIDDEN int bt_ctf_field_path_get_index( - const struct bt_ctf_field_path *field_path, uint64_t index); - -#endif /* BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL */ diff --git a/include/babeltrace2/ctf-writer/field-types-internal.h b/include/babeltrace2/ctf-writer/field-types-internal.h deleted file mode 100644 index 757170fa..00000000 --- a/include/babeltrace2/ctf-writer/field-types-internal.h +++ /dev/null @@ -1,790 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H -#define BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(_ft, _type_id, _name) \ - BT_CTF_ASSERT_PRE(((struct bt_ctf_field_type_common *) (_ft))->id == (_type_id), \ - _name " has the wrong type ID: expected-type-id=%s, " \ - "ft-addr=%p", bt_ctf_field_type_id_string(_type_id), (_ft)) - -#define BT_CTF_ASSERT_PRE_CTF_FT_HOT(_ft, _name) \ - BT_CTF_ASSERT_PRE_HOT((_ft), (_name), ": ft-addr=%p", (_ft)) - -#define BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(_ft, _index) \ - (&g_array_index(((struct bt_ctf_field_type_common_structure *) (_ft))->fields, \ - struct bt_ctf_field_type_common_structure_field, (_index))) - -#define BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(_ft, _index) \ - (&g_array_index(((struct bt_ctf_field_type_common_variant *) (_ft))->choices, \ - struct bt_ctf_field_type_common_variant_choice, (_index))) - -struct bt_ctf_field_common; -struct bt_ctf_field_type_common; - -typedef void (*bt_ctf_field_type_common_method_freeze)( - struct bt_ctf_field_type_common *); -typedef int (*bt_ctf_field_type_common_method_validate)( - struct bt_ctf_field_type_common *); -typedef void (*bt_ctf_field_type_common_method_set_byte_order)( - struct bt_ctf_field_type_common *, enum bt_ctf_byte_order); -typedef struct bt_ctf_field_type_common *(*bt_ctf_field_type_common_method_copy)( - struct bt_ctf_field_type_common *); -typedef int (*bt_ctf_field_type_common_method_compare)( - struct bt_ctf_field_type_common *, - struct bt_ctf_field_type_common *); - -struct bt_ctf_field_type_common_methods { - bt_ctf_field_type_common_method_freeze freeze; - bt_ctf_field_type_common_method_validate validate; - bt_ctf_field_type_common_method_set_byte_order set_byte_order; - bt_ctf_field_type_common_method_copy copy; - bt_ctf_field_type_common_method_compare compare; -}; - -struct bt_ctf_field_type_common { - struct bt_ctf_object base; - enum bt_ctf_field_type_id id; - unsigned int alignment; - - /* Virtual table */ - struct bt_ctf_field_type_common_methods *methods; - - /* - * A type can't be modified once it is added to an event or after a - * a field has been instanciated from it. - */ - int frozen; - - /* - * This flag indicates if the field type is valid. A valid - * field type is _always_ frozen. All the nested field types of - * a valid field type are also valid (and thus frozen). - */ - int valid; - - /* - * Specialized data for either CTF IR or CTF writer APIs. - * Having this here ensures that: - * - * * The type-specific common data is always found at the same - * offset when the common API has a `struct - * bt_ctf_field_type_common *` so that you can cast it to `struct - * bt_ctf_field_type_common_integer *` for example and access the - * common integer field type fields. - * - * * The specific CTF IR and CTF writer APIs can access their - * specific field type fields in this union at an offset known - * at build time. This avoids a pointer to specific data so - * that all the fields, common or specific, of a CTF IR - * integer field type or of a CTF writer integer field type, - * for example, are contained within the same contiguous block - * of memory. - */ - union { - struct { - } ir; - struct { - void *serialize_func; - } writer; - } spec; -}; - -struct bt_ctf_field_type_common_integer { - struct bt_ctf_field_type_common common; - - /* Owned by this */ - struct bt_ctf_clock_class *mapped_clock_class; - - enum bt_ctf_byte_order user_byte_order; - bt_bool is_signed; - unsigned int size; - enum bt_ctf_integer_base base; - enum bt_ctf_string_encoding encoding; -}; - -struct bt_ctf_enumeration_mapping { - union { - uint64_t _unsigned; - int64_t _signed; - } range_start; - union { - uint64_t _unsigned; - int64_t _signed; - } range_end; - GQuark string; -}; - -struct bt_ctf_field_type_common_enumeration { - struct bt_ctf_field_type_common common; - - /* Owned by this */ - struct bt_ctf_field_type_common_integer *container_ft; - - /* Array of `struct bt_ctf_enumeration_mapping *`, owned by this */ - GPtrArray *entries; - - /* Only set during validation */ - bt_bool has_overlapping_ranges; -}; - -enum bt_ctf_field_type_enumeration_mapping_iterator_type { - CTF_ITERATOR_BY_NAME, - CTF_ITERATOR_BY_SIGNED_VALUE, - CTF_ITERATOR_BY_UNSIGNED_VALUE, -}; - -struct bt_ctf_field_type_enumeration_mapping_iterator { - struct bt_ctf_object base; - - /* Owned by this */ - struct bt_ctf_field_type_common_enumeration *enumeration_ft; - - enum bt_ctf_field_type_enumeration_mapping_iterator_type type; - int index; - union { - GQuark name_quark; - int64_t signed_value; - uint64_t unsigned_value; - } u; -}; - -struct bt_ctf_field_type_common_floating_point { - struct bt_ctf_field_type_common common; - enum bt_ctf_byte_order user_byte_order; - unsigned int exp_dig; - unsigned int mant_dig; -}; - -struct bt_ctf_field_type_common_structure_field { - GQuark name; - - /* Owned by this */ - struct bt_ctf_field_type_common *type; -}; - -struct bt_ctf_field_type_common_structure { - struct bt_ctf_field_type_common common; - GHashTable *field_name_to_index; - - /* - * Array of `struct bt_ctf_field_type_common_structure_field`, - * owned by this - */ - GArray *fields; -}; - -struct bt_ctf_field_type_common_variant_choice_range { - union { - int64_t i; - uint64_t u; - } lower; - union { - int64_t i; - uint64_t u; - } upper; -}; - -struct bt_ctf_field_type_common_variant_choice { - GQuark name; - - /* Owned by this */ - struct bt_ctf_field_type_common *type; - - /* Array of `struct bt_ctf_field_type_common_variant_choice_range` */ - GArray *ranges; -}; - -struct bt_ctf_field_type_common_variant { - struct bt_ctf_field_type_common common; - GString *tag_name; - bool choices_up_to_date; - - /* Owned by this */ - struct bt_ctf_field_type_common_enumeration *tag_ft; - - /* Owned by this */ - struct bt_ctf_field_path *tag_field_path; - - GHashTable *choice_name_to_index; - - /* - * Array of `struct bt_ctf_field_type_common_variant_choice`, - * owned by this */ - GArray *choices; -}; - -struct bt_ctf_field_type_common_array { - struct bt_ctf_field_type_common common; - - /* Owned by this */ - struct bt_ctf_field_type_common *element_ft; - - unsigned int length; -}; - -struct bt_ctf_field_type_common_sequence { - struct bt_ctf_field_type_common common; - - /* Owned by this */ - struct bt_ctf_field_type_common *element_ft; - - GString *length_field_name; - - /* Owned by this */ - struct bt_ctf_field_path *length_field_path; -}; - -struct bt_ctf_field_type_common_string { - struct bt_ctf_field_type_common common; - enum bt_ctf_string_encoding encoding; -}; - -typedef struct bt_ctf_field_common *(* bt_ctf_field_common_create_func)( - struct bt_ctf_field_type_common *); - -BT_HIDDEN -void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft, - bool init_bo, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_integer_initialize( - struct bt_ctf_field_type_common *ft, - unsigned int size, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *container_ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_string_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_structure_initialize( - struct bt_ctf_field_type_common *ft, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_array_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft, - unsigned int length, bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft, - const char *length_field_name, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_variant_initialize( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *tag_ft, - const char *tag_name, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_type_common_methods *methods); - -BT_HIDDEN -void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj); - -BT_HIDDEN -void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_validate_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_validate_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_array_validate_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_validate_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_validate_recursive( - struct bt_ctf_field_type_common *type); - -BT_HIDDEN -int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -bt_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft, - bt_bool is_signed); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft, - unsigned int size); - -BT_HIDDEN -enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft, - enum bt_ctf_integer_base base); - -BT_HIDDEN -enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft, - enum bt_ctf_string_encoding encoding); - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_set_mapped_clock_class( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_find_mappings_by_name( - struct bt_ctf_field_type_common *ft, const char *name); - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value( - struct bt_ctf_field_type_common *ft, int64_t value); - -BT_HIDDEN -struct bt_ctf_field_type_enumeration_mapping_iterator * -bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value( - struct bt_ctf_field_type_common *ft, uint64_t value); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( - struct bt_ctf_field_type_common *ft, uint64_t index, - const char **mapping_name, int64_t *range_begin, - int64_t *range_end); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( - struct bt_ctf_field_type_common *ft, uint64_t index, - const char **mapping_name, uint64_t *range_begin, - uint64_t *range_end); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_enumeration_borrow_container_field_type( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_signed_add_mapping( - struct bt_ctf_field_type_common *ft, const char *string, - int64_t range_start, int64_t range_end); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_unsigned_add_mapping( - struct bt_ctf_field_type_common *ft, const char *string, - uint64_t range_start, uint64_t range_end); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_enumeration_get_mapping_count( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_get_exponent_digits( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_set_exponent_digits( - struct bt_ctf_field_type_common *ft, - unsigned int exponent_digits); - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_get_mantissa_digits( - struct bt_ctf_field_type_common *type); - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_set_mantissa_digits( - struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_replace_field( - struct bt_ctf_field_type_common *ft, - const char *field_name, - struct bt_ctf_field_type_common *field_type); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *field_type, - const char *field_name); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_structure_get_field_count( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_borrow_field_by_index( - struct bt_ctf_field_type_common *ft, - const char **field_name, - struct bt_ctf_field_type_common **field_type, uint64_t index); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_structure_borrow_field_type_by_name( - struct bt_ctf_field_type_common *ft, const char *name); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_tag_field_type( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -const char *bt_ctf_field_type_common_variant_get_tag_name( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_name( - struct bt_ctf_field_type_common *ft, const char *name); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *field_type, - const char *field_name); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_update_choices( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_by_name( - struct bt_ctf_field_type_common *ft, - const char *field_name); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_variant_get_field_count( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_borrow_field_by_index( - struct bt_ctf_field_type_common *ft, - const char **field_name, - struct bt_ctf_field_type_common **field_type, uint64_t index); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_array_borrow_element_field_type( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_array_set_element_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_sequence_borrow_element_field_type( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_set_element_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *element_ft); - -BT_HIDDEN -const char *bt_ctf_field_type_common_sequence_get_length_field_name( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft, - enum bt_ctf_string_encoding encoding); - -BT_HIDDEN -int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *type); - -BT_HIDDEN -int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft, - unsigned int alignment); - -BT_HIDDEN -enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_signed( - struct bt_ctf_field_type_common_variant *var_ft, - int64_t tag_value); - -BT_HIDDEN -struct bt_ctf_field_type_common * -bt_ctf_field_type_common_variant_borrow_field_type_unsigned( - struct bt_ctf_field_type_common_variant *var_ft, - uint64_t tag_value); - -BT_HIDDEN -struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_get_field_name_index( - struct bt_ctf_field_type_common *ft, const char *name); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_get_field_name_index( - struct bt_ctf_field_type_common *ft, const char *name); - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_set_length_field_path( - struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_field_path( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_path *path); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_set_tag_field_type( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_field_type_common *tag_ft); - -BT_HIDDEN -void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_freeze_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_structure_freeze_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_variant_freeze_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_array_freeze_recursive( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_freeze_recursive( - struct bt_ctf_field_type_common *type); - -BT_HIDDEN -void bt_ctf_field_type_common_integer_set_byte_order( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_enumeration_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_floating_point_set_byte_order( - struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_structure_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_variant_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_array_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_field_type_common_sequence_set_byte_order_recursive( - struct bt_ctf_field_type_common *ft, - enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_floating_point_compare( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_enumeration_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_structure_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_variant_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_array_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_sequence_compare_recursive( - struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a, - struct bt_ctf_field_type_common *ft_b); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index( - struct bt_ctf_field_type_common *ft, int index); - -BT_HIDDEN -int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft, - const char *name); - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path( - struct bt_ctf_field_type_common *ft); - -BT_HIDDEN -int bt_ctf_field_type_common_validate_single_clock_class( - struct bt_ctf_field_type_common *ft, - struct bt_ctf_clock_class **expected_clock_class); - -BT_HIDDEN -int64_t bt_ctf_field_type_common_variant_find_choice_index( - struct bt_ctf_field_type_common *ft, uint64_t uval, - bool is_signed); - -BT_HIDDEN -int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type, - struct metadata_context *context); - -BT_HIDDEN -struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft); - -#endif /* BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/field-wrapper-internal.h b/include/babeltrace2/ctf-writer/field-wrapper-internal.h deleted file mode 100644 index dcdf9684..00000000 --- a/include/babeltrace2/ctf-writer/field-wrapper-internal.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H -#define BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H - -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_ctf_field_wrapper { - struct bt_ctf_object base; - - /* Owned by this */ - struct bt_ctf_field_common *field; -}; - -BT_HIDDEN -struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data); - -BT_HIDDEN -void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field); - -BT_HIDDEN -struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create( - struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft); - -#endif /* BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/fields-internal.h b/include/babeltrace2/ctf-writer/fields-internal.h deleted file mode 100644 index e9bd3962..00000000 --- a/include/babeltrace2/ctf-writer/fields-internal.h +++ /dev/null @@ -1,851 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H -#define BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(_field, _type_id, _name) \ - BT_CTF_ASSERT_PRE((_field)->type->id == ((int) (_type_id)), \ - _name " has the wrong type ID: expected-type-id=%s, " \ - "field-addr=%p", \ - bt_ctf_field_type_id_string((int) (_type_id)), (_field)) - -#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(_field, _name) \ - BT_CTF_ASSERT_PRE(bt_ctf_field_common_is_set_recursive(_field), \ - _name " is not set: field-addr=%p", (_field)) - -#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(_field, _name) \ - BT_CTF_ASSERT_PRE_HOT((_field), (_name), ": field-addr=%p", (_field)) - -struct bt_ctf_field_common; - -typedef void (*bt_ctf_field_common_method_set_is_frozen)(struct bt_ctf_field_common *, - bool); -typedef int (*bt_ctf_field_common_method_validate)(struct bt_ctf_field_common *); -typedef struct bt_ctf_field_common *(*bt_ctf_field_common_method_copy)( - struct bt_ctf_field_common *); -typedef bt_bool (*bt_ctf_field_common_method_is_set)(struct bt_ctf_field_common *); -typedef void (*bt_ctf_field_common_method_reset)(struct bt_ctf_field_common *); - -struct bt_ctf_field_common_methods { - bt_ctf_field_common_method_set_is_frozen set_is_frozen; - bt_ctf_field_common_method_validate validate; - bt_ctf_field_common_method_copy copy; - bt_ctf_field_common_method_is_set is_set; - bt_ctf_field_common_method_reset reset; -}; - -struct bt_ctf_field_common { - struct bt_ctf_object base; - struct bt_ctf_field_type_common *type; - struct bt_ctf_field_common_methods *methods; - bool payload_set; - bool frozen; - - /* - * Specialized data for either CTF IR or CTF writer APIs. - * See comment in `field-types-internal.h` for more details. - */ - union { - struct { - } ir; - struct { - void *serialize_func; - } writer; - } spec; -}; - -struct bt_ctf_field_common_integer { - struct bt_ctf_field_common common; - union { - int64_t signd; - uint64_t unsignd; - } payload; -}; - -struct bt_ctf_field_common_floating_point { - struct bt_ctf_field_common common; - double payload; -}; - -struct bt_ctf_field_common_structure { - struct bt_ctf_field_common common; - - /* Array of `struct bt_ctf_field_common *`, owned by this */ - GPtrArray *fields; -}; - -struct bt_ctf_field_common_variant { - struct bt_ctf_field_common common; - - union { - uint64_t u; - int64_t i; - } tag_value; - - /* Weak: belongs to `choices` below */ - struct bt_ctf_field_common *current_field; - - /* Array of `struct bt_ctf_field_common *`, owned by this */ - GPtrArray *fields; -}; - -struct bt_ctf_field_common_array { - struct bt_ctf_field_common common; - - /* Array of `struct bt_ctf_field_common *`, owned by this */ - GPtrArray *elements; -}; - -struct bt_ctf_field_common_sequence { - struct bt_ctf_field_common common; - - /* - * This is the true sequence field's length: its value can be - * less than `elements->len` below because we never shrink the - * array of elements to avoid reallocation. - */ - uint64_t length; - - /* Array of `struct bt_ctf_field_common *`, owned by this */ - GPtrArray *elements; -}; - -struct bt_ctf_field_common_string { - struct bt_ctf_field_common common; - GArray *buf; - size_t size; -}; - -BT_HIDDEN -struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field); - -BT_HIDDEN -int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_release_func); - -BT_HIDDEN -int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_destroy_func); - -BT_HIDDEN -int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - GDestroyNotify field_destroy_func); - -BT_HIDDEN -int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods, - bt_ctf_field_common_create_func field_create_func, - GDestroyNotify field_release_func); - -BT_HIDDEN -int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *type, - bool is_shared, bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods); - -BT_HIDDEN -int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field); - -BT_HIDDEN -int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field, - bool is_frozen); - -BT_HIDDEN -void bt_ctf_field_common_structure_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen); - -BT_HIDDEN -void bt_ctf_field_common_variant_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen); - -BT_HIDDEN -void bt_ctf_field_common_array_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen); - -BT_HIDDEN -void bt_ctf_field_common_sequence_set_is_frozen_recursive( - struct bt_ctf_field_common *field, bool is_frozen); - -BT_HIDDEN -void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field, - bool is_frozen); - -BT_HIDDEN -bt_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field); - -BT_HIDDEN -bt_bool bt_ctf_field_common_structure_is_set_recursive( - struct bt_ctf_field_common *field); - -BT_HIDDEN -bt_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -bt_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field); - -BT_HIDDEN -bt_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field); - -#ifdef BT_DEV_MODE -# define bt_ctf_field_common_validate_recursive _bt_ctf_field_common_validate_recursive -# define bt_ctf_field_common_set_is_frozen_recursive _bt_ctf_field_common_set_is_frozen_recursive -# define bt_ctf_field_common_is_set_recursive _bt_ctf_field_common_is_set_recursive -# define bt_ctf_field_common_reset_recursive _bt_ctf_field_common_reset_recursive -# define bt_ctf_field_common_set _bt_ctf_field_common_set -#else -# define bt_ctf_field_common_validate_recursive(_field) (-1) -# define bt_ctf_field_common_set_is_frozen_recursive(_field, _is_frozen) -# define bt_ctf_field_common_is_set_recursive(_field) (BT_FALSE) -# define bt_ctf_field_common_reset_recursive(_field) -# define bt_ctf_field_common_set(_field, _val) -#endif - -BT_ASSERT_FUNC -static inline bool field_type_common_has_known_id( - struct bt_ctf_field_type_common *ft) -{ - return (int) ft->id > BT_CTF_FIELD_TYPE_ID_UNKNOWN || - (int) ft->id < BT_CTF_FIELD_TYPE_ID_NR; -} - -static inline -int _bt_ctf_field_common_validate_recursive(struct bt_ctf_field_common *field) -{ - int ret = 0; - - if (!field) { - BT_CTF_ASSERT_PRE_MSG("%s", "Invalid field: field is NULL."); - ret = -1; - goto end; - } - - BT_ASSERT(field_type_common_has_known_id(field->type)); - - if (field->methods->validate) { - ret = field->methods->validate(field); - } - -end: - return ret; -} - -static inline -void _bt_ctf_field_common_reset_recursive(struct bt_ctf_field_common *field) -{ - BT_ASSERT(field); - BT_ASSERT(field->methods->reset); - field->methods->reset(field); -} - -static inline -void _bt_ctf_field_common_set(struct bt_ctf_field_common *field, bool value) -{ - BT_ASSERT(field); - field->payload_set = value; -} - -static inline -bt_bool _bt_ctf_field_common_is_set_recursive(struct bt_ctf_field_common *field) -{ - bt_bool is_set = BT_FALSE; - - if (!field) { - goto end; - } - - BT_ASSERT(field_type_common_has_known_id(field->type)); - BT_ASSERT(field->methods->is_set); - is_set = field->methods->is_set(field); - -end: - return is_set; -} - -static inline -void bt_ctf_field_common_initialize(struct bt_ctf_field_common *field, - struct bt_ctf_field_type_common *ft, bool is_shared, - bt_ctf_object_release_func release_func, - struct bt_ctf_field_common_methods *methods) -{ - BT_ASSERT(field); - BT_ASSERT(ft); - bt_ctf_object_init(&field->base, is_shared, release_func); - field->methods = methods; - field->type = (void *) bt_ctf_object_get_ref(ft); -} - -static inline -struct bt_ctf_field_type_common *bt_ctf_field_common_borrow_type( - struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_type_common *ret = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); - ret = field->type; - return ret; -} - -static inline -int64_t bt_ctf_field_common_sequence_get_length(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_SEQUENCE, - "Field"); - return (int64_t) sequence->length; -} - -static inline -int bt_ctf_field_common_sequence_set_length(struct bt_ctf_field_common *field, - uint64_t length, bt_ctf_field_common_create_func field_create_func) -{ - int ret = 0; - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); - BT_CTF_ASSERT_PRE(((int64_t) length) >= 0, - "Invalid sequence length (too large): length=%" PRId64, - length); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "Sequence field"); - - if (unlikely(length > sequence->elements->len)) { - /* Make more room */ - struct bt_ctf_field_type_common_sequence *sequence_ft; - uint64_t cur_len = sequence->elements->len; - uint64_t i; - - g_ptr_array_set_size(sequence->elements, length); - sequence_ft = BT_CTF_FROM_COMMON(sequence->common.type); - - for (i = cur_len; i < sequence->elements->len; i++) { - struct bt_ctf_field_common *elem_field = - field_create_func(sequence_ft->element_ft); - - if (!elem_field) { - ret = -1; - goto end; - } - - BT_ASSERT(!sequence->elements->pdata[i]); - sequence->elements->pdata[i] = elem_field; - } - } - - sequence->length = length; - -end: - return ret; -} - -static inline -struct bt_ctf_field_common *bt_ctf_field_common_structure_borrow_field_by_name( - struct bt_ctf_field_common *field, const char *name) -{ - struct bt_ctf_field_common *ret = NULL; - GQuark field_quark; - struct bt_ctf_field_type_common_structure *structure_ft; - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - size_t index; - GHashTable *field_name_to_index; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Structure field"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Field name"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRUCT, "Field"); - structure_ft = BT_CTF_FROM_COMMON(field->type); - field_name_to_index = structure_ft->field_name_to_index; - field_quark = g_quark_from_string(name); - if (!g_hash_table_lookup_extended(field_name_to_index, - GUINT_TO_POINTER(field_quark), - NULL, (gpointer *) &index)) { - BT_LOGV("Invalid parameter: no such field in structure field's type: " - "struct-field-addr=%p, struct-ft-addr=%p, name=\"%s\"", - field, field->type, name); - goto error; - } - - ret = structure->fields->pdata[index]; - BT_ASSERT(ret); - -error: - return ret; -} - -static inline -struct bt_ctf_field_common *bt_ctf_field_common_structure_borrow_field_by_index( - struct bt_ctf_field_common *field, uint64_t index) -{ - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Structure field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRUCT, "Field"); - BT_CTF_ASSERT_PRE(index < structure->fields->len, - "Index is out of bound: struct-field-addr=%p, " - "index=%" PRIu64 ", count=%u", field, index, - structure->fields->len); - return structure->fields->pdata[index]; -} - -static inline -struct bt_ctf_field_common *bt_ctf_field_common_array_borrow_field( - struct bt_ctf_field_common *field, uint64_t index) -{ - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Array field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_ARRAY, - "Field"); - BT_CTF_ASSERT_PRE(index < array->elements->len, - "Index is out of bound: array-field-addr=%p, " - "index=%" PRIu64 ", count=%u", field, - index, array->elements->len); - return array->elements->pdata[(size_t) index]; -} - -static inline -struct bt_ctf_field_common *bt_ctf_field_common_sequence_borrow_field( - struct bt_ctf_field_common *field, uint64_t index) -{ - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_SEQUENCE, - "Field"); - BT_CTF_ASSERT_PRE(index < sequence->length, - "Index is out of bound: seq-field-addr=%p, " - "index=%" PRIu64 ", count=%u", field, index, - sequence->elements->len); - return sequence->elements->pdata[(size_t) index]; -} - -static inline -int bt_ctf_field_common_variant_set_tag(struct bt_ctf_field_common *variant_field, - uint64_t tag_uval, bool is_signed) -{ - int ret = 0; - int64_t choice_index; - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); - - BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, - BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); - - /* Find matching index in variant field's type */ - choice_index = bt_ctf_field_type_common_variant_find_choice_index( - variant_field->type, tag_uval, is_signed); - if (choice_index < 0) { - ret = -1; - goto end; - } - - /* Select corresponding field */ - BT_ASSERT(choice_index < variant->fields->len); - variant->current_field = variant->fields->pdata[choice_index]; - variant->tag_value.u = tag_uval; - -end: - return ret; -} - -static inline -struct bt_ctf_field_common *bt_ctf_field_common_variant_borrow_current_field( - struct bt_ctf_field_common *variant_field) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); - - BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, - BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); - BT_CTF_ASSERT_PRE(variant->current_field, - "Variant field has no current field: field-addr=%p", variant_field); - return variant->current_field; -} - -static inline -int bt_ctf_field_common_variant_get_tag_signed(struct bt_ctf_field_common *variant_field, - int64_t *tag) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); - - BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, - BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); - BT_CTF_ASSERT_PRE(variant->current_field, - "Variant field has no current field: field-addr=%p", variant_field); - *tag = variant->tag_value.i; - return 0; -} - -static inline -int bt_ctf_field_common_variant_get_tag_unsigned(struct bt_ctf_field_common *variant_field, - uint64_t *tag) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); - - BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, - BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); - BT_CTF_ASSERT_PRE(variant->current_field, - "Variant field has no current field: field-addr=%p", variant_field); - *tag = variant->tag_value.u; - return 0; -} - -static inline -int bt_ctf_field_common_floating_point_get_value(struct bt_ctf_field_common *field, - double *value) -{ - struct bt_ctf_field_common_floating_point *floating_point = - BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Floating point number field"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Floating point number field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_FLOAT, "Field"); - *value = floating_point->payload; - return 0; -} - -static inline -int bt_ctf_field_common_floating_point_set_value(struct bt_ctf_field_common *field, - double value) -{ - struct bt_ctf_field_common_floating_point *floating_point = - BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "Floating point number field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "Floating point number field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_FLOAT, "Field"); - floating_point->payload = value; - bt_ctf_field_common_set(field, true); - return 0; -} - -static inline -const char *bt_ctf_field_common_string_get_value(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRING, "Field"); - return (const char *) string->buf->data; -} - -static inline -int bt_ctf_field_common_string_clear(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_string *string_field = BT_CTF_FROM_COMMON(field); - - BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRING, "Field"); - string_field->size = 0; - bt_ctf_field_common_set(field, true); - return 0; -} - -static inline -int bt_ctf_field_common_string_append_len(struct bt_ctf_field_common *field, - const char *value, unsigned int length) -{ - struct bt_ctf_field_common_string *string_field = BT_CTF_FROM_COMMON(field); - char *data; - size_t new_size; - - BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRING, "Field"); - - /* Make sure no null bytes are appended */ - BT_CTF_ASSERT_PRE(memchr(value, '\0', length) == NULL, - "String value to append contains a null character: " - "partial-value=\"%.32s\", length=%u", value, length); - - new_size = string_field->size + length; - - if (unlikely(new_size + 1 > string_field->buf->len)) { - g_array_set_size(string_field->buf, new_size + 1); - } - - data = string_field->buf->data; - memcpy(data + string_field->size, value, length); - ((char *) string_field->buf->data)[new_size] = '\0'; - string_field->size = new_size; - bt_ctf_field_common_set(field, true); - return 0; -} - -static inline -int bt_ctf_field_common_string_append(struct bt_ctf_field_common *field, - const char *value) -{ - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - return bt_ctf_field_common_string_append_len(field, value, - strlen(value)); -} - -static inline -int bt_ctf_field_common_string_set_value(struct bt_ctf_field_common *field, - const char *value) -{ - BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); - BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); - BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, - BT_CTF_FIELD_TYPE_ID_STRING, "Field"); - bt_ctf_field_common_string_clear(field); - return bt_ctf_field_common_string_append_len(field, - value, strlen(value)); -} - -static inline -void bt_ctf_field_common_finalize(struct bt_ctf_field_common *field) -{ - BT_ASSERT(field); - BT_LOGD_STR("Putting field's type."); - bt_ctf_object_put_ref(field->type); -} - -static inline -void bt_ctf_field_common_integer_finalize(struct bt_ctf_field_common *field) -{ - BT_ASSERT(field); - BT_LOGD("Finalizing common integer field object: addr=%p", field); - bt_ctf_field_common_finalize(field); -} - -static inline -void bt_ctf_field_common_floating_point_finalize(struct bt_ctf_field_common *field) -{ - BT_ASSERT(field); - BT_LOGD("Finalizing common floating point number field object: addr=%p", field); - bt_ctf_field_common_finalize(field); -} - -static inline -void bt_ctf_field_common_structure_finalize_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - BT_LOGD("Finalizing common structure field object: addr=%p", field); - bt_ctf_field_common_finalize(field); - - if (structure->fields) { - g_ptr_array_free(structure->fields, TRUE); - } -} - -static inline -void bt_ctf_field_common_variant_finalize_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - BT_LOGD("Finalizing common variant field object: addr=%p", field); - bt_ctf_field_common_finalize(field); - - if (variant->fields) { - g_ptr_array_free(variant->fields, TRUE); - } -} - -static inline -void bt_ctf_field_common_array_finalize_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - BT_LOGD("Finalizing common array field object: addr=%p", field); - bt_ctf_field_common_finalize(field); - - if (array->elements) { - g_ptr_array_free(array->elements, TRUE); - } -} - -static inline -void bt_ctf_field_common_sequence_finalize_recursive(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - BT_LOGD("Finalizing common sequence field object: addr=%p", field); - bt_ctf_field_common_finalize(field); - - if (sequence->elements) { - g_ptr_array_free(sequence->elements, TRUE); - } -} - -static inline -void bt_ctf_field_common_string_finalize(struct bt_ctf_field_common *field) -{ - struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); - - BT_ASSERT(field); - BT_LOGD("Finalizing common string field object: addr=%p", field); - bt_ctf_field_common_finalize(field); - - if (string->buf) { - g_array_free(string->buf, TRUE); - } -} - -BT_CTF_ASSERT_PRE_FUNC -static inline bool value_is_in_range_signed(unsigned int size, int64_t value) -{ - bool ret = true; - int64_t min_value, max_value; - - min_value = -(1ULL << (size - 1)); - max_value = (1ULL << (size - 1)) - 1; - if (value < min_value || value > max_value) { - BT_LOGF("Value is out of bounds: value=%" PRId64 ", " - "min-value=%" PRId64 ", max-value=%" PRId64, - value, min_value, max_value); - ret = false; - } - - return ret; -} - -BT_CTF_ASSERT_PRE_FUNC -static inline bool value_is_in_range_unsigned(unsigned int size, uint64_t value) -{ - bool ret = true; - int64_t max_value; - - max_value = (size == 64) ? UINT64_MAX : ((uint64_t) 1 << size) - 1; - if (value > max_value) { - BT_LOGF("Value is out of bounds: value=%" PRIu64 ", " - "max-value=%" PRIu64, - value, max_value); - ret = false; - } - - return ret; -} - -struct bt_ctf_field_enumeration { - struct bt_ctf_field_common common; - struct bt_ctf_field_common_integer *container; -}; - -struct bt_ctf_field_variant { - struct bt_ctf_field_common_variant common; - struct bt_ctf_field_enumeration *tag; -}; - -BT_HIDDEN -int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field, - struct bt_ctfser *ctfser, - enum bt_ctf_byte_order native_byte_order); - -BT_HIDDEN -int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field, - const char *name, struct bt_ctf_field *value); - -BT_HIDDEN -struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container( - struct bt_ctf_field *field); - -static inline -bt_bool bt_ctf_field_is_set_recursive(struct bt_ctf_field *field) -{ - return bt_ctf_field_common_is_set_recursive((void *) field); -} - -#endif /* BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/functor-internal.h b/include/babeltrace2/ctf-writer/functor-internal.h deleted file mode 100644 index 0d6cda19..00000000 --- a/include/babeltrace2/ctf-writer/functor-internal.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H -#define BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -BT_HIDDEN -void value_exists(gpointer element, gpointer search_query); - -#endif /* BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/object-internal.h b/include/babeltrace2/ctf-writer/object-internal.h deleted file mode 100644 index 365b1126..00000000 --- a/include/babeltrace2/ctf-writer/object-internal.h +++ /dev/null @@ -1,317 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H -#define BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H - -/* - * Copyright 2015 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_ctf_object; - -typedef void (*bt_ctf_object_release_func)(struct bt_ctf_object *); -typedef void (*bt_ctf_object_parent_is_owner_listener_func)( - struct bt_ctf_object *); - -static inline -void *bt_ctf_object_get_no_null_check(struct bt_ctf_object *obj); - -static inline -void bt_ctf_object_put_no_null_check(struct bt_ctf_object *obj); - -/* - * Babeltrace object base. - * - * All objects publicly exposed by Babeltrace APIs must contain this - * object as their first member. - */ -struct bt_ctf_object { - /* - * True if this object is shared, that is, it has a reference - * count. - */ - bool is_shared; - - /* - * Current reference count. - */ - unsigned long long ref_count; - - /* - * Release function called when the object's reference count - * falls to zero. For an object with a parent, this function is - * bt_ctf_object_with_parent_release_func(), which calls - * `spec_release_func` below if there's no current parent. - */ - bt_ctf_object_release_func release_func; - - /* - * Specific release function called by - * bt_ctf_object_with_parent_release_func() or directly by a - * parent object. - */ - bt_ctf_object_release_func spec_release_func; - - /* - * Optional callback for an object with a parent, called by - * bt_ctf_object_with_parent_release_func() to indicate to the - * object that its parent is its owner. - */ - bt_ctf_object_parent_is_owner_listener_func - parent_is_owner_listener_func; - - /* - * Optional parent object. - */ - struct bt_ctf_object *parent; -}; - -static inline -unsigned long long bt_ctf_object_get_ref_count(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - return obj->ref_count; -} - -static inline -struct bt_ctf_object *bt_ctf_object_borrow_parent(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - return obj->parent; -} - -static inline -struct bt_ctf_object *bt_ctf_object_get_parent(struct bt_ctf_object *obj) -{ - struct bt_ctf_object *parent = bt_ctf_object_borrow_parent(obj); - - if (parent) { - bt_ctf_object_get_no_null_check(parent); - } - - return parent; -} - -static inline -void bt_ctf_object_set_parent(struct bt_ctf_object *child, struct bt_ctf_object *parent) -{ - BT_ASSERT(child); - BT_ASSERT(child->is_shared); - -#ifdef BT_LOGV - BT_LOGV("Setting object's parent: addr=%p, parent-addr=%p", - child, parent); -#endif - - /* - * It is assumed that a "child" having a parent is publicly - * reachable. Therefore, a reference to its parent must be - * taken. The reference to the parent will be released once the - * object's reference count falls to zero. - */ - if (parent) { - BT_ASSERT(!child->parent); - child->parent = parent; - bt_ctf_object_get_no_null_check(parent); - } else { - if (child->parent) { - bt_ctf_object_put_no_null_check(child->parent); - } - - child->parent = NULL; - } -} - -static inline -void bt_ctf_object_try_spec_release(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->spec_release_func); - - if (bt_ctf_object_get_ref_count(obj) == 0) { - obj->spec_release_func(obj); - } -} - -static inline -void bt_ctf_object_with_parent_release_func(struct bt_ctf_object *obj) -{ - if (obj->parent) { - /* - * Keep our own copy of the parent address because `obj` - * could be destroyed in - * obj->parent_is_owner_listener_func(). - */ - struct bt_ctf_object *parent = obj->parent; - -#ifdef BT_LOGV - BT_LOGV("Releasing parented object: addr=%p, ref-count=%llu, " - "parent-addr=%p, parent-ref-count=%llu", - obj, obj->ref_count, - parent, parent->ref_count); -#endif - - if (obj->parent_is_owner_listener_func) { - /* - * Object has a chance to destroy itself here - * under certain conditions and notify its - * parent. At this point the parent is - * guaranteed to exist because it's not put yet. - */ - obj->parent_is_owner_listener_func(obj); - } - - /* The release function will be invoked by the parent. */ - bt_ctf_object_put_no_null_check(parent); - } else { - bt_ctf_object_try_spec_release(obj); - } -} - -static inline -void bt_ctf_object_init(struct bt_ctf_object *obj, bool is_shared, - bt_ctf_object_release_func release_func) -{ - BT_ASSERT(obj); - BT_ASSERT(!is_shared || release_func); - obj->is_shared = is_shared; - obj->release_func = release_func; - obj->parent_is_owner_listener_func = NULL; - obj->spec_release_func = NULL; - obj->parent = NULL; - obj->ref_count = 1; -} - -static inline -void bt_ctf_object_init_shared(struct bt_ctf_object *obj, - bt_ctf_object_release_func release_func) -{ - bt_ctf_object_init(obj, true, release_func); -} - -static inline -void bt_ctf_object_init_unique(struct bt_ctf_object *obj) -{ - bt_ctf_object_init(obj, false, NULL); -} - -static inline -void bt_ctf_object_init_shared_with_parent(struct bt_ctf_object *obj, - bt_ctf_object_release_func spec_release_func) -{ - BT_ASSERT(obj); - BT_ASSERT(spec_release_func); - bt_ctf_object_init_shared(obj, bt_ctf_object_with_parent_release_func); - obj->spec_release_func = spec_release_func; -} - -static inline -void bt_ctf_object_set_parent_is_owner_listener_func(struct bt_ctf_object *obj, - bt_ctf_object_parent_is_owner_listener_func func) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->spec_release_func); - ((struct bt_ctf_object *) obj)->parent_is_owner_listener_func = func; -} - -static inline -void bt_ctf_object_inc_ref_count(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - obj->ref_count++; - BT_ASSERT(obj->ref_count != 0); -} - -static inline -void *bt_ctf_object_get_no_null_check_no_parent_check(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - -#ifdef BT_LOGV - BT_LOGV("Incrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count + 1, - obj, obj->ref_count, obj->ref_count + 1); -#endif - - bt_ctf_object_inc_ref_count(obj); - return obj; -} - -static inline -void *bt_ctf_object_get_no_null_check(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - - if (unlikely(obj->parent && bt_ctf_object_get_ref_count(obj) == 0)) { -#ifdef BT_LOGV - BT_LOGV("Incrementing object's parent's reference count: " - "addr=%p, parent-addr=%p", obj, obj->parent); -#endif - - bt_ctf_object_get_no_null_check(obj->parent); - } - -#ifdef BT_LOGV - BT_LOGV("Incrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count + 1, - obj, obj->ref_count, obj->ref_count + 1); -#endif - - bt_ctf_object_inc_ref_count(obj); - return obj; -} - -static inline -void bt_ctf_object_put_no_null_check(struct bt_ctf_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->ref_count > 0); - -#ifdef BT_LOGV - BT_LOGV("Decrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count - 1, - obj, obj->ref_count, obj->ref_count - 1); -#endif - - obj->ref_count--; - - if (obj->ref_count == 0) { - BT_ASSERT(obj->release_func); - obj->release_func(obj); - } -} - -#endif /* BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/object-pool-internal.h b/include/babeltrace2/ctf-writer/object-pool-internal.h deleted file mode 100644 index 1217ef12..00000000 --- a/include/babeltrace2/ctf-writer/object-pool-internal.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H -#define BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H - -/* - * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * This is a generic object pool to avoid memory allocation/deallocation - * for objects of which the lifespan is typically short, but which are - * created a lot. - * - * The object pool, thanks to two user functions, knows how to allocate - * a brand new object in memory when the pool is empty and how to - * destroy an object when we destroy the pool. - * - * The object pool's user is responsible for: - * - * * Setting whatever references the object needs to keep and reset some - * properties _after_ calling bt_ctf_object_pool_create_object(). This is - * typically done in the bt_*_create() function which calls - * bt_ctf_object_pool_create_object() (which could call the user-provided - * allocation function if the pool is empty) and then sets the - * appropriate properties on the possibly recycled object. - * - * * Releasing whatever references the object keeps _before_ calling - * bt_ctf_object_pool_recycle_object(). This is typically done in a custom - * bt_*_recycle() function which does the necessary before calling - * bt_ctf_object_pool_recycle_object() with an object ready to be reused - * at any time. - */ - -#include -#include - -typedef void *(*bt_ctf_object_pool_new_object_func)(void *data); -typedef void *(*bt_ctf_object_pool_destroy_object_func)(void *obj, void *data); - -struct bt_ctf_object_pool { - /* - * Container of recycled objects, owned by this. The array's size - * is the pool's capacity. - */ - GPtrArray *objects; - - /* - * Pool's size, that is, number of elements in the array above, - * starting at index 0, which exist as recycled objects. - */ - size_t size; - - /* User functions */ - struct { - /* Allocate a new object in memory */ - bt_ctf_object_pool_new_object_func new_object; - - /* Free direct and indirect memory occupied by object */ - bt_ctf_object_pool_destroy_object_func destroy_object; - } funcs; - - /* User data passed to user functions */ - void *data; -}; - -/* - * Initializes an object pool which is already allocated. - */ -int bt_ctf_object_pool_initialize(struct bt_ctf_object_pool *pool, - bt_ctf_object_pool_new_object_func new_object_func, - bt_ctf_object_pool_destroy_object_func destroy_object_func, - void *data); - -/* - * Finalizes an object pool without deallocating it. - */ -void bt_ctf_object_pool_finalize(struct bt_ctf_object_pool *pool); - -/* - * Creates an object from an object pool. If the pool is empty, this - * function calls the "new" user function to allocate a new object - * before returning it. Otherwise this function returns a recycled - * object, removing it from the pool. - * - * The returned object is owned by the caller. - */ -static inline -void *bt_ctf_object_pool_create_object(struct bt_ctf_object_pool *pool) -{ - struct bt_ctf_object *obj; - - BT_ASSERT(pool); - -#ifdef BT_LOGV - BT_LOGV("Creating object from pool: pool-addr=%p, pool-size=%zu, pool-cap=%u", - pool, pool->size, pool->objects->len); -#endif - - if (pool->size > 0) { - /* Pick one from the pool */ - pool->size--; - obj = pool->objects->pdata[pool->size]; - pool->objects->pdata[pool->size] = NULL; - goto end; - } - - /* Pool is empty: create a brand new object */ -#ifdef BT_LOGV - BT_LOGV("Pool is empty: allocating new object: pool-addr=%p", - pool); -#endif - - obj = pool->funcs.new_object(pool->data); - -end: -#ifdef BT_LOGV - BT_LOGV("Created one object from pool: pool-addr=%p, obj-addr=%p", - pool, obj); -#endif - - return obj; -} - -/* - * Recycles an object, that is, puts it back into the pool. - * - * The pool becomes the sole owner of the object to recycle. - */ -static inline -void bt_ctf_object_pool_recycle_object(struct bt_ctf_object_pool *pool, void *obj) -{ - struct bt_ctf_object *bt_obj = obj; - - BT_ASSERT(pool); - BT_ASSERT(obj); - -#ifdef BT_LOGV - BT_LOGV("Recycling object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", - pool, pool->size, pool->objects->len, obj); -#endif - - if (pool->size == pool->objects->len) { - /* Backing array is full: make place for recycled object */ -#ifdef BT_LOGV - BT_LOGV("Object pool is full: increasing object pool capacity: " - "pool-addr=%p, old-pool-cap=%u, new-pool-cap=%u", - pool, pool->objects->len, pool->objects->len + 1); -#endif - g_ptr_array_set_size(pool->objects, pool->size + 1); - } - - /* Reset reference count to 1 since it could be 0 now */ - bt_obj->ref_count = 1; - - /* Back to the pool */ - pool->objects->pdata[pool->size] = obj; - pool->size++; - -#ifdef BT_LOGV - BT_LOGV("Recycled object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", - pool, pool->size, pool->objects->len, obj); -#endif -} - -#endif /* BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/resolve-internal.h b/include/babeltrace2/ctf-writer/resolve-internal.h deleted file mode 100644 index f779238d..00000000 --- a/include/babeltrace2/ctf-writer/resolve-internal.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H -#define BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H - -/* - * Copyright 2015 Jérémie Galarneau - * Copyright 2016 Philippe Proulx - * - * Authors: Jérémie Galarneau - * Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -enum bt_ctf_resolve_flag { - BT_CTF_RESOLVE_FLAG_PACKET_HEADER = 0x01, - BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT = 0x02, - BT_CTF_RESOLVE_FLAG_EVENT_HEADER = 0x04, - BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX = 0x08, - BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT = 0x10, - BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD = 0x20, -}; - -/* - * Resolves CTF IR field types: recursively locates the tag and length - * field types of resp. variant and sequence field types. - * - * All `*_type` parameters may be resolved, and may as well serve as - * resolving targets. - * - * Resolving is performed based on the flags in `flags`. - * - * It is expected that, amongst all the provided types, no common - * references to sequence variant field types exist. In other words, - * this function does not copy field types. - * - * All parameters are owned by the caller. - */ -BT_HIDDEN -int bt_ctf_resolve_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type, - struct bt_ctf_field_type_common *event_context_type, - struct bt_ctf_field_type_common *event_payload_type, - enum bt_ctf_resolve_flag flags); - -#endif /* BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/stream-class-internal.h b/include/babeltrace2/ctf-writer/stream-class-internal.h deleted file mode 100644 index 0b7fd47d..00000000 --- a/include/babeltrace2/ctf-writer/stream-class-internal.h +++ /dev/null @@ -1,538 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H -#define BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H - -/* - * Copyright 2014 EfficiOS Inc. - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_stream_class_common { - struct bt_ctf_object base; - GString *name; - - /* Array of pointers to event class addresses */ - GPtrArray *event_classes; - - /* event class id (int64_t) to event class address */ - GHashTable *event_classes_ht; - int id_set; - int64_t id; - int64_t next_event_id; - struct bt_ctf_field_type_common *packet_context_field_type; - struct bt_ctf_field_type_common *event_header_field_type; - struct bt_ctf_field_type_common *event_context_field_type; - int frozen; - int byte_order; - - /* - * This flag indicates if the stream class is valid. A valid - * stream class is _always_ frozen. - */ - int valid; - - /* - * Unique clock class mapped to any field type within this - * stream class, including all the stream class's event class - * field types. This is only set if the stream class is frozen. - * - * If the stream class is frozen and this is still NULL, it is - * still possible that it becomes non-NULL because - * bt_ctf_stream_class_add_event_class() can add an event class - * containing a field type mapped to some clock class. In this - * case, this is the mapped clock class, and at this point, both - * the new event class and the stream class are frozen, so the - * next added event classes are expected to contain field types - * which only map to this specific clock class. - * - * If this is a CTF writer stream class, then this is the - * backing clock class of the `clock` member above. - */ - struct bt_ctf_clock_class *clock_class; -}; - -struct bt_ctf_event_class_common; - -BT_HIDDEN -int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class, - const char *name, bt_ctf_object_release_func release_func); - -BT_HIDDEN -void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class); - -BT_HIDDEN -void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class); - -static inline -const char *bt_ctf_stream_class_common_get_name( - struct bt_ctf_stream_class_common *stream_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->name->len > 0 ? stream_class->name->str : NULL; -} - -static inline -int64_t bt_ctf_stream_class_common_get_id( - struct bt_ctf_stream_class_common *stream_class) -{ - int64_t ret; - - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - - if (!stream_class->id_set) { - BT_LOGV("Stream class's ID is not set: addr=%p, name=\"%s\"", - stream_class, - bt_ctf_stream_class_common_get_name(stream_class)); - ret = (int64_t) -1; - goto end; - } - - ret = stream_class->id; - -end: - return ret; -} - -BT_HIDDEN -void bt_ctf_stream_class_common_set_byte_order( - struct bt_ctf_stream_class_common *stream_class, int byte_order); - -BT_HIDDEN -int bt_ctf_stream_class_common_validate_single_clock_class( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_clock_class **expected_clock_class); - -BT_HIDDEN -int bt_ctf_stream_class_common_add_event_class( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_event_class_common *event_class, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func); - -BT_HIDDEN -int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class, - bt_ctf_visitor visitor, void *data); - -BT_HIDDEN -int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class, - bt_ctf_visitor visitor, void *data); - -static inline -struct bt_ctf_trace_common *bt_ctf_stream_class_common_borrow_trace( - struct bt_ctf_stream_class_common *stream_class) -{ - BT_ASSERT(stream_class); - return (void *) bt_ctf_object_borrow_parent(&stream_class->base); -} - -static inline -int bt_ctf_stream_class_common_set_name(struct bt_ctf_stream_class_common *stream_class, - const char *name) -{ - int ret = 0; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - ret = -1; - goto end; - } - - if (!name) { - g_string_assign(stream_class->name, ""); - } else { - if (strlen(name) == 0) { - BT_LOGW("Invalid parameter: name is empty."); - ret = -1; - goto end; - } - - g_string_assign(stream_class->name, name); - } - - BT_LOGV("Set stream class's name: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); -end: - return ret; -} - -static inline -void _bt_ctf_stream_class_common_set_id( - struct bt_ctf_stream_class_common *stream_class, int64_t id) -{ - BT_ASSERT(stream_class); - stream_class->id = id; - stream_class->id_set = 1; - BT_LOGV("Set stream class's ID (internal): " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); -} - -static inline -int bt_ctf_stream_class_common_set_id_no_check( - struct bt_ctf_stream_class_common *stream_class, int64_t id) -{ - _bt_ctf_stream_class_common_set_id(stream_class, id); - return 0; -} - -static inline -int bt_ctf_stream_class_common_set_id(struct bt_ctf_stream_class_common *stream_class, - uint64_t id_param) -{ - int ret = 0; - int64_t id = (int64_t) id_param; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - ret = -1; - goto end; - } - - if (id < 0) { - BT_LOGW("Invalid parameter: invalid stream class's ID: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRId64 ", id=%" PRIu64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - id_param); - ret = -1; - goto end; - } - - ret = bt_ctf_stream_class_common_set_id_no_check(stream_class, id); - if (ret == 0) { - BT_LOGV("Set stream class's ID: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - } -end: - return ret; -} - -static inline -int64_t bt_ctf_stream_class_common_get_event_class_count( - struct bt_ctf_stream_class_common *stream_class) -{ - int64_t ret; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = (int64_t) -1; - goto end; - } - - ret = (int64_t) stream_class->event_classes->len; -end: - return ret; -} - -static inline -struct bt_ctf_event_class_common *bt_ctf_stream_class_common_borrow_event_class_by_index( - struct bt_ctf_stream_class_common *stream_class, uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_CTF_ASSERT_PRE(index < stream_class->event_classes->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u", - index, stream_class->event_classes->len); - return g_ptr_array_index(stream_class->event_classes, index); -} - -static inline -struct bt_ctf_event_class_common *bt_ctf_stream_class_common_borrow_event_class_by_id( - struct bt_ctf_stream_class_common *stream_class, uint64_t id) -{ - int64_t id_key = (int64_t) id; - - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_CTF_ASSERT_PRE(id_key >= 0, - "Invalid event class ID: %" PRIu64, id); - return g_hash_table_lookup(stream_class->event_classes_ht, - &id_key); -} - -static inline -struct bt_ctf_field_type_common * -bt_ctf_stream_class_common_borrow_packet_context_field_type( - struct bt_ctf_stream_class_common *stream_class) -{ - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->packet_context_field_type; -} - -static inline -int bt_ctf_stream_class_common_set_packet_context_field_type( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_type) -{ - int ret = 0; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - ret = -1; - goto end; - } - - if (packet_context_type && - bt_ctf_field_type_common_get_type_id(packet_context_type) != - BT_CTF_FIELD_TYPE_ID_STRUCT) { - /* A packet context must be a structure. */ - BT_LOGW("Invalid parameter: stream class's packet context field type must be a structure: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "packet-context-ft-addr=%p, packet-context-ft-id=%s", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - packet_context_type, - bt_ctf_field_type_id_string( - bt_ctf_field_type_common_get_type_id(packet_context_type))); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(stream_class->packet_context_field_type); - stream_class->packet_context_field_type = packet_context_type; - bt_ctf_object_get_ref(stream_class->packet_context_field_type); - BT_LOGV("Set stream class's packet context field type: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "packet-context-ft-addr=%p", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - packet_context_type); - -end: - return ret; -} - -static inline -struct bt_ctf_field_type_common * -bt_ctf_stream_class_common_borrow_event_header_field_type( - struct bt_ctf_stream_class_common *stream_class) -{ - struct bt_ctf_field_type_common *ret = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - - if (!stream_class->event_header_field_type) { - BT_LOGV("Stream class has no event header field type: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - goto end; - } - - ret = stream_class->event_header_field_type; - -end: - return ret; -} - -static inline -int bt_ctf_stream_class_common_set_event_header_field_type( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *event_header_type) -{ - int ret = 0; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, - bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - ret = -1; - goto end; - } - - if (event_header_type && - bt_ctf_field_type_common_get_type_id(event_header_type) != - BT_CTF_FIELD_TYPE_ID_STRUCT) { - /* An event header must be a structure. */ - BT_LOGW("Invalid parameter: stream class's event header field type must be a structure: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "event-header-ft-addr=%p, event-header-ft-id=%s", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_header_type, - bt_ctf_field_type_id_string( - bt_ctf_field_type_common_get_type_id(event_header_type))); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(stream_class->event_header_field_type); - stream_class->event_header_field_type = event_header_type; - bt_ctf_object_get_ref(stream_class->event_header_field_type); - BT_LOGV("Set stream class's event header field type: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "event-header-ft-addr=%p", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_header_type); -end: - return ret; -} - -static inline -struct bt_ctf_field_type_common * -bt_ctf_stream_class_common_borrow_event_context_field_type( - struct bt_ctf_stream_class_common *stream_class) -{ - struct bt_ctf_field_type_common *ret = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - - if (!stream_class->event_context_field_type) { - goto end; - } - - ret = stream_class->event_context_field_type; - -end: - return ret; -} - -static inline -int bt_ctf_stream_class_common_set_event_context_field_type( - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *event_context_type) -{ - int ret = 0; - - if (!stream_class) { - BT_LOGW_STR("Invalid parameter: stream class is NULL."); - ret = -1; - goto end; - } - - if (stream_class->frozen) { - BT_LOGW("Invalid parameter: stream class is frozen: " - "addr=%p, name=\"%s\", id=%" PRId64, - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class)); - ret = -1; - goto end; - } - - if (event_context_type && - bt_ctf_field_type_common_get_type_id(event_context_type) != - BT_CTF_FIELD_TYPE_ID_STRUCT) { - /* A packet context must be a structure. */ - BT_LOGW("Invalid parameter: stream class's event context field type must be a structure: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "event-context-ft-addr=%p, event-context-ft-id=%s", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_context_type, - bt_ctf_field_type_id_string( - bt_ctf_field_type_common_get_type_id(event_context_type))); - ret = -1; - goto end; - } - - bt_ctf_object_put_ref(stream_class->event_context_field_type); - stream_class->event_context_field_type = event_context_type; - bt_ctf_object_get_ref(stream_class->event_context_field_type); - BT_LOGV("Set stream class's event context field type: " - "addr=%p, name=\"%s\", id=%" PRId64 ", " - "event-context-ft-addr=%p", - stream_class, bt_ctf_stream_class_common_get_name(stream_class), - bt_ctf_stream_class_common_get_id(stream_class), - event_context_type); -end: - return ret; -} - -struct bt_ctf_stream_class { - struct bt_ctf_stream_class_common common; - struct bt_ctf_clock *clock; - int64_t next_stream_id; -}; - -struct metadata_context; - -BT_HIDDEN -int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class, - struct metadata_context *context); - -BT_HIDDEN -int bt_ctf_stream_class_map_clock_class( - struct bt_ctf_stream_class *stream_class, - struct bt_ctf_field_type *packet_context_type, - struct bt_ctf_field_type *event_header_type); - -#endif /* BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/stream-internal.h b/include/babeltrace2/ctf-writer/stream-internal.h deleted file mode 100644 index c7eb9fae..00000000 --- a/include/babeltrace2/ctf-writer/stream-internal.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H -#define BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_stream_common; - -struct bt_ctf_stream_common { - struct bt_ctf_object base; - int64_t id; - struct bt_ctf_stream_class_common *stream_class; - GString *name; -}; - -BT_HIDDEN -int bt_ctf_stream_common_initialize( - struct bt_ctf_stream_common *stream, - struct bt_ctf_stream_class_common *stream_class, const char *name, - uint64_t id, bt_ctf_object_release_func release_func); - -BT_HIDDEN -void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream); - -static inline -struct bt_ctf_stream_class_common *bt_ctf_stream_common_borrow_class( - struct bt_ctf_stream_common *stream) -{ - BT_ASSERT(stream); - return stream->stream_class; -} - -static inline -const char *bt_ctf_stream_common_get_name(struct bt_ctf_stream_common *stream) -{ - BT_CTF_ASSERT_PRE_NON_NULL(stream, "Stream"); - return stream->name ? stream->name->str : NULL; -} - -static inline -int64_t bt_ctf_stream_common_get_id(struct bt_ctf_stream_common *stream) -{ - int64_t ret; - - BT_CTF_ASSERT_PRE_NON_NULL(stream, "Stream"); - ret = stream->id; - if (ret < 0) { - BT_LOGV("Stream's ID is not set: addr=%p, name=\"%s\"", - stream, bt_ctf_stream_common_get_name(stream)); - } - - return ret; -} - -struct bt_ctf_stream { - struct bt_ctf_stream_common common; - struct bt_ctf_field *packet_header; - struct bt_ctf_field *packet_context; - - /* Array of pointers to bt_ctf_event for the current packet */ - GPtrArray *events; - struct bt_ctfser ctfser; - unsigned int flushed_packet_count; - uint64_t discarded_events; - uint64_t last_ts_end; -}; - -BT_HIDDEN -struct bt_ctf_stream *bt_ctf_stream_create_with_id( - struct bt_ctf_stream_class *stream_class, - const char *name, uint64_t id); - -#endif /* BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/trace-internal.h b/include/babeltrace2/ctf-writer/trace-internal.h deleted file mode 100644 index 4d3e4d1a..00000000 --- a/include/babeltrace2/ctf-writer/trace-internal.h +++ /dev/null @@ -1,390 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H -#define BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H - -/* - * Copyright 2014 EfficiOS Inc. - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctf_trace_common { - struct bt_ctf_object base; - GString *name; - int frozen; - unsigned char uuid[BABELTRACE_UUID_LEN]; - bt_bool uuid_set; - enum bt_ctf_byte_order native_byte_order; - struct bt_ctf_private_value *environment; - GPtrArray *clock_classes; /* Array of pointers to bt_ctf_clock_class */ - GPtrArray *stream_classes; /* Array of ptrs to bt_ctf_stream_class_common */ - GPtrArray *streams; /* Array of ptrs to bt_ctf_stream_common */ - struct bt_ctf_field_type_common *packet_header_field_type; - int64_t next_stream_id; - - /* - * This flag indicates if the trace is valid. A valid - * trace is _always_ frozen. - */ - int valid; -}; - -BT_HIDDEN -bt_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace, - bt_ctf_object_release_func release_func); - -BT_HIDDEN -void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace); - -static inline -const char *bt_ctf_trace_common_get_name(struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->name ? trace->name->str : NULL; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name); - -static inline -const unsigned char *bt_ctf_trace_common_get_uuid(struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->uuid_set ? trace->uuid : NULL; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace, const unsigned char *uuid); - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace, - const char *name, struct bt_ctf_private_value *value); - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace, - const char *name, const char *value); - -BT_HIDDEN -int bt_ctf_trace_common_set_environment_field_integer(struct bt_ctf_trace_common *trace, - const char *name, int64_t value); - -static inline -int64_t bt_ctf_trace_common_get_environment_field_count( - struct bt_ctf_trace_common *trace) -{ - int64_t ret; - - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - ret = bt_ctf_attributes_get_count(trace->environment); - BT_ASSERT(ret >= 0); - return ret; -} - -static inline -const char * -bt_ctf_trace_common_get_environment_field_name_by_index( - struct bt_ctf_trace_common *trace, uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return bt_ctf_attributes_get_field_name(trace->environment, index); -} - -static inline -struct bt_ctf_private_value * -bt_ctf_trace_common_borrow_environment_field_value_by_index( - struct bt_ctf_trace_common *trace, uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return bt_ctf_attributes_borrow_field_value(trace->environment, index); -} - -static inline -struct bt_ctf_private_value * -bt_ctf_trace_common_borrow_environment_field_value_by_name( - struct bt_ctf_trace_common *trace, const char *name) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - return bt_ctf_attributes_borrow_field_value_by_name(trace->environment, - name); -} - -BT_HIDDEN -int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_clock_class *clock_class); - -static inline -int64_t bt_ctf_trace_common_get_clock_class_count(struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->clock_classes->len; -} - -static inline -struct bt_ctf_clock_class *bt_ctf_trace_common_borrow_clock_class_by_index( - struct bt_ctf_trace_common *trace, uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE(index < trace->clock_classes->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u", - index, trace->clock_classes->len); - return g_ptr_array_index(trace->clock_classes, index); -} - -static inline -int64_t bt_ctf_trace_common_get_stream_count(struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return (int64_t) trace->streams->len; -} - -static inline -struct bt_ctf_stream_common *bt_ctf_trace_common_borrow_stream_by_index( - struct bt_ctf_trace_common *trace, - uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE(index < trace->streams->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u", - index, trace->streams->len); - return g_ptr_array_index(trace->streams, index); -} - -static inline -int64_t bt_ctf_trace_common_get_stream_class_count(struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return (int64_t) trace->stream_classes->len; -} - -static inline -struct bt_ctf_stream_class_common *bt_ctf_trace_common_borrow_stream_class_by_index( - struct bt_ctf_trace_common *trace, uint64_t index) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE(index < trace->stream_classes->len, - "Index is out of bounds: index=%" PRIu64 ", " - "count=%u", - index, trace->stream_classes->len); - return g_ptr_array_index(trace->stream_classes, index); -} - -static inline -struct bt_ctf_stream_class_common *bt_ctf_trace_common_borrow_stream_class_by_id( - struct bt_ctf_trace_common *trace, uint64_t id_param) -{ - int i; - struct bt_ctf_stream_class_common *stream_class = NULL; - int64_t id = (int64_t) id_param; - - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE(id >= 0, - "Invalid stream class ID: %" PRIu64, id_param); - - for (i = 0; i < trace->stream_classes->len; i++) { - struct bt_ctf_stream_class_common *stream_class_candidate; - - stream_class_candidate = - g_ptr_array_index(trace->stream_classes, i); - - if (bt_ctf_stream_class_common_get_id(stream_class_candidate) == - (int64_t) id) { - stream_class = stream_class_candidate; - goto end; - } - } - -end: - return stream_class; -} - -static inline -struct bt_ctf_clock_class *bt_ctf_trace_common_borrow_clock_class_by_name( - struct bt_ctf_trace_common *trace, const char *name) -{ - size_t i; - struct bt_ctf_clock_class *clock_class = NULL; - - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); - - for (i = 0; i < trace->clock_classes->len; i++) { - struct bt_ctf_clock_class *cur_clk = - g_ptr_array_index(trace->clock_classes, i); - const char *cur_clk_name = bt_ctf_clock_class_get_name(cur_clk); - - if (!cur_clk_name) { - goto end; - } - - if (!strcmp(cur_clk_name, name)) { - clock_class = cur_clk; - goto end; - } - } - -end: - return clock_class; -} - -static inline -enum bt_ctf_byte_order bt_ctf_trace_common_get_native_byte_order( - struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->native_byte_order; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace, - enum bt_ctf_byte_order byte_order, bool allow_unspecified); - -static inline -struct bt_ctf_field_type_common *bt_ctf_trace_common_borrow_packet_header_field_type( - struct bt_ctf_trace_common *trace) -{ - BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->packet_header_field_type; -} - -BT_HIDDEN -int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace, - struct bt_ctf_field_type_common *packet_header_field_type); - -static inline -void bt_ctf_trace_common_freeze(struct bt_ctf_trace_common *trace) -{ - int i; - - if (trace->frozen) { - return; - } - - BT_LOGD("Freezing trace: addr=%p, name=\"%s\"", - trace, bt_ctf_trace_common_get_name(trace)); - BT_LOGD_STR("Freezing packet header field type."); - bt_ctf_field_type_common_freeze(trace->packet_header_field_type); - BT_LOGD_STR("Freezing environment attributes."); - bt_ctf_attributes_freeze(trace->environment); - - if (trace->clock_classes->len > 0) { - BT_LOGD_STR("Freezing clock classes."); - } - - for (i = 0; i < trace->clock_classes->len; i++) { - struct bt_ctf_clock_class *clock_class = - g_ptr_array_index(trace->clock_classes, i); - - bt_ctf_clock_class_freeze(clock_class); - } - - trace->frozen = 1; -} - -BT_HIDDEN -int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func, - struct bt_ctf_clock_class *init_expected_clock_class, - int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_field_type_common *packet_context_field_type, - struct bt_ctf_field_type_common *event_header_field_type), - bool check_ts_begin_end_mapped); - -struct bt_ctf_trace { - struct bt_ctf_trace_common common; -}; - -/* - * bt_ctf_trace_get_metadata_string: get metadata string. - * - * Get the trace's TSDL metadata. The caller assumes the ownership of the - * returned string. - * - * @param trace Trace instance. - * - * Returns the metadata string on success, NULL on error. - */ -BT_HIDDEN -char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace); - -BT_HIDDEN -struct bt_ctf_trace *bt_ctf_trace_create(void); - -BT_HIDDEN -int64_t bt_ctf_trace_get_clock_class_count( - struct bt_ctf_trace *trace); - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index( - struct bt_ctf_trace *trace, uint64_t index); - -BT_HIDDEN -struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name( - struct bt_ctf_trace *trace, const char *name); - -BT_HIDDEN -int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace, - struct bt_ctf_clock_class *clock_class); - -BT_HIDDEN -int64_t bt_ctf_trace_get_environment_field_count( - struct bt_ctf_trace *trace); - -BT_HIDDEN -const char *bt_ctf_trace_get_environment_field_name_by_index( - struct bt_ctf_trace *trace, uint64_t index); - -BT_HIDDEN -struct bt_ctf_value * -bt_ctf_trace_get_environment_field_value_by_index(struct bt_ctf_trace *trace, - uint64_t index); - -BT_HIDDEN -struct bt_ctf_value * -bt_ctf_trace_get_environment_field_value_by_name( - struct bt_ctf_trace *trace, const char *name); - -#endif /* BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/utils-internal.h b/include/babeltrace2/ctf-writer/utils-internal.h deleted file mode 100644 index d253824c..00000000 --- a/include/babeltrace2/ctf-writer/utils-internal.h +++ /dev/null @@ -1,223 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H -#define BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H - -/* - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#define BT_CTF_TO_COMMON(_obj) (&(_obj)->common) -#define BT_CTF_FROM_COMMON(_obj) ((void *) _obj) - -struct bt_ctf_search_query { - gpointer value; - int found; -}; - -BT_HIDDEN -const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order); - -static inline -const char *bt_ctf_field_type_id_string(enum bt_ctf_field_type_id type_id) -{ - switch (type_id) { - case BT_CTF_FIELD_TYPE_ID_UNKNOWN: - return "BT_CTF_FIELD_TYPE_ID_UNKNOWN"; - case BT_CTF_FIELD_TYPE_ID_INTEGER: - return "BT_CTF_FIELD_TYPE_ID_INTEGER"; - case BT_CTF_FIELD_TYPE_ID_FLOAT: - return "BT_CTF_FIELD_TYPE_ID_FLOAT"; - case BT_CTF_FIELD_TYPE_ID_ENUM: - return "BT_CTF_FIELD_TYPE_ID_ENUM"; - case BT_CTF_FIELD_TYPE_ID_STRING: - return "BT_CTF_FIELD_TYPE_ID_STRING"; - case BT_CTF_FIELD_TYPE_ID_STRUCT: - return "BT_CTF_FIELD_TYPE_ID_STRUCT"; - case BT_CTF_FIELD_TYPE_ID_ARRAY: - return "BT_CTF_FIELD_TYPE_ID_ARRAY"; - case BT_CTF_FIELD_TYPE_ID_SEQUENCE: - return "BT_CTF_FIELD_TYPE_ID_SEQUENCE"; - case BT_CTF_FIELD_TYPE_ID_VARIANT: - return "BT_CTF_FIELD_TYPE_ID_VARIANT"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_ctf_byte_order_string(enum bt_ctf_byte_order bo) -{ - switch (bo) { - case BT_CTF_BYTE_ORDER_UNKNOWN: - return "BT_CTF_BYTE_ORDER_UNKNOWN"; - case BT_CTF_BYTE_ORDER_UNSPECIFIED: - return "BT_CTF_BYTE_ORDER_UNSPECIFIED"; - case BT_CTF_BYTE_ORDER_NATIVE: - return "BT_CTF_BYTE_ORDER_NATIVE"; - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - return "BT_CTF_BYTE_ORDER_LITTLE_ENDIAN"; - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - return "BT_CTF_BYTE_ORDER_BIG_ENDIAN"; - case BT_CTF_BYTE_ORDER_NETWORK: - return "BT_CTF_BYTE_ORDER_NETWORK"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_ctf_string_encoding_string(enum bt_ctf_string_encoding encoding) -{ - switch (encoding) { - case BT_CTF_STRING_ENCODING_UNKNOWN: - return "BT_CTF_STRING_ENCODING_UNKNOWN"; - case BT_CTF_STRING_ENCODING_NONE: - return "BT_CTF_STRING_ENCODING_NONE"; - case BT_CTF_STRING_ENCODING_UTF8: - return "BT_CTF_STRING_ENCODING_UTF8"; - case BT_CTF_STRING_ENCODING_ASCII: - return "BT_CTF_STRING_ENCODING_ASCII"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_ctf_integer_base_string(enum bt_ctf_integer_base base) -{ - switch (base) { - case BT_CTF_INTEGER_BASE_UNKNOWN: - return "BT_CTF_INTEGER_BASE_UNKNOWN"; - case BT_CTF_INTEGER_BASE_UNSPECIFIED: - return "BT_CTF_INTEGER_BASE_UNSPECIFIED"; - case BT_CTF_INTEGER_BASE_BINARY: - return "BT_CTF_INTEGER_BASE_BINARY"; - case BT_CTF_INTEGER_BASE_OCTAL: - return "BT_CTF_INTEGER_BASE_OCTAL"; - case BT_CTF_INTEGER_BASE_DECIMAL: - return "BT_CTF_INTEGER_BASE_DECIMAL"; - case BT_CTF_INTEGER_BASE_HEXADECIMAL: - return "BT_CTF_INTEGER_BASE_HEXADECIMAL"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_ctf_scope_string(enum bt_ctf_scope scope) -{ - switch (scope) { - case BT_CTF_SCOPE_UNKNOWN: - return "BT_CTF_SCOPE_UNKNOWN"; - case BT_CTF_SCOPE_TRACE_PACKET_HEADER: - return "BT_CTF_SCOPE_TRACE_PACKET_HEADER"; - case BT_CTF_SCOPE_STREAM_PACKET_CONTEXT: - return "BT_CTF_SCOPE_STREAM_PACKET_CONTEXT"; - case BT_CTF_SCOPE_STREAM_EVENT_HEADER: - return "BT_CTF_SCOPE_STREAM_EVENT_HEADER"; - case BT_CTF_SCOPE_STREAM_EVENT_CONTEXT: - return "BT_CTF_SCOPE_STREAM_EVENT_CONTEXT"; - case BT_CTF_SCOPE_EVENT_CONTEXT: - return "BT_CTF_SCOPE_EVENT_CONTEXT"; - case BT_CTF_SCOPE_EVENT_PAYLOAD: - return "BT_CTF_SCOPE_EVENT_PAYLOAD"; - case BT_CTF_SCOPE_ENV: - return "BT_CTF_SCOPE_ENV"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_ctf_event_class_log_level_string( - enum bt_ctf_event_class_log_level level) -{ - switch (level) { - case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE"; - case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG: - return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG"; - default: - return "(unknown)"; - } -}; - -static inline -GString *bt_ctf_field_path_string(struct bt_ctf_field_path *path) -{ - GString *str = g_string_new(NULL); - size_t i; - - BT_ASSERT(path); - - if (!str) { - goto end; - } - - g_string_append_printf(str, "[%s", bt_common_scope_string( - bt_ctf_field_path_get_root_scope(path))); - - for (i = 0; i < bt_ctf_field_path_get_index_count(path); i++) { - int index = bt_ctf_field_path_get_index(path, i); - - g_string_append_printf(str, ", %d", index); - } - - g_string_append(str, "]"); - -end: - return str; -} - -#endif /* BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/validation-internal.h b/include/babeltrace2/ctf-writer/validation-internal.h deleted file mode 100644 index 608078f0..00000000 --- a/include/babeltrace2/ctf-writer/validation-internal.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H -#define BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H - -/* - * Copyright 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -struct bt_ctf_trace_common; -struct bt_ctf_stream_class_common; -struct bt_ctf_event_class_common; -struct bt_ctf_field_type_common; - -typedef struct bt_ctf_field_type_common *(*bt_ctf_validation_flag_copy_field_type_func)( - struct bt_ctf_field_type_common *); - -enum bt_ctf_validation_flag { - BT_CTF_VALIDATION_FLAG_TRACE = 1, - BT_CTF_VALIDATION_FLAG_STREAM = 2, - BT_CTF_VALIDATION_FLAG_EVENT = 4, -}; - -/* - * Validation output structure. - * - * This is where the results of the validation function go. The field - * types are the validated ones which should replace the original field - * types of a trace, a stream class, and an event class. - * - * `valid_flags` contains the results of the validation. - */ -struct bt_ctf_validation_output { - struct bt_ctf_field_type_common *packet_header_type; - struct bt_ctf_field_type_common *packet_context_type; - struct bt_ctf_field_type_common *event_header_type; - struct bt_ctf_field_type_common *stream_event_ctx_type; - struct bt_ctf_field_type_common *event_context_type; - struct bt_ctf_field_type_common *event_payload_type; - enum bt_ctf_validation_flag valid_flags; -}; - -/* - * This function resolves and validates the field types of an event - * class, a stream class, and a trace. Copies are created if needed - * and the resulting field types to use are placed in the `output` - * validation structure, which also contains the results of the - * validation. Copies can replace the original field types of a trace, - * a stream class, and an event class using - * bt_ctf_validation_replace_types(). - * - * The current known validity of the field types of the trace, - * stream class, and event class must be indicated with the - * `trace_valid`, `stream_class_valid`, and `event_class_valid` - * parameters. If a class is valid, its field types are not copied, - * validated, or resolved during this call. - * - * The validation flags `validate_flags` indicate which classes should - * have their field types validated. - * - * All parameters are owned by the caller. - */ -BT_HIDDEN -int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment, - struct bt_ctf_field_type_common *packet_header_type, - struct bt_ctf_field_type_common *packet_context_type, - struct bt_ctf_field_type_common *event_header_type, - struct bt_ctf_field_type_common *stream_event_ctx_type, - struct bt_ctf_field_type_common *event_context_type, - struct bt_ctf_field_type_common *event_payload_type, - int trace_valid, int stream_class_valid, int event_class_valid, - struct bt_ctf_validation_output *output, - enum bt_ctf_validation_flag validate_flags, - bt_ctf_validation_flag_copy_field_type_func copy_field_type_func); - -/* - * This function replaces the actual field types of a trace, a stream - * class, and an event class with the appropriate field types contained - * in a validation output structure. - * - * The replace flags `replace_flags` indicate which classes should have - * their field types replaced. - * - * Note that the field types that are not used in the validation output - * structure are still owned by it at the end of this call. - * bt_ctf_validation_output_put_types() should be called to clean the - * structure. - * - * All parameters are owned by the caller. - */ -BT_HIDDEN -void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace, - struct bt_ctf_stream_class_common *stream_class, - struct bt_ctf_event_class_common *event_class, - struct bt_ctf_validation_output *output, - enum bt_ctf_validation_flag replace_flags); - -/* - * This function puts all the field types contained in a given - * validation output structure. - * - * `output` is owned by the caller and is not freed here. - */ -BT_HIDDEN -void bt_ctf_validation_output_put_types( - struct bt_ctf_validation_output *output); - -#endif /* BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/values-internal.h b/include/babeltrace2/ctf-writer/values-internal.h deleted file mode 100644 index dff46384..00000000 --- a/include/babeltrace2/ctf-writer/values-internal.h +++ /dev/null @@ -1,357 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H -#define BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H - -/* - * Copyright (c) 2015-2017 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -struct bt_ctf_value; -struct bt_ctf_private_value; - -/** -@brief Status codes. -*/ -enum bt_ctf_value_status { - /// Operation canceled. - BT_CTF_VALUE_STATUS_CANCELED = 125, - - /// Cannot allocate memory. - BT_CTF_VALUE_STATUS_NOMEM = -12, - - /// Okay, no error. - BT_CTF_VALUE_STATUS_OK = 0, -}; - -BT_HIDDEN -enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object); - -#ifdef BT_DEV_MODE -# define bt_ctf_value_freeze _bt_ctf_value_freeze -#else -# define bt_ctf_value_freeze(_value) -#endif /* BT_DEV_MODE */ - -extern struct bt_ctf_value *const bt_ctf_value_null; - -enum bt_ctf_value_type { - /// Null value object. - BT_CTF_VALUE_TYPE_NULL = 0, - - /// Boolean value object (holds #BT_TRUE or #BT_FALSE). - BT_CTF_VALUE_TYPE_BOOL = 1, - - /// Integer value object (holds a signed 64-bit integer raw value). - BT_CTF_VALUE_TYPE_INTEGER = 2, - - /// Floating point number value object (holds a \c double raw value). - BT_CTF_VALUE_TYPE_REAL = 3, - - /// String value object. - BT_CTF_VALUE_TYPE_STRING = 4, - - /// Array value object. - BT_CTF_VALUE_TYPE_ARRAY = 5, - - /// Map value object. - BT_CTF_VALUE_TYPE_MAP = 6, -}; - -BT_HIDDEN -enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object); - -static inline -bt_bool bt_ctf_value_is_null(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_NULL; -} - -static inline -bt_bool bt_ctf_value_is_bool(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_BOOL; -} - -static inline -bt_bool bt_ctf_value_is_integer(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_INTEGER; -} - -static inline -bt_bool bt_ctf_value_is_real(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_REAL; -} - -static inline -bt_bool bt_ctf_value_is_string(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_STRING; -} - -static inline -bt_bool bt_ctf_value_is_array(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_ARRAY; -} - -static inline -bt_bool bt_ctf_value_is_map(const struct bt_ctf_value *object) -{ - return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_MAP; -} - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy, - const struct bt_ctf_value *object); - -BT_HIDDEN -bt_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a, - const struct bt_ctf_value *object_b); - -BT_HIDDEN -bt_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj); - -BT_HIDDEN -int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj); - -BT_HIDDEN -double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj); - -BT_HIDDEN -const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj); - -BT_HIDDEN -uint64_t bt_ctf_value_array_get_size(const struct bt_ctf_value *array_obj); - -static inline -bt_bool bt_ctf_value_array_is_empty(const struct bt_ctf_value *array_obj) -{ - return bt_ctf_value_array_get_size(array_obj) == 0; -} - -BT_HIDDEN -struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index( - const struct bt_ctf_value *array_obj, uint64_t index); - -BT_HIDDEN -uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj); - -static inline -bt_bool bt_ctf_value_map_is_empty(const struct bt_ctf_value *map_obj) -{ - return bt_ctf_value_map_get_size(map_obj) == 0; -} - -BT_HIDDEN -struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value( - const struct bt_ctf_value *map_obj, const char *key); - -typedef bt_bool (* bt_ctf_value_map_foreach_entry_cb)(const char *key, - struct bt_ctf_value *object, void *data); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_map_foreach_entry( - const struct bt_ctf_value *map_obj, - bt_ctf_value_map_foreach_entry_cb cb, void *data); - -BT_HIDDEN -bt_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, - const char *key); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_value_map_extend( - struct bt_ctf_private_value **extended_map_obj, - const struct bt_ctf_value *base_map_obj, - const struct bt_ctf_value *extension_map_obj); - - -struct bt_ctf_value; -struct bt_ctf_private_value; - -extern struct bt_ctf_private_value *const bt_ctf_private_value_null; - -static inline -struct bt_ctf_value *bt_ctf_private_value_as_value( - struct bt_ctf_private_value *priv_value) -{ - return (void *) priv_value; -} - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_bool val); - -BT_HIDDEN -void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, - bt_bool val); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init( - int64_t val); - -BT_HIDDEN -void bt_ctf_private_value_integer_set( - struct bt_ctf_private_value *integer_obj, int64_t val); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_real_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val); - -BT_HIDDEN -void bt_ctf_private_value_real_set( - struct bt_ctf_private_value *real_obj, double val); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_string_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_string_create_init( - const char *val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_string_set( - struct bt_ctf_private_value *string_obj, - const char *val); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_array_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index( - const struct bt_ctf_private_value *array_obj, uint64_t index); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_element( - struct bt_ctf_private_value *array_obj, - struct bt_ctf_value *element_obj); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element( - struct bt_ctf_private_value *array_obj, - bt_bool val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element( - struct bt_ctf_private_value *array_obj, - int64_t val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element( - struct bt_ctf_private_value *array_obj, - double val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element( - struct bt_ctf_private_value *array_obj, const char *val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element( - struct bt_ctf_private_value *array_obj); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element( - struct bt_ctf_private_value *array_obj); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index( - struct bt_ctf_private_value *array_obj, uint64_t index, - struct bt_ctf_value *element_obj); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_map_create(void); - -BT_HIDDEN -struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value( - const struct bt_ctf_private_value *map_obj, const char *key); - -typedef bt_bool (* bt_ctf_private_value_map_foreach_entry_cb)(const char *key, - struct bt_ctf_private_value *object, void *data); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry( - const struct bt_ctf_private_value *map_obj, - bt_ctf_private_value_map_foreach_entry_cb cb, void *data); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry( - struct bt_ctf_private_value *map_obj, const char *key, - struct bt_ctf_value *element_obj); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry( - struct bt_ctf_private_value *map_obj, const char *key, bt_bool val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry( - struct bt_ctf_private_value *map_obj, const char *key, int64_t val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry( - struct bt_ctf_private_value *map_obj, const char *key, double val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry( - struct bt_ctf_private_value *map_obj, const char *key, - const char *val); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry( - struct bt_ctf_private_value *map_obj, const char *key); - -BT_HIDDEN -enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry( - struct bt_ctf_private_value *map_obj, const char *key); - -static inline -const char *bt_ctf_value_type_string(enum bt_ctf_value_type type) -{ - switch (type) { - case BT_CTF_VALUE_TYPE_NULL: - return "BT_CTF_VALUE_TYPE_NULL"; - case BT_CTF_VALUE_TYPE_BOOL: - return "BT_CTF_VALUE_TYPE_BOOL"; - case BT_CTF_VALUE_TYPE_INTEGER: - return "BT_CTF_VALUE_TYPE_INTEGER"; - case BT_CTF_VALUE_TYPE_REAL: - return "BT_CTF_VALUE_TYPE_REAL"; - case BT_CTF_VALUE_TYPE_STRING: - return "BT_CTF_VALUE_TYPE_STRING"; - case BT_CTF_VALUE_TYPE_ARRAY: - return "BT_CTF_VALUE_TYPE_ARRAY"; - case BT_CTF_VALUE_TYPE_MAP: - return "BT_CTF_VALUE_TYPE_MAP"; - default: - return "(unknown)"; - } -}; - -#endif /* BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/visitor-internal.h b/include/babeltrace2/ctf-writer/visitor-internal.h deleted file mode 100644 index 810628d1..00000000 --- a/include/babeltrace2/ctf-writer/visitor-internal.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H -#define BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H - -/* - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -typedef void *(*bt_ctf_child_accessor)(void *object, int index); -typedef int64_t (*bt_ctf_child_count_accessor)(void *object); -typedef int (*bt_ctf_child_visitor)(void *object, bt_ctf_visitor visitor, - void *data); - -struct bt_ctf_visitor_object { - enum bt_ctf_visitor_object_type type; - void *object; -}; - -BT_HIDDEN -int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root, - bt_ctf_child_count_accessor child_counter, - bt_ctf_child_accessor child_accessor, - bt_ctf_child_visitor child_visitor, - bt_ctf_visitor visitor, - void *data); - -#endif /* BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H */ diff --git a/include/babeltrace2/ctf-writer/writer-internal.h b/include/babeltrace2/ctf-writer/writer-internal.h deleted file mode 100644 index 7969513b..00000000 --- a/include/babeltrace2/ctf-writer/writer-internal.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H -#define BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H - -/* - * Copyright 2013, 2014 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -struct metadata_context { - GString *string; - GString *field_name; - unsigned int current_indentation_level; -}; - -struct bt_ctf_writer { - struct bt_ctf_object base; - int frozen; /* Protects attributes that can't be changed mid-trace */ - struct bt_ctf_trace *trace; - GString *path; - int metadata_fd; -}; - -enum field_type_alias { - FIELD_TYPE_ALIAS_UINT5_T = 0, - FIELD_TYPE_ALIAS_UINT8_T, - FIELD_TYPE_ALIAS_UINT16_T, - FIELD_TYPE_ALIAS_UINT27_T, - FIELD_TYPE_ALIAS_UINT32_T, - FIELD_TYPE_ALIAS_UINT64_T, - NR_FIELD_TYPE_ALIAS, -}; - -BT_HIDDEN -struct bt_ctf_field_type *get_field_type(enum field_type_alias alias); - -BT_HIDDEN -const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order); - -BT_HIDDEN -void bt_ctf_writer_freeze(struct bt_ctf_writer *writer); - -#endif /* BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H */ diff --git a/include/babeltrace2/ctfser-internal.h b/include/babeltrace2/ctfser-internal.h deleted file mode 100644 index 983b86f0..00000000 --- a/include/babeltrace2/ctfser-internal.h +++ /dev/null @@ -1,576 +0,0 @@ -#ifndef BABELTRACE_CTFSER_INTERNAL_H -#define BABELTRACE_CTFSER_INTERNAL_H - -/* - * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation - * Copyright 2013, 2014 Jérémie Galarneau - * Copyright 2017-2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_ctfser { - /* Stream file's descriptor */ - int fd; - - /* Offset (bytes) of memory map (current packet) in the stream file */ - off_t mmap_offset; - - /* Offset (bytes) of packet's first byte in the memory map */ - off_t mmap_base_offset; - - /* Current offset (bits) within current packet */ - uint64_t offset_in_cur_packet_bits; - - /* Current packet size (bytes) */ - uint64_t cur_packet_size_bytes; - - /* Previous packet size (bytes) */ - uint64_t prev_packet_size_bytes; - - /* Current stream size (bytes) */ - uint64_t stream_size_bytes; - - /* Memory map base address */ - struct mmap_align *base_mma; - - /* Stream file's path (for debugging) */ - GString *path; -}; - -/* - * Initializes a CTF serializer. - * - * This function opens the file `path` for writing. - */ -BT_HIDDEN -int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path); - -/* - * Finalizes a CTF serializer. - * - * This function truncates the stream file so that there's no extra - * padding after the last packet, and then closes the file. - */ -BT_HIDDEN -int bt_ctfser_fini(struct bt_ctfser *ctfser); - -/* - * Opens a new packet. - * - * All the next writing functions are performed within this new packet. - */ -BT_HIDDEN -int bt_ctfser_open_packet(struct bt_ctfser *ctfser); - -/* - * Closes the current packet, making its size `packet_size_bytes`. - */ -BT_HIDDEN -void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser, - uint64_t packet_size_bytes); - -BT_HIDDEN -int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser); - -static inline -uint64_t _bt_ctfser_cur_packet_size_bits(struct bt_ctfser *ctfser) -{ - return ctfser->cur_packet_size_bytes * 8; -} - -static inline -uint64_t _bt_ctfser_prev_packet_size_bits(struct bt_ctfser *ctfser) -{ - return ctfser->prev_packet_size_bytes * 8; -} - -static inline -uint64_t _bt_ctfser_offset_bytes(struct bt_ctfser *ctfser) -{ - return ctfser->offset_in_cur_packet_bits / 8; -} - -static inline -uint8_t *_bt_ctfser_get_addr(struct bt_ctfser *ctfser) -{ - /* Only makes sense to get the address after aligning on byte */ - BT_ASSERT(ctfser->offset_in_cur_packet_bits % 8 == 0); - return ((uint8_t *) mmap_align_addr(ctfser->base_mma)) + - ctfser->mmap_base_offset + _bt_ctfser_offset_bytes(ctfser); -} - -static inline -bool _bt_ctfser_has_space_left(struct bt_ctfser *ctfser, uint64_t size_bits) -{ - bool has_space_left = true; - - if (unlikely((ctfser->offset_in_cur_packet_bits + size_bits > - _bt_ctfser_cur_packet_size_bits(ctfser)))) { - has_space_left = false; - goto end; - } - - if (unlikely(size_bits > UINT64_MAX - ctfser->offset_in_cur_packet_bits)) { - has_space_left = false; - goto end; - } - -end: - return has_space_left; -} - -static inline -void _bt_ctfser_incr_offset(struct bt_ctfser *ctfser, uint64_t size_bits) -{ - BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); - ctfser->offset_in_cur_packet_bits += size_bits; -} - -/* - * Aligns the current offset within the current packet to - * `alignment_bits` bits (power of two, > 0). - */ -static inline -int bt_ctfser_align_offset_in_current_packet(struct bt_ctfser *ctfser, - uint64_t alignment_bits) -{ - int ret = 0; - uint64_t align_size_bits; - - BT_ASSERT(alignment_bits > 0); - align_size_bits = ALIGN(ctfser->offset_in_cur_packet_bits, - alignment_bits) - ctfser->offset_in_cur_packet_bits; - - if (unlikely(!_bt_ctfser_has_space_left(ctfser, align_size_bits))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - _bt_ctfser_incr_offset(ctfser, align_size_bits); - -end: - return ret; -} - -static inline -int _bt_ctfser_write_byte_aligned_unsigned_int_no_align( - struct bt_ctfser *ctfser, uint64_t value, - unsigned int size_bits, int byte_order) -{ - int ret = 0; - - /* Reverse byte order? */ - bool rbo = byte_order != BYTE_ORDER; - - BT_ASSERT(size_bits % 8 == 0); - BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); - - switch (size_bits) { - case 8: - { - uint8_t v = (uint8_t) value; - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 16: - { - uint16_t v = (uint16_t) value; - - if (rbo) { - v = GUINT16_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 32: - { - uint32_t v = (uint32_t) value; - - if (rbo) { - v = GUINT32_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 64: - { - uint64_t v = (uint64_t) value; - - if (rbo) { - v = GUINT64_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - default: - abort(); - } - - _bt_ctfser_incr_offset(ctfser, size_bits); - return ret; -} - -static inline -int _bt_ctfser_write_byte_aligned_signed_int_no_align( - struct bt_ctfser *ctfser, int64_t value, - unsigned int size_bits, int byte_order) -{ - int ret = 0; - - /* Reverse byte order? */ - bool rbo = byte_order != BYTE_ORDER; - - BT_ASSERT(size_bits % 8 == 0); - BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); - - switch (size_bits) { - case 8: - { - int8_t v = (int8_t) value; - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 16: - { - int16_t v = (int16_t) value; - - if (rbo) { - v = GUINT16_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 32: - { - int32_t v = (int32_t) value; - - if (rbo) { - v = GUINT32_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - case 64: - { - int64_t v = (int64_t) value; - - if (rbo) { - v = GUINT64_SWAP_LE_BE(v); - } - - memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); - break; - } - default: - abort(); - } - - _bt_ctfser_incr_offset(ctfser, size_bits); - return ret; -} - -/* - * Writes an unsigned integer known to have a size that is a multiple of - * 8 and an alignment that is >= 8 at the current offset within the - * current packet. - */ -static inline -int bt_ctfser_write_byte_aligned_unsigned_int(struct bt_ctfser *ctfser, - uint64_t value, unsigned int alignment_bits, - unsigned int size_bits, int byte_order) -{ - int ret; - - BT_ASSERT(alignment_bits % 8 == 0); - ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); - if (unlikely(ret)) { - goto end; - } - - if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - ret = _bt_ctfser_write_byte_aligned_unsigned_int_no_align(ctfser, value, - size_bits, byte_order); - if (unlikely(ret)) { - goto end; - } - -end: - return ret; -} - -/* - * Writes a signed integer known to have a size that is a multiple of 8 - * and an alignment that is >= 8 at the current offset within the - * current packet. - */ -static inline -int bt_ctfser_write_byte_aligned_signed_int(struct bt_ctfser *ctfser, - int64_t value, unsigned int alignment_bits, - unsigned int size_bits, int byte_order) -{ - int ret; - - BT_ASSERT(alignment_bits % 8 == 0); - ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); - if (unlikely(ret)) { - goto end; - } - - if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - ret = _bt_ctfser_write_byte_aligned_signed_int_no_align(ctfser, value, - size_bits, byte_order); - if (unlikely(ret)) { - goto end; - } - -end: - return ret; -} - -/* - * Writes an unsigned integer at the current offset within the current - * packet. - */ -static inline -int bt_ctfser_write_unsigned_int(struct bt_ctfser *ctfser, uint64_t value, - unsigned int alignment_bits, unsigned int size_bits, - int byte_order) -{ - int ret = 0; - - ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); - if (unlikely(ret)) { - goto end; - } - - if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - if (alignment_bits % 8 == 0 && size_bits % 8 == 0) { - ret = _bt_ctfser_write_byte_aligned_unsigned_int_no_align( - ctfser, value, size_bits, byte_order); - goto end; - } - - if (byte_order == LITTLE_ENDIAN) { - bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) + - ctfser->mmap_base_offset, uint8_t, - ctfser->offset_in_cur_packet_bits, size_bits, value); - } else { - bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) + - ctfser->mmap_base_offset, uint8_t, - ctfser->offset_in_cur_packet_bits, size_bits, value); - } - - _bt_ctfser_incr_offset(ctfser, size_bits); - -end: - return ret; -} - -/* - * Writes a signed integer at the current offset within the current - * packet. - */ -static inline -int bt_ctfser_write_signed_int(struct bt_ctfser *ctfser, int64_t value, - unsigned int alignment_bits, unsigned int size_bits, - int byte_order) -{ - int ret = 0; - - ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); - if (unlikely(ret)) { - goto end; - } - - if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - if (alignment_bits % 8 == 0 && size_bits % 8 == 0) { - ret = _bt_ctfser_write_byte_aligned_signed_int_no_align( - ctfser, value, size_bits, byte_order); - goto end; - } - - if (byte_order == LITTLE_ENDIAN) { - bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) + - ctfser->mmap_base_offset, uint8_t, - ctfser->offset_in_cur_packet_bits, size_bits, value); - } else { - bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) + - ctfser->mmap_base_offset, uint8_t, - ctfser->offset_in_cur_packet_bits, size_bits, value); - } - - _bt_ctfser_incr_offset(ctfser, size_bits); - -end: - return ret; -} - -/* - * Writes a 32-bit floating point number at the current offset within - * the current packet. - */ -static inline -int bt_ctfser_write_float32(struct bt_ctfser *ctfser, double value, - unsigned int alignment_bits, int byte_order) -{ - union u32f { - uint32_t u; - float f; - } u32f; - - u32f.f = (float) value; - return bt_ctfser_write_unsigned_int(ctfser, (uint64_t) u32f.u, - alignment_bits, 32, byte_order); -} - -/* - * Writes a 64-bit floating point number at the current offset within - * the current packet. - */ -static inline -int bt_ctfser_write_float64(struct bt_ctfser *ctfser, double value, - unsigned int alignment_bits, int byte_order) -{ - union u64f { - uint64_t u; - float f; - } u64f; - - u64f.f = value; - return bt_ctfser_write_unsigned_int(ctfser, u64f.u, alignment_bits, - 64, byte_order); -} - -/* - * Writes a C string, including the terminating null character, at the - * current offset within the current packet. - */ -static inline -int bt_ctfser_write_string(struct bt_ctfser *ctfser, const char *value) -{ - int ret = 0; - const char *at = value; - - ret = bt_ctfser_align_offset_in_current_packet(ctfser, 8); - if (unlikely(ret)) { - goto end; - } - - while (true) { - if (unlikely(!_bt_ctfser_has_space_left(ctfser, 8))) { - ret = _bt_ctfser_increase_cur_packet_size(ctfser); - if (unlikely(ret)) { - goto end; - } - } - - memcpy(_bt_ctfser_get_addr(ctfser), at, sizeof(*at)); - _bt_ctfser_incr_offset(ctfser, 8); - - if (unlikely(*at == '\0')) { - break; - } - - at++; - } - -end: - return ret; -} - -/* - * Returns the current offset within the current packet (bits). - */ -static inline -uint64_t bt_ctfser_get_offset_in_current_packet_bits(struct bt_ctfser *ctfser) -{ - return ctfser->offset_in_cur_packet_bits; -} - -/* - * Sets the current offset within the current packet (bits). - */ -static inline -void bt_ctfser_set_offset_in_current_packet_bits(struct bt_ctfser *ctfser, - uint64_t offset_bits) -{ - BT_ASSERT(offset_bits <= _bt_ctfser_cur_packet_size_bits(ctfser)); - ctfser->offset_in_cur_packet_bits = offset_bits; -} - -static inline -const char *bt_ctfser_get_file_path(struct bt_ctfser *ctfser) -{ - return ctfser->path->str; -} - -#endif /* BABELTRACE_CTFSER_INTERNAL_H */ diff --git a/include/babeltrace2/endian-internal.h b/include/babeltrace2/endian-internal.h deleted file mode 100644 index 5c74c35d..00000000 --- a/include/babeltrace2/endian-internal.h +++ /dev/null @@ -1,240 +0,0 @@ -#ifndef _BABELTRACE_ENDIAN_H -#define _BABELTRACE_ENDIAN_H - -/* - * Copyright 2012 (c) - Mathieu Desnoyers - * - * endian.h compatibility layer. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef __FreeBSD__ -#include - -#elif defined(__sun__) -#include - -#ifndef __BIG_ENDIAN -#define __BIG_ENDIAN 4321 -#endif -#ifndef __LITTLE_ENDIAN -#define __LITTLE_ENDIAN 1234 -#endif - -#ifdef _LITTLE_ENDIAN -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif - -#ifdef _BIG_ENDIAN -#define __BYTE_ORDER __BIG_ENDIAN -#endif - -#define LITTLE_ENDIAN __LITTLE_ENDIAN -#define BIG_ENDIAN __BIG_ENDIAN -#define BYTE_ORDER __BYTE_ORDER - -#define betoh16(x) BE_16(x) -#define letoh16(x) LE_16(x) -#define betoh32(x) BE_32(x) -#define letoh32(x) LE_32(x) -#define betoh64(x) BE_64(x) -#define letoh64(x) LE_64(x) -#define htobe16(x) BE_16(x) -#define be16toh(x) BE_16(x) -#define htobe32(x) BE_32(x) -#define be32toh(x) BE_32(x) -#define htobe64(x) BE_64(x) -#define be64toh(x) BE_64(x) - -#elif defined(__MINGW32__) -#include - -#ifndef __BIG_ENDIAN -#define __BIG_ENDIAN 4321 -#endif -#ifndef __LITTLE_ENDIAN -#define __LITTLE_ENDIAN 1234 -#endif - -#ifndef __BYTE_ORDER -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif - -#define LITTLE_ENDIAN __LITTLE_ENDIAN -#define BIG_ENDIAN __BIG_ENDIAN -#define PDP_ENDIAN __PDP_ENDIAN -#define BYTE_ORDER __BYTE_ORDER - -#define htobe16(x) (uint16_t) _byteswap_ushort(x) -#define htole16(x) (x) -#define be16toh(x) (uint16_t) _byteswap_ushort(x) -#define le16toh(x) (x) - -#define htobe32(x) (uint32_t) _byteswap_ulong(x) -#define htole32(x) (x) -#define be32toh(x) (uint32_t) _byteswap_ulong(x) -#define le32toh(x) (x) - -#define htobe64(x) (uint64_t) _byteswap_uint64(x) -#define htole64(x) (x) -#define be64toh(x) (uint64_t) _byteswap_uint64(x) -#define le64toh(x) (x) - -#elif defined(__APPLE__) -# include -# include - -# if BYTE_ORDER == LITTLE_ENDIAN -# define htobe16(x) OSSwapConstInt16(x) -# define htole16(x) (x) -# define be16toh(x) OSSwapConstInt16(x) -# define le16toh(x) (x) - -# define htobe32(x) OSSwapConstInt32(x) -# define htole32(x) (x) -# define be32toh(x) OSSwapConstInt32(x) -# define le32toh(x) (x) - -# define htobe64(x) OSSwapConstInt64(x) -# define htole64(x) (x) -# define be64toh(x) OSSwapConstInt64(x) -# define le64toh(x) (x) - -# else /* BYTE_ORDER == LITTLE_ENDIAN */ -# define htobe16(x) (x) -# define htole16(x) OSSwapConstInt16(x) -# define be16toh(x) (x) -# define le16toh(x) OSSwapConstInt16(x) - -# define htobe32(x) (x) -# define htole32(x) OSSwapConstInt32(x) -# define be32toh(x) (x) -# define le32toh(x) OSSwapConstInt32(x) - -# define htobe64(x) (x) -# define htole64(x) OSSwapConstInt64(x) -# define be64toh(x) (x) -# define le64toh(x) OSSwapConstInt64(x) -# endif - -#else -#include - -/* - * htobe/betoh are not defined for glibc < 2.9, so add them explicitly - * if they are missing. - */ -# ifdef __USE_BSD -/* Conversion interfaces. */ -# include - -# if __BYTE_ORDER == __LITTLE_ENDIAN -# ifndef htobe16 -# define htobe16(x) __bswap_16(x) -# endif -# ifndef htole16 -# define htole16(x) (x) -# endif -# ifndef be16toh -# define be16toh(x) __bswap_16(x) -# endif -# ifndef le16toh -# define le16toh(x) (x) -# endif - -# ifndef htobe32 -# define htobe32(x) __bswap_32(x) -# endif -# ifndef htole32 -# define htole32(x) (x) -# endif -# ifndef be32toh -# define be32toh(x) __bswap_32(x) -# endif -# ifndef le32toh -# define le32toh(x) (x) -# endif - -# ifndef htobe64 -# define htobe64(x) __bswap_64(x) -# endif -# ifndef htole64 -# define htole64(x) (x) -# endif -# ifndef be64toh -# define be64toh(x) __bswap_64(x) -# endif -# ifndef le64toh -# define le64toh(x) (x) -# endif - -# else /* __BYTE_ORDER == __LITTLE_ENDIAN */ -# ifndef htobe16 -# define htobe16(x) (x) -# endif -# ifndef htole16 -# define htole16(x) __bswap_16(x) -# endif -# ifndef be16toh -# define be16toh(x) (x) -# endif -# ifndef le16toh -# define le16toh(x) __bswap_16(x) -# endif - -# ifndef htobe32 -# define htobe32(x) (x) -# endif -# ifndef htole32 -# define htole32(x) __bswap_32(x) -# endif -# ifndef be32toh -# define be32toh(x) (x) -# endif -# ifndef le32toh -# define le32toh(x) __bswap_32(x) -# endif - -# ifndef htobe64 -# define htobe64(x) (x) -# endif -# ifndef htole64 -# define htole64(x) __bswap_64(x) -# endif -# ifndef be64toh -# define be64toh(x) (x) -# endif -# ifndef le64toh -# define le64toh(x) __bswap_64(x) -# endif - -# endif /* __BYTE_ORDER == __LITTLE_ENDIAN */ -# endif /* __USE_BSD */ -#endif /* else -- __FreeBSD__ */ - -#ifndef FLOAT_WORD_ORDER -#ifdef __FLOAT_WORD_ORDER -#define FLOAT_WORD_ORDER __FLOAT_WORD_ORDER -#else /* __FLOAT_WORD_ORDER */ -#define FLOAT_WORD_ORDER BYTE_ORDER -#endif /* __FLOAT_WORD_ORDER */ -#endif /* FLOAT_WORD_ORDER */ - -#endif /* _BABELTRACE_ENDIAN_H */ diff --git a/include/babeltrace2/fd-cache-internal.h b/include/babeltrace2/fd-cache-internal.h deleted file mode 100644 index 546dcfaa..00000000 --- a/include/babeltrace2/fd-cache-internal.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef BABELTRACE_FD_CACHE_INTERNAL_H -#define BABELTRACE_FD_CACHE_INTERNAL_H -/* - * fd-cache-internal.h - * - * Babeltrace - File descriptor cache - * - * Copyright 2019 Francis Deslauriers - * - * Author: Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -struct bt_fd_cache_handle { - int fd; -}; - -struct bt_fd_cache { - GHashTable *cache; -}; - -static inline -int bt_fd_cache_handle_get_fd(struct bt_fd_cache_handle *handle) -{ - return handle->fd; -} - -BT_HIDDEN -int bt_fd_cache_init(struct bt_fd_cache *fdc); - -BT_HIDDEN -void bt_fd_cache_fini(struct bt_fd_cache *fdc); - -BT_HIDDEN -struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc, - const char *path); - -BT_HIDDEN -void bt_fd_cache_put_handle(struct bt_fd_cache *fdc, - struct bt_fd_cache_handle *handle); - -#endif /* BABELTRACE_FD_CACHE_INTERNAL_H */ diff --git a/include/babeltrace2/graph/component-class-internal.h b/include/babeltrace2/graph/component-class-internal.h deleted file mode 100644 index 4e39404f..00000000 --- a/include/babeltrace2/graph/component-class-internal.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H -#define BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_component_class; -struct bt_plugin_so_shared_lib_handle; - -typedef void (*bt_component_class_destroy_listener_func)( - struct bt_component_class *class, void *data); - -struct bt_component_class_destroy_listener { - bt_component_class_destroy_listener_func func; - void *data; -}; - -struct bt_component_class { - struct bt_object base; - enum bt_component_class_type type; - GString *name; - GString *description; - GString *help; - - /* Array of struct bt_component_class_destroy_listener */ - GArray *destroy_listeners; - bool frozen; - struct bt_list_head node; - struct bt_plugin_so_shared_lib_handle *so_handle; -}; - -struct bt_component_class_source { - struct bt_component_class parent; - struct { - bt_component_class_source_init_method init; - bt_component_class_source_finalize_method finalize; - bt_component_class_source_message_iterator_init_method msg_iter_init; - bt_component_class_source_message_iterator_finalize_method msg_iter_finalize; - bt_component_class_source_message_iterator_next_method msg_iter_next; - bt_component_class_source_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; - bt_component_class_source_message_iterator_seek_beginning_method msg_iter_seek_beginning; - bt_component_class_source_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; - bt_component_class_source_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; - bt_component_class_source_query_method query; - bt_component_class_source_accept_output_port_connection_method accept_output_port_connection; - bt_component_class_source_output_port_connected_method output_port_connected; - } methods; -}; - -struct bt_component_class_sink { - struct bt_component_class parent; - struct { - bt_component_class_sink_init_method init; - bt_component_class_sink_finalize_method finalize; - bt_component_class_sink_query_method query; - bt_component_class_sink_accept_input_port_connection_method accept_input_port_connection; - bt_component_class_sink_input_port_connected_method input_port_connected; - bt_component_class_sink_graph_is_configured_method graph_is_configured; - bt_component_class_sink_consume_method consume; - } methods; -}; - -struct bt_component_class_filter { - struct bt_component_class parent; - struct { - bt_component_class_filter_init_method init; - bt_component_class_filter_finalize_method finalize; - bt_component_class_filter_message_iterator_init_method msg_iter_init; - bt_component_class_filter_message_iterator_finalize_method msg_iter_finalize; - bt_component_class_filter_message_iterator_next_method msg_iter_next; - bt_component_class_filter_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; - bt_component_class_filter_message_iterator_seek_beginning_method msg_iter_seek_beginning; - bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; - bt_component_class_filter_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; - bt_component_class_filter_query_method query; - bt_component_class_filter_accept_input_port_connection_method accept_input_port_connection; - bt_component_class_filter_accept_output_port_connection_method accept_output_port_connection; - bt_component_class_filter_input_port_connected_method input_port_connected; - bt_component_class_filter_output_port_connected_method output_port_connected; - } methods; -}; - -BT_HIDDEN -void bt_component_class_add_destroy_listener(struct bt_component_class *class, - bt_component_class_destroy_listener_func func, void *data); - -BT_HIDDEN -void _bt_component_class_freeze( - const struct bt_component_class *component_class); - -#ifdef BT_DEV_MODE -# define bt_component_class_freeze _bt_component_class_freeze -#else -# define bt_component_class_freeze(_cc) -#endif - -static inline -const char *bt_component_class_type_string(enum bt_component_class_type type) -{ - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - return "BT_COMPONENT_CLASS_TYPE_SOURCE"; - case BT_COMPONENT_CLASS_TYPE_SINK: - return "BT_COMPONENT_CLASS_TYPE_SINK"; - case BT_COMPONENT_CLASS_TYPE_FILTER: - return "BT_COMPONENT_CLASS_TYPE_FILTER"; - default: - return "(unknown)"; - } -} - -#endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/graph/component-class-sink-colander-internal.h b/include/babeltrace2/graph/component-class-sink-colander-internal.h deleted file mode 100644 index faa7a6eb..00000000 --- a/include/babeltrace2/graph/component-class-sink-colander-internal.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H -#define BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct bt_component_class_sink_colander_priv_data { - bt_message_array_const msgs; - uint64_t *count_addr; - struct bt_self_component_port_input_message_iterator *msg_iter; -}; - -struct bt_component_class_sink_colander_data { - bt_message_array_const msgs; - uint64_t *count_addr; -}; - -extern struct bt_component_class_sink * -bt_component_class_sink_colander_get(void); - -#ifdef __cplusplus -} -#endif - -#endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H */ diff --git a/include/babeltrace2/graph/component-filter-internal.h b/include/babeltrace2/graph/component-filter-internal.h deleted file mode 100644 index 5d4b4a1d..00000000 --- a/include/babeltrace2/graph/component-filter-internal.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H -#define BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct bt_component_filter { - struct bt_component parent; -}; - -BT_HIDDEN -struct bt_component *bt_component_filter_create( - const struct bt_component_class *class); - -BT_HIDDEN -void bt_component_filter_destroy(struct bt_component *component); - -#endif /* BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H */ diff --git a/include/babeltrace2/graph/component-internal.h b/include/babeltrace2/graph/component-internal.h deleted file mode 100644 index 13986c87..00000000 --- a/include/babeltrace2/graph/component-internal.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_INTERNAL_H -#define BABELTRACE_GRAPH_COMPONENT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef void (*bt_component_destroy_listener_func)( - struct bt_component *class, void *data); - -struct bt_component_destroy_listener { - bt_component_destroy_listener_func func; - void *data; -}; - -struct bt_graph; - -struct bt_component { - struct bt_object base; - struct bt_component_class *class; - GString *name; - - /* - * Internal destroy function specific to a source, filter, or - * sink component object. - */ - void (*destroy)(struct bt_component *); - - /* User-defined data */ - void *user_data; - - /* Input and output ports (weak references) */ - GPtrArray *input_ports; - GPtrArray *output_ports; - - /* Array of struct bt_component_destroy_listener */ - GArray *destroy_listeners; - - bool initialized; -}; - -static inline -struct bt_graph *bt_component_borrow_graph(struct bt_component *comp) -{ - BT_ASSERT(comp); - return (void *) bt_object_borrow_parent(&comp->base); -} - -BT_HIDDEN -int bt_component_create(struct bt_component_class *component_class, - const char *name, struct bt_component **component); - -BT_HIDDEN -enum bt_self_component_status bt_component_accept_port_connection( - struct bt_component *component, struct bt_port *self_port, - struct bt_port *other_port); - -BT_HIDDEN -enum bt_self_component_status bt_component_port_connected( - struct bt_component *comp, - struct bt_port *self_port, struct bt_port *other_port); - -BT_HIDDEN -void bt_component_set_graph(struct bt_component *component, - struct bt_graph *graph); - -BT_HIDDEN -uint64_t bt_component_get_input_port_count(const struct bt_component *comp); - -BT_HIDDEN -uint64_t bt_component_get_output_port_count(const struct bt_component *comp); - -BT_HIDDEN -struct bt_port_input *bt_component_borrow_input_port_by_index( - struct bt_component *comp, uint64_t index); - -BT_HIDDEN -struct bt_port_output *bt_component_borrow_output_port_by_index( - struct bt_component *comp, uint64_t index); - -BT_HIDDEN -struct bt_port_input *bt_component_borrow_input_port_by_name( - struct bt_component *comp, const char *name); - -BT_HIDDEN -struct bt_port_output *bt_component_borrow_output_port_by_name( - struct bt_component *comp, const char *name); - -BT_HIDDEN -enum bt_self_component_status bt_component_add_input_port( - struct bt_component *component, const char *name, - void *user_data, struct bt_port **port); - -BT_HIDDEN -enum bt_self_component_status bt_component_add_output_port( - struct bt_component *component, const char *name, - void *user_data, struct bt_port **port); - -BT_HIDDEN -void bt_component_remove_port(struct bt_component *component, - struct bt_port *port); - -BT_HIDDEN -void bt_component_add_destroy_listener(struct bt_component *component, - bt_component_destroy_listener_func func, void *data); - -BT_HIDDEN -void bt_component_remove_destroy_listener(struct bt_component *component, - bt_component_destroy_listener_func func, void *data); - -static inline -const char *bt_self_component_status_string( - enum bt_self_component_status status) -{ - switch (status) { - case BT_SELF_COMPONENT_STATUS_OK: - return "BT_SELF_COMPONENT_STATUS_OK"; - case BT_SELF_COMPONENT_STATUS_END: - return "BT_SELF_COMPONENT_STATUS_END"; - case BT_SELF_COMPONENT_STATUS_AGAIN: - return "BT_SELF_COMPONENT_STATUS_AGAIN"; - case BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION: - return "BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION"; - case BT_SELF_COMPONENT_STATUS_ERROR: - return "BT_SELF_COMPONENT_STATUS_ERROR"; - case BT_SELF_COMPONENT_STATUS_NOMEM: - return "BT_SELF_COMPONENT_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -#endif /* BABELTRACE_GRAPH_COMPONENT_INTERNAL_H */ diff --git a/include/babeltrace2/graph/component-sink-internal.h b/include/babeltrace2/graph/component-sink-internal.h deleted file mode 100644 index d035711d..00000000 --- a/include/babeltrace2/graph/component-sink-internal.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H -#define BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_component_sink { - struct bt_component parent; - bool graph_is_configured_method_called; -}; - -BT_HIDDEN -struct bt_component *bt_component_sink_create( - const struct bt_component_class *class); - -BT_HIDDEN -void bt_component_sink_destroy(struct bt_component *component); - -#endif /* BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H */ diff --git a/include/babeltrace2/graph/component-source-internal.h b/include/babeltrace2/graph/component-source-internal.h deleted file mode 100644 index 9106d8ae..00000000 --- a/include/babeltrace2/graph/component-source-internal.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H -#define BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_component_source { - struct bt_component parent; -}; - -BT_HIDDEN -struct bt_component *bt_component_source_create( - const struct bt_component_class *class); - -BT_HIDDEN -void bt_component_source_destroy(struct bt_component *component); - -#endif /* BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H */ diff --git a/include/babeltrace2/graph/connection-internal.h b/include/babeltrace2/graph/connection-internal.h deleted file mode 100644 index 8c3dc17f..00000000 --- a/include/babeltrace2/graph/connection-internal.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef BABELTRACE_GRAPH_CONNECTION_INTERNAL_H -#define BABELTRACE_GRAPH_CONNECTION_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -struct bt_graph; - -struct bt_connection { - /* - * The graph is a connection's parent and the connection is the parent - * of all iterators it has created. - */ - struct bt_object base; - /* - * Weak references are held to both ports. Their existence is guaranteed - * by the existence of the graph and thus, of their respective - * components. - */ - /* Downstream port. */ - struct bt_port *downstream_port; - /* Upstream port. */ - struct bt_port *upstream_port; - - /* - * Weak references to all the message iterators that were - * created on this connection. - */ - GPtrArray *iterators; - - bool notified_upstream_port_connected; - bool notified_downstream_port_connected; - bool notified_graph_ports_connected; -}; - -BT_HIDDEN -struct bt_connection *bt_connection_create(struct bt_graph *graph, - struct bt_port *upstream_port, - struct bt_port *downstream_port); - -BT_HIDDEN -void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph); - -BT_HIDDEN -void bt_connection_remove_iterator(struct bt_connection *conn, - struct bt_self_component_port_input_message_iterator *iterator); - -static inline -struct bt_graph *bt_connection_borrow_graph(struct bt_connection *conn) -{ - BT_ASSERT(conn); - return (void *) conn->base.parent; -} - -#endif /* BABELTRACE_GRAPH_CONNECTION_INTERNAL_H */ diff --git a/include/babeltrace2/graph/graph-internal.h b/include/babeltrace2/graph/graph-internal.h deleted file mode 100644 index dd7374ed..00000000 --- a/include/babeltrace2/graph/graph-internal.h +++ /dev/null @@ -1,299 +0,0 @@ -#ifndef BABELTRACE_GRAPH_GRAPH_INTERNAL_H -#define BABELTRACE_GRAPH_GRAPH_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_component; -struct bt_port; - -enum bt_graph_configuration_state { - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED, - BT_GRAPH_CONFIGURATION_STATE_CONFIGURED, - BT_GRAPH_CONFIGURATION_STATE_FAULTY, -}; - -struct bt_graph { - /** - * A component graph contains components and point-to-point connection - * between these components. - * - * In terms of ownership: - * 1) The graph is the components' parent, - * 2) The graph is the connnections' parent, - * 3) Components share the ownership of their connections, - * 4) A connection holds weak references to its two component endpoints. - */ - struct bt_object base; - - /* Array of pointers to bt_connection. */ - GPtrArray *connections; - /* Array of pointers to bt_component. */ - GPtrArray *components; - /* Queue of pointers (weak references) to sink bt_components. */ - GQueue *sinks_to_consume; - - bool canceled; - bool in_remove_listener; - bool has_sink; - - /* - * If this is false, then the public API's consuming - * functions (bt_graph_consume() and bt_graph_run()) return - * BT_GRAPH_STATUS_CANNOT_CONSUME. The internal "no check" - * functions always work. - * - * In bt_port_output_message_iterator_create(), on success, - * this flag is cleared so that the iterator remains the only - * consumer for the graph's lifetime. - */ - bool can_consume; - - enum bt_graph_configuration_state config_state; - - struct { - GArray *source_output_port_added; - GArray *filter_output_port_added; - GArray *filter_input_port_added; - GArray *sink_input_port_added; - GArray *source_filter_ports_connected; - GArray *source_sink_ports_connected; - GArray *filter_filter_ports_connected; - GArray *filter_sink_ports_connected; - } listeners; - - /* Pool of `struct bt_message_event *` */ - struct bt_object_pool event_msg_pool; - - /* Pool of `struct bt_message_packet_beginning *` */ - struct bt_object_pool packet_begin_msg_pool; - - /* Pool of `struct bt_message_packet_end *` */ - struct bt_object_pool packet_end_msg_pool; - - /* - * Array of `struct bt_message *` (weak). - * - * This is an array of all the messages ever created from - * this graph. Some of them can be in one of the pools above, - * some of them can be at large. Because each message has a - * weak pointer to the graph containing its pool, we need to - * notify each message that the graph is gone on graph - * destruction. - * - * TODO: When we support a maximum size for object pools, - * add a way for a message to remove itself from this - * array (on destruction). - */ - GPtrArray *messages; -}; - -static inline -void _bt_graph_set_can_consume(struct bt_graph *graph, bool can_consume) -{ - BT_ASSERT(graph); - graph->can_consume = can_consume; -} - -#ifdef BT_DEV_MODE -# define bt_graph_set_can_consume _bt_graph_set_can_consume -#else -# define bt_graph_set_can_consume(_graph, _can_consume) -#endif - -BT_HIDDEN -enum bt_graph_status bt_graph_consume_sink_no_check(struct bt_graph *graph, - struct bt_component_sink *sink); - -BT_HIDDEN -enum bt_graph_listener_status bt_graph_notify_port_added(struct bt_graph *graph, - struct bt_port *port); - -BT_HIDDEN -enum bt_graph_listener_status bt_graph_notify_ports_connected( - struct bt_graph *graph, struct bt_port *upstream_port, - struct bt_port *downstream_port); - -BT_HIDDEN -void bt_graph_remove_connection(struct bt_graph *graph, - struct bt_connection *connection); - -/* - * This only works with a component which is not connected at this - * point. - * - * Also the reference count of `component` should be 0 when you call - * this function, which means only `graph` owns the component, so it - * is safe to destroy. - */ -BT_HIDDEN -int bt_graph_remove_unconnected_component(struct bt_graph *graph, - struct bt_component *component); - -BT_HIDDEN -void bt_graph_add_message(struct bt_graph *graph, - struct bt_message *msg); - -static inline -const char *bt_graph_status_string(enum bt_graph_status status) -{ - switch (status) { - case BT_GRAPH_STATUS_CANCELED: - return "BT_GRAPH_STATUS_CANCELED"; - case BT_GRAPH_STATUS_AGAIN: - return "BT_GRAPH_STATUS_AGAIN"; - case BT_GRAPH_STATUS_END: - return "BT_GRAPH_STATUS_END"; - case BT_GRAPH_STATUS_OK: - return "BT_GRAPH_STATUS_OK"; - case BT_GRAPH_STATUS_ERROR: - return "BT_GRAPH_STATUS_ERROR"; - case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; - case BT_GRAPH_STATUS_NOMEM: - return "BT_GRAPH_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_graph_configuration_state_string( - enum bt_graph_configuration_state state) -{ - switch (state) { - case BT_GRAPH_CONFIGURATION_STATE_CONFIGURING: - return "BT_GRAPH_CONFIGURATION_STATE_CONFIGURING"; - case BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED: - return "BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED"; - case BT_GRAPH_CONFIGURATION_STATE_CONFIGURED: - return "BT_GRAPH_CONFIGURATION_STATE_CONFIGURED"; - default: - return "(unknown)"; - } -} - -static inline -enum bt_graph_status bt_graph_configure(struct bt_graph *graph) -{ - enum bt_graph_status status = BT_GRAPH_STATUS_OK; - uint64_t i; - - BT_ASSERT(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY); - - if (likely(graph->config_state == - BT_GRAPH_CONFIGURATION_STATE_CONFIGURED)) { - goto end; - } - -#ifdef BT_ASSERT_PRE - BT_ASSERT_PRE(graph->has_sink, "Graph has no sink component: %!+g", graph); -#endif - - graph->config_state = BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED; - - for (i = 0; i < graph->components->len; i++) { - struct bt_component *comp = graph->components->pdata[i]; - struct bt_component_sink *comp_sink = (void *) comp; - struct bt_component_class_sink *comp_cls_sink = - (void *) comp->class; - - if (comp->class->type != BT_COMPONENT_CLASS_TYPE_SINK) { - continue; - } - - if (comp_sink->graph_is_configured_method_called) { - continue; - } - - if (comp_cls_sink->methods.graph_is_configured) { - enum bt_self_component_status comp_status; - -#ifdef BT_LIB_LOGD - BT_LIB_LOGD("Calling user's \"graph is configured\" method: " - "%![graph-]+g, %![comp-]+c", - graph, comp); -#endif - - comp_status = comp_cls_sink->methods.graph_is_configured( - (void *) comp_sink); - -#ifdef BT_LIB_LOGD - BT_LIB_LOGD("User method returned: status=%s", - bt_self_component_status_string(comp_status)); -#endif - -#ifdef BT_ASSERT_PRE - BT_ASSERT_PRE(comp_status == BT_SELF_COMPONENT_STATUS_OK || - comp_status == BT_SELF_COMPONENT_STATUS_ERROR || - comp_status == BT_SELF_COMPONENT_STATUS_NOMEM, - "Unexpected returned status: status=%s", - bt_self_component_status_string(comp_status)); -#endif - - if (comp_status != BT_SELF_COMPONENT_STATUS_OK) { - status = BT_GRAPH_STATUS_ERROR; -#ifdef BT_LIB_LOGW - BT_LIB_LOGW("User's \"graph is configured\" method failed: " - "%![comp-]+c, status=%s", - comp, - bt_self_component_status_string( - comp_status)); -#endif - - goto end; - } - } - - comp_sink->graph_is_configured_method_called = true; - } - - graph->config_state = BT_GRAPH_CONFIGURATION_STATE_CONFIGURED; - -end: - return status; -} - -static inline -void bt_graph_make_faulty(struct bt_graph *graph) -{ - graph->config_state = BT_GRAPH_CONFIGURATION_STATE_FAULTY; -#ifdef BT_LIB_LOGD - BT_LIB_LOGD("Set graph's state to faulty: %![graph-]+g", graph); -#endif -} - -#endif /* BABELTRACE_GRAPH_GRAPH_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-discarded-items-internal.h b/include/babeltrace2/graph/message-discarded-items-internal.h deleted file mode 100644 index 404a9eef..00000000 --- a/include/babeltrace2/graph/message-discarded-items-internal.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_message_discarded_items { - struct bt_message parent; - struct bt_stream *stream; - struct bt_clock_snapshot *default_begin_cs; - struct bt_clock_snapshot *default_end_cs; - struct bt_property_uint count; -}; - -#endif /* BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-event-internal.h b/include/babeltrace2/graph/message-event-internal.h deleted file mode 100644 index f8b8946b..00000000 --- a/include/babeltrace2/graph/message-event-internal.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct bt_message_event { - struct bt_message parent; - struct bt_event *event; - struct bt_clock_snapshot *default_cs; -}; - -BT_HIDDEN -struct bt_message *bt_message_event_new(struct bt_graph *graph); - -BT_HIDDEN -void bt_message_event_recycle(struct bt_message *msg); - -BT_HIDDEN -void bt_message_event_destroy(struct bt_message *msg); - -#ifdef __cplusplus -} -#endif - -#endif /* BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-internal.h b/include/babeltrace2/graph/message-internal.h deleted file mode 100644 index 28c66168..00000000 --- a/include/babeltrace2/graph/message-internal.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct bt_stream *(*get_stream_func)( - struct bt_message *message); - -struct bt_message { - struct bt_object base; - enum bt_message_type type; - bt_bool frozen; - - /* Owned by this; keeps the graph alive while the msg. is alive */ - struct bt_graph *graph; -}; - -#define BT_ASSERT_PRE_MSG_IS_TYPE(_msg, _type) \ - BT_ASSERT_PRE(((struct bt_message *) (_msg))->type == (_type), \ - "Message has the wrong type: expected-type=%s, " \ - "%![msg-]+n", bt_message_type_string(_type), \ - (_msg)) - -BT_HIDDEN -void bt_message_init(struct bt_message *message, - enum bt_message_type type, - bt_object_release_func release, - struct bt_graph *graph); - -static inline -void bt_message_reset(struct bt_message *message) -{ - BT_ASSERT(message); - -#ifdef BT_DEV_MODE - message->frozen = BT_FALSE; -#endif -} - -static inline -struct bt_message *bt_message_create_from_pool( - struct bt_object_pool *pool, struct bt_graph *graph) -{ - struct bt_message *msg = bt_object_pool_create_object(pool); - - if (unlikely(!msg)) { -#ifdef BT_LIB_LOGE - BT_LIB_LOGE("Cannot allocate one message from message pool: " - "%![pool-]+o, %![graph-]+g", pool, graph); -#endif - goto error; - } - - if (likely(!msg->graph)) { - msg->graph = graph; - } - - goto end; - -error: - BT_ASSERT(!msg); - -end: - return msg; -} - -static inline void _bt_message_freeze(struct bt_message *message) -{ - message->frozen = BT_TRUE; -} - -BT_HIDDEN -void bt_message_unlink_graph(struct bt_message *msg); - -#ifdef BT_DEV_MODE -# define bt_message_freeze _bt_message_freeze -#else -# define bt_message_freeze(_x) -#endif /* BT_DEV_MODE */ - -static inline -const char *bt_message_type_string(enum bt_message_type type) -{ - switch (type) { - case BT_MESSAGE_TYPE_EVENT: - return "BT_MESSAGE_TYPE_EVENT"; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - return "BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY"; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - return "BT_MESSAGE_TYPE_STREAM_BEGINNING"; - case BT_MESSAGE_TYPE_STREAM_END: - return "BT_MESSAGE_TYPE_STREAM_END"; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - return "BT_MESSAGE_TYPE_PACKET_BEGINNING"; - case BT_MESSAGE_TYPE_PACKET_END: - return "BT_MESSAGE_TYPE_PACKET_END"; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - return "BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING"; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - return "BT_MESSAGE_TYPE_STREAM_ACTIVITY_END"; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - return "BT_MESSAGE_TYPE_DISCARDED_EVENTS"; - default: - return "(unknown)"; - } -} - -#endif /* BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-iterator-internal.h b/include/babeltrace2/graph/message-iterator-internal.h deleted file mode 100644 index c6ef7b96..00000000 --- a/include/babeltrace2/graph/message-iterator-internal.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_port; -struct bt_graph; - -enum bt_message_iterator_type { - BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT, - BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT, -}; - -enum bt_self_component_port_input_message_iterator_state { - /* Iterator is not initialized */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED, - - /* Iterator is active, not at the end yet, and not finalized */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE, - - /* - * Iterator is ended, not finalized yet: the "next" method - * returns BT_MESSAGE_ITERATOR_STATUS_END. - */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED, - - /* Iterator is currently being finalized */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING, - - /* Iterator is finalized */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED, - - /* Iterator is seeking */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING, - - /* Iterator did seek, but returned `BT_MESSAGE_ITERATOR_STATUS_AGAIN` */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN, - - /* Iterator did seek, but returned error status */ - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR, -}; - -struct bt_message_iterator { - struct bt_object base; - enum bt_message_iterator_type type; - GPtrArray *msgs; -}; - -typedef enum bt_self_message_iterator_status -(*bt_self_component_port_input_message_iterator_next_method)( - void *, bt_message_array_const, uint64_t, uint64_t *); - -typedef enum bt_self_message_iterator_status -(*bt_self_component_port_input_message_iterator_seek_ns_from_origin_method)( - void *, int64_t); - -typedef enum bt_self_message_iterator_status -(*bt_self_component_port_input_message_iterator_seek_beginning_method)( - void *); - -typedef bt_bool -(*bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method)( - void *, int64_t); - -typedef bt_bool -(*bt_self_component_port_input_message_iterator_can_seek_beginning_method)( - void *); - -struct bt_self_component_port_input_message_iterator { - struct bt_message_iterator base; - struct bt_component *upstream_component; /* Weak */ - struct bt_port *upstream_port; /* Weak */ - struct bt_connection *connection; /* Weak */ - struct bt_graph *graph; /* Weak */ - - struct { - bt_self_component_port_input_message_iterator_next_method next; - bt_self_component_port_input_message_iterator_seek_ns_from_origin_method seek_ns_from_origin; - bt_self_component_port_input_message_iterator_seek_beginning_method seek_beginning; - bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method can_seek_ns_from_origin; - bt_self_component_port_input_message_iterator_can_seek_beginning_method can_seek_beginning; - } methods; - - enum bt_self_component_port_input_message_iterator_state state; - GQueue *auto_seek_msgs; - void *user_data; -}; - -struct bt_port_output_message_iterator { - struct bt_message_iterator base; - struct bt_graph *graph; /* Owned by this */ - struct bt_component_sink *colander; /* Owned by this */ - - /* - * Only used temporarily as a bridge between a colander sink and - * the user. - */ - uint64_t count; -}; - -BT_HIDDEN -void bt_self_component_port_input_message_iterator_try_finalize( - struct bt_self_component_port_input_message_iterator *iterator); - -BT_HIDDEN -void bt_self_component_port_input_message_iterator_set_connection( - struct bt_self_component_port_input_message_iterator *iterator, - struct bt_connection *connection); - -static inline -const char *bt_message_iterator_status_string( - enum bt_message_iterator_status status) -{ - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - return "BT_MESSAGE_ITERATOR_STATUS_AGAIN"; - case BT_MESSAGE_ITERATOR_STATUS_END: - return "BT_MESSAGE_ITERATOR_STATUS_END"; - case BT_MESSAGE_ITERATOR_STATUS_OK: - return "BT_MESSAGE_ITERATOR_STATUS_OK"; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - return "BT_MESSAGE_ITERATOR_STATUS_ERROR"; - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - return "BT_MESSAGE_ITERATOR_STATUS_NOMEM"; - default: - return "(unknown)"; - } -}; - -static inline -const char *bt_self_component_port_input_message_iterator_state_string( - enum bt_self_component_port_input_message_iterator_state state) -{ - switch (state) { - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN"; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR: - return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR"; - default: - return "(unknown)"; - } -}; - -#endif /* BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-message-iterator-inactivity-internal.h b/include/babeltrace2/graph/message-message-iterator-inactivity-internal.h deleted file mode 100644 index 5e607e29..00000000 --- a/include/babeltrace2/graph/message-message-iterator-inactivity-internal.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_message_message_iterator_inactivity { - struct bt_message parent; - struct bt_clock_snapshot *default_cs; -}; - -#endif /* BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-packet-internal.h b/include/babeltrace2/graph/message-packet-internal.h deleted file mode 100644 index 6e532bce..00000000 --- a/include/babeltrace2/graph/message-packet-internal.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_message_packet { - struct bt_message parent; - struct bt_packet *packet; - struct bt_clock_snapshot *default_cs; -}; - -BT_HIDDEN -void bt_message_packet_destroy(struct bt_message *msg); - -BT_HIDDEN -struct bt_message *bt_message_packet_beginning_new( - struct bt_graph *graph); -BT_HIDDEN -void bt_message_packet_beginning_recycle(struct bt_message *msg); - -BT_HIDDEN -struct bt_message *bt_message_packet_end_new(struct bt_graph *graph); - -BT_HIDDEN -void bt_message_packet_end_recycle(struct bt_message *msg); - -#endif /* BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-stream-activity-internal.h b/include/babeltrace2/graph/message-stream-activity-internal.h deleted file mode 100644 index 8298b0c7..00000000 --- a/include/babeltrace2/graph/message-stream-activity-internal.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_message_stream_activity { - struct bt_message parent; - struct bt_stream *stream; - struct bt_clock_snapshot *default_cs; - enum bt_message_stream_activity_clock_snapshot_state default_cs_state; -}; - -static inline -const char *bt_message_stream_activity_clock_snapshot_state_string( - enum bt_message_stream_activity_clock_snapshot_state state) -{ - switch (state) { - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: - return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN"; - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: - return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN"; - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: - return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE"; - default: - return "(unknown)"; - } -} - -#endif /* BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H */ diff --git a/include/babeltrace2/graph/message-stream-internal.h b/include/babeltrace2/graph/message-stream-internal.h deleted file mode 100644 index 54beef6c..00000000 --- a/include/babeltrace2/graph/message-stream-internal.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H -#define BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_message_stream { - struct bt_message parent; - struct bt_stream *stream; -}; - -#endif /* BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H */ diff --git a/include/babeltrace2/graph/port-internal.h b/include/babeltrace2/graph/port-internal.h deleted file mode 100644 index c1be31ad..00000000 --- a/include/babeltrace2/graph/port-internal.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef BABELTRACE_GRAPH_PORT_INTERNAL_H -#define BABELTRACE_GRAPH_PORT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -struct bt_port { - struct bt_object base; - enum bt_port_type type; - GString *name; - struct bt_connection *connection; - void *user_data; -}; - -struct bt_component; - -BT_HIDDEN -struct bt_port *bt_port_create(struct bt_component *parent_component, - enum bt_port_type type, const char *name, void *user_data); - -BT_HIDDEN -void bt_port_set_connection(struct bt_port *port, - struct bt_connection *connection); - -static inline -struct bt_component *bt_port_borrow_component_inline(const struct bt_port *port) -{ - BT_ASSERT(port); - return (void *) bt_object_borrow_parent(&port->base); -} - -static inline -const char *bt_port_type_string(enum bt_port_type port_type) -{ - switch (port_type) { - case BT_PORT_TYPE_INPUT: - return "BT_PORT_TYPE_INPUT"; - case BT_PORT_TYPE_OUTPUT: - return "BT_PORT_TYPE_OUTPUT"; - default: - return "(unknown)"; - } -} - -#endif /* BABELTRACE_GRAPH_PORT_INTERNAL_H */ diff --git a/include/babeltrace2/graph/query-executor-internal.h b/include/babeltrace2/graph/query-executor-internal.h deleted file mode 100644 index 2bf9367b..00000000 --- a/include/babeltrace2/graph/query-executor-internal.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H -#define BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct bt_query_executor { - struct bt_object base; - bool canceled; -}; - -static inline const char *bt_query_status_string(enum bt_query_status status) -{ - switch (status) { - case BT_QUERY_STATUS_OK: - return "BT_QUERY_STATUS_OK"; - case BT_QUERY_STATUS_AGAIN: - return "BT_QUERY_STATUS_AGAIN"; - case BT_QUERY_STATUS_ERROR: - return "BT_QUERY_STATUS_ERROR"; - case BT_QUERY_STATUS_INVALID_OBJECT: - return "BT_QUERY_STATUS_INVALID_OBJECT"; - case BT_QUERY_STATUS_INVALID_PARAMS: - return "BT_QUERY_STATUS_INVALID_PARAMS"; - case BT_QUERY_STATUS_NOMEM: - return "BT_QUERY_STATUS_NOMEM"; - default: - return "(unknown)"; - } -}; - -static inline const char *bt_query_executor_status_string( - enum bt_query_executor_status status) -{ - switch (status) { - case BT_QUERY_EXECUTOR_STATUS_OK: - return "BT_QUERY_EXECUTOR_STATUS_OK"; - case BT_QUERY_EXECUTOR_STATUS_AGAIN: - return "BT_QUERY_EXECUTOR_STATUS_AGAIN"; - case BT_QUERY_EXECUTOR_STATUS_CANCELED: - return "BT_QUERY_EXECUTOR_STATUS_CANCELED"; - case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: - return "BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED"; - case BT_QUERY_EXECUTOR_STATUS_ERROR: - return "BT_QUERY_EXECUTOR_STATUS_ERROR"; - case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: - return "BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT"; - case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: - return "BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS"; - case BT_QUERY_EXECUTOR_STATUS_NOMEM: - return "BT_QUERY_EXECUTOR_STATUS_NOMEM"; - default: - return "(unknown)"; - } -}; - -#endif /* BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H */ diff --git a/include/babeltrace2/lib-logging-internal.h b/include/babeltrace2/lib-logging-internal.h deleted file mode 100644 index 3b12756d..00000000 --- a/include/babeltrace2/lib-logging-internal.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H -#define BABELTRACE_LIB_LOGGING_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#ifndef BT_LOG_TAG -# error Please define a tag with BT_LOG_TAG before including this file. -#endif - -#define BT_LOG_OUTPUT_LEVEL bt_lib_log_level - -#include - -extern -int bt_lib_log_level; - -#define BT_LIB_LOG(_lvl, _fmt, ...) \ - do { \ - if (BT_LOG_ON(_lvl)) { \ - bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__, \ - __LINE__, _lvl, _BT_LOG_TAG, \ - (_fmt), ##__VA_ARGS__); \ - } \ - } while (0) - -/* - * The six macros below are logging statements which are specialized - * for the Babeltrace library. - * - * `_fmt` is a typical printf()-style format string, with the following - * limitations: - * - * * The `*` width specifier is not accepted. - * * The `*` precision specifier is not accepted. - * * The `j` and `t` length modifiers are not accepted. - * * The `n` format specifier is not accepted. - * * The format specifiers defined in are not accepted - * except for `PRId64`, `PRIu64`, `PRIx64`, `PRIX64`, `PRIo64`, and - * `PRIi64`. - * - * The Babeltrace extension conversion specifier is accepted. Its syntax - * is either `%!u` to format a UUID (`bt_uuid` type) or: - * - * 1. Introductory `%!` sequence. - * - * 2. Optional: `[` followed by a custom prefix for the printed fields - * of this specifier, followed by `]`. The standard form is to end - * this prefix with `-` so that, for example, with the prefix - * `prefix-`, the complete field name is `prefix-addr`. - * - * 3. Optional: `+` to print extended fields. This depends on the - * provided format specifier. - * - * 4. Format specifier (see below). - * - * The available format specifiers are: - * - * `F`: - * Trace IR field class. The parameter type is - * `struct bt_field_class *`. - * - * `f`: - * Trace IR field. The parameter type is `struct bt_field *`. - * - * `P`: - * Field path. The parameter type is `struct bt_field_path *`. - * - * `E`: - * Trace IR event class. The parameter type is - * `struct bt_event_class *`. - * - * `e`: - * Trace IR event. The parameter type is `struct bt_event *`. - * - * `S`: - * Trace IR stream class. The parameter type is - * `struct bt_stream_class *`. - * - * `s`: - * Trace IR stream. The parameter type is `struct bt_stream *`. - * - * `a`: - * Packet. The parameter type is `struct bt_packet *`. - * - * `T`: - * Trace IR trace class. The parameter type is `struct bt_trace_class *`. - * - * `t`: - * Trace IR trace. The parameter type is `struct bt_trace *`. - * - * `K`: - * Clock class. The parameter type is `struct bt_clock_class *`. - * - * `k`: - * Clock snapshot. The parameter type is `struct bt_clock_snapshot *`. - * - * `v`: - * Value. The parameter type is `struct bt_value *`. - * - * `n`: - * Message. The parameter type is `struct bt_message *`. - * - * `i`: - * Message iterator. The parameter type is - * `struct bt_message_iterator *`. - * - * `C`: - * Component class. The parameter type is - * `struct bt_component_class *`. - * - * `c`: - * Component. The parameter type is `struct bt_component *`. - * - * `p`: - * Port. The parameter type is `struct bt_port *`. - * - * `x`: - * Connection. The parameter type is `struct bt_connection *`. - * - * `g`: - * Graph. The parameter type is `struct bt_graph *`. - * - * `l`: - * Plugin. The parameter type is `const struct bt_plugin *`. - * - * `o`: - * Object pool. The parameter type is `struct bt_object_pool *`. - * - * `O`: - * Object. The parameter type is `struct bt_object *`. - * - * Conversion specifier examples: - * - * %!f - * %![my-event-]+e - * %!t - * %!+F - * - * The string `, ` is printed between individual fields, but not after - * the last one. Therefore you must put this separator in the format - * string between two conversion specifiers, e.g.: - * - * BT_LIB_LOGW("Message: count=%u, %!E, %!+K", count, event_class, - * clock_class); - * - * Example with a custom prefix: - * - * BT_LIB_LOGI("Some message: %![ec-a-]e, %![ec-b-]+e", ec_a, ec_b); - * - * It is safe to pass NULL as any Babeltrace object parameter: the - * macros only print its null address. - */ -#define BT_LIB_LOGF(_fmt, ...) BT_LIB_LOG(BT_LOG_FATAL, _fmt, ##__VA_ARGS__) -#define BT_LIB_LOGE(_fmt, ...) BT_LIB_LOG(BT_LOG_ERROR, _fmt, ##__VA_ARGS__) -#define BT_LIB_LOGW(_fmt, ...) BT_LIB_LOG(BT_LOG_WARN, _fmt, ##__VA_ARGS__) -#define BT_LIB_LOGI(_fmt, ...) BT_LIB_LOG(BT_LOG_INFO, _fmt, ##__VA_ARGS__) -#define BT_LIB_LOGD(_fmt, ...) BT_LIB_LOG(BT_LOG_DEBUG, _fmt, ##__VA_ARGS__) -#define BT_LIB_LOGV(_fmt, ...) BT_LIB_LOG(BT_LOG_VERBOSE, _fmt, ##__VA_ARGS__) - -/* - * Log statement, specialized for the Babeltrace library. - * - * Use one of the BT_LIB_LOGF*() macros above instead of calling this - * function directly. - */ - -void bt_lib_log(const char *func, const char *file, unsigned line, - int lvl, const char *tag, const char *fmt, ...); - -#endif /* BABELTRACE_LIB_LOGGING_INTERNAL_H */ diff --git a/include/babeltrace2/list-internal.h b/include/babeltrace2/list-internal.h deleted file mode 100644 index 48d9bbb9..00000000 --- a/include/babeltrace2/list-internal.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2002 Free Software Foundation, Inc. - * This file is part of the GNU C Library. - * Contributed by Ulrich Drepper , 2002. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; only - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _BT_LIST_H -#define _BT_LIST_H 1 - -/* The definitions of this file are adopted from those which can be - found in the Linux kernel headers to enable people familiar with - the latter find their way in these sources as well. */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Basic type for the double-link list. */ -struct bt_list_head -{ - struct bt_list_head *next; - struct bt_list_head *prev; -}; - - -/* Define a variable with the head and tail of the list. */ -#define BT_LIST_HEAD(name) \ - struct bt_list_head name = { &(name), &(name) } - -/* Initialize a new list head. */ -#define BT_INIT_LIST_HEAD(ptr) \ - (ptr)->next = (ptr)->prev = (ptr) - -#define BT_LIST_HEAD_INIT(name) { .prev = &(name), .next = &(name) } - -/* Add new element at the head of the list. */ -static inline void -bt_list_add (struct bt_list_head *newp, struct bt_list_head *head) -{ - head->next->prev = newp; - newp->next = head->next; - newp->prev = head; - head->next = newp; -} - - -/* Add new element at the tail of the list. */ -static inline void -bt_list_add_tail (struct bt_list_head *newp, struct bt_list_head *head) -{ - head->prev->next = newp; - newp->next = head; - newp->prev = head->prev; - head->prev = newp; -} - - -/* Remove element from list. */ -static inline void -__bt_list_del (struct bt_list_head *prev, struct bt_list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -/* Remove element from list. */ -static inline void -bt_list_del (struct bt_list_head *elem) -{ - __bt_list_del (elem->prev, elem->next); -} - -/* delete from list, add to another list as head */ -static inline void -bt_list_move (struct bt_list_head *elem, struct bt_list_head *head) -{ - __bt_list_del (elem->prev, elem->next); - bt_list_add (elem, head); -} - -/* replace an old entry. - */ -static inline void -bt_list_replace(struct bt_list_head *old, struct bt_list_head *_new) -{ - _new->next = old->next; - _new->prev = old->prev; - _new->prev->next = _new; - _new->next->prev = _new; -} - -/* Join two lists. */ -static inline void -bt_list_splice (struct bt_list_head *add, struct bt_list_head *head) -{ - /* Do nothing if the list which gets added is empty. */ - if (add != add->next) - { - add->next->prev = head; - add->prev->next = head->next; - head->next->prev = add->prev; - head->next = add->next; - } -} - - -/* Get typed element from list at a given position. */ -#define bt_list_entry(ptr, type, member) \ - ((type *) ((char *) (ptr) - (uintptr_t) (&((type *) 0)->member))) - - - -/* Iterate forward over the elements of the list. */ -#define bt_list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - - -/* Iterate forward over the elements of the list. */ -#define bt_list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) - - -/* Iterate backwards over the elements list. The list elements can be - removed from the list while doing this. */ -#define bt_list_for_each_prev_safe(pos, p, head) \ - for (pos = (head)->prev, p = pos->prev; \ - pos != (head); \ - pos = p, p = pos->prev) - -#define bt_list_for_each_entry(pos, head, member) \ - for (pos = bt_list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = bt_list_entry(pos->member.next, typeof(*pos), member)) - -#define bt_list_for_each_entry_reverse(pos, head, member) \ - for (pos = bt_list_entry((head)->prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = bt_list_entry(pos->member.prev, typeof(*pos), member)) - -#define bt_list_for_each_entry_safe(pos, p, head, member) \ - for (pos = bt_list_entry((head)->next, typeof(*pos), member), \ - p = bt_list_entry(pos->member.next,typeof(*pos), member); \ - &pos->member != (head); \ - pos = p, p = bt_list_entry(pos->member.next, typeof(*pos), member)) - -static inline int bt_list_empty(struct bt_list_head *head) -{ - return head == head->next; -} - -static inline void bt_list_replace_init(struct bt_list_head *old, - struct bt_list_head *_new) -{ - struct bt_list_head *head = old->next; - bt_list_del(old); - bt_list_add_tail(_new, head); - BT_INIT_LIST_HEAD(old); -} - -#ifdef __cplusplus -} -#endif - -#endif /* _BT_LIST_H */ diff --git a/include/babeltrace2/logging-internal.h b/include/babeltrace2/logging-internal.h deleted file mode 100644 index 7aff10b2..00000000 --- a/include/babeltrace2/logging-internal.h +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * This is zf_log.h, modified with Babeltrace prefixes. - * See . - * See logging/LICENSE in the Babeltrace source tree. - */ - -#pragma once - -#ifndef BABELTRACE_LOGGING_INTERNAL_H -#define BABELTRACE_LOGGING_INTERNAL_H - -#include -#include -#include -#include - -/* To detect incompatible changes you can define BT_LOG_VERSION_REQUIRED to be - * the current value of BT_LOG_VERSION before including this file (or via - * compiler command line): - * - * #define BT_LOG_VERSION_REQUIRED 4 - * #include - * - * Compilation will fail when included file has different version. - */ -#define BT_LOG_VERSION 4 -#if defined(BT_LOG_VERSION_REQUIRED) - #if BT_LOG_VERSION_REQUIRED != BT_LOG_VERSION - #error different bt_log version required - #endif -#endif - -/* Log level guideline: - * - BT_LOG_FATAL - happened something impossible and absolutely unexpected. - * Process can't continue and must be terminated. - * Example: division by zero, unexpected modifications from other thread. - * - BT_LOG_ERROR - happened something possible, but highly unexpected. The - * process is able to recover and continue execution. - * Example: out of memory (could also be FATAL if not handled properly). - * - BT_LOG_WARN - happened something that *usually* should not happen and - * significantly changes application behavior for some period of time. - * Example: configuration file not found, auth error. - * - BT_LOG_INFO - happened significant life cycle event or major state - * transition. - * Example: app started, user logged in. - * - BT_LOG_DEBUG - minimal set of events that could help to reconstruct the - * execution path. Usually disabled in release builds. - * - BT_LOG_VERBOSE - all other events. Usually disabled in release builds. - * - * *Ideally*, log file of debugged, well tested, production ready application - * should be empty or very small. Choosing a right log level is as important as - * providing short and self descriptive log message. - */ -#define BT_LOG_VERBOSE BT_LOGGING_LEVEL_VERBOSE -#define BT_LOG_DEBUG BT_LOGGING_LEVEL_DEBUG -#define BT_LOG_INFO BT_LOGGING_LEVEL_INFO -#define BT_LOG_WARN BT_LOGGING_LEVEL_WARN -#define BT_LOG_ERROR BT_LOGGING_LEVEL_ERROR -#define BT_LOG_FATAL BT_LOGGING_LEVEL_FATAL -#define BT_LOG_NONE BT_LOGGING_LEVEL_NONE - -/* "Current" log level is a compile time check and has no runtime overhead. Log - * level that is below current log level it said to be "disabled". Otherwise, - * it's "enabled". Log messages that are disabled has no runtime overhead - they - * are converted to no-op by preprocessor and then eliminated by compiler. - * Current log level is configured per compilation module (.c/.cpp/.m file) by - * defining BT_LOG_DEF_LEVEL or BT_LOG_LEVEL. BT_LOG_LEVEL has higer priority - * and when defined overrides value provided by BT_LOG_DEF_LEVEL. - * - * Common practice is to define default current log level with BT_LOG_DEF_LEVEL - * in build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire - * project or target: - * - * CC_ARGS := -DBT_LOG_DEF_LEVEL=BT_LOG_INFO - * - * And when necessary to override it with BT_LOG_LEVEL in .c/.cpp/.m files - * before including bt_log.h: - * - * #define BT_LOG_LEVEL BT_LOG_VERBOSE - * #include - * - * If both BT_LOG_DEF_LEVEL and BT_LOG_LEVEL are undefined, then BT_LOG_INFO - * will be used for release builds (NDEBUG is defined) and BT_LOG_DEBUG - * otherwise (NDEBUG is not defined). - */ -#if defined(BT_LOG_LEVEL) - #define _BT_LOG_LEVEL BT_LOG_LEVEL -#elif defined(BT_LOG_DEF_LEVEL) - #define _BT_LOG_LEVEL BT_LOG_DEF_LEVEL -#else - #ifdef NDEBUG - #define _BT_LOG_LEVEL BT_LOG_INFO - #else - #define _BT_LOG_LEVEL BT_LOG_DEBUG - #endif -#endif - -/* "Output" log level is a runtime check. When log level is below output log - * level it said to be "turned off" (or just "off" for short). Otherwise it's - * "turned on" (or just "on"). Log levels that were "disabled" (see - * BT_LOG_LEVEL and BT_LOG_DEF_LEVEL) can't be "turned on", but "enabled" log - * levels could be "turned off". Only messages with log level which is - * "turned on" will reach output facility. All other messages will be ignored - * (and their arguments will not be evaluated). Output log level is a global - * property and configured per process using bt_log_set_output_level() function - * which can be called at any time. - * - * Though in some cases it could be useful to configure output log level per - * compilation module or per library. There are two ways to achieve that: - * - Define BT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired output - * log level. - * - Copy bt_log.h and bt_log.c files into your library and build it with - * BT_LOG_LIBRARY_PREFIX defined to library specific prefix. See - * BT_LOG_LIBRARY_PREFIX for more details. - * - * When defined, BT_LOG_OUTPUT_LEVEL must evaluate to integral value that - * corresponds to desired output log level. Use it only when compilation module - * is required to have output log level which is different from global output - * log level set by bt_log_set_output_level() function. For other cases, - * consider defining BT_LOG_LEVEL or using bt_log_set_output_level() function. - * - * Example: - * - * #define BT_LOG_OUTPUT_LEVEL g_module_log_level - * #include - * static int g_module_log_level = BT_LOG_INFO; - * static void foo() { - * BT_LOGI("Will check g_module_log_level for output log level"); - * } - * void debug_log(bool on) { - * g_module_log_level = on? BT_LOG_DEBUG: BT_LOG_INFO; - * } - * - * Note on performance. This expression will be evaluated each time message is - * logged (except when message log level is "disabled" - see BT_LOG_LEVEL for - * details). Keep this expression as simple as possible, otherwise it will not - * only add runtime overhead, but also will increase size of call site (which - * will result in larger executable). The prefered way is to use integer - * variable (as in example above). If structure must be used, log_level field - * must be the first field in this structure: - * - * #define BT_LOG_OUTPUT_LEVEL (g_config.log_level) - * #include - * struct config { - * int log_level; - * unsigned other_field; - * [...] - * }; - * static config g_config = {BT_LOG_INFO, 0, ...}; - * - * This allows compiler to generate more compact load instruction (no need to - * specify offset since it's zero). Calling a function to get output log level - * is generaly a bad idea, since it will increase call site size and runtime - * overhead even further. - */ -#if defined(BT_LOG_OUTPUT_LEVEL) - #define _BT_LOG_OUTPUT_LEVEL BT_LOG_OUTPUT_LEVEL -#else - /* - * We disallow this to make sure Babeltrace modules always - * have their own local log level. - */ - #error No log level symbol specified: please define BT_LOG_OUTPUT_LEVEL before including this header. -#endif - -/* "Tag" is a compound string that could be associated with a log message. It - * consists of tag prefix and tag (both are optional). - * - * Tag prefix is a global property and configured per process using - * bt_log_set_tag_prefix() function. Tag prefix identifies context in which - * component or module is running (e.g. process name). For example, the same - * library could be used in both client and server processes that work on the - * same machine. Tag prefix could be used to easily distinguish between them. - * For more details about tag prefix see bt_log_set_tag_prefix() function. Tag - * prefix - * - * Tag identifies component or module. It is configured per compilation module - * (.c/.cpp/.m file) by defining BT_LOG_TAG or BT_LOG_DEF_TAG. BT_LOG_TAG has - * higer priority and when defined overrides value provided by BT_LOG_DEF_TAG. - * When defined, value must evaluate to (const char *), so for strings double - * quotes must be used. - * - * Default tag could be defined with BT_LOG_DEF_TAG in build script (e.g. - * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target: - * - * CC_ARGS := -DBT_LOG_DEF_TAG=\"MISC\" - * - * And when necessary could be overriden with BT_LOG_TAG in .c/.cpp/.m files - * before including bt_log.h: - * - * #define BT_LOG_TAG "MAIN" - * #include - * - * If both BT_LOG_DEF_TAG and BT_LOG_TAG are undefined no tag will be added to - * the log message (tag prefix still could be added though). - * - * Output example: - * - * 04-29 22:43:20.244 40059 1299 I hello.MAIN Number of arguments: 1 - * | | - * | +- tag (e.g. module) - * +- tag prefix (e.g. process name) - */ -#if defined(BT_LOG_TAG) - #define _BT_LOG_TAG BT_LOG_TAG -#elif defined(BT_LOG_DEF_TAG) - #define _BT_LOG_TAG BT_LOG_DEF_TAG -#else - #define _BT_LOG_TAG 0 -#endif - -/* Source location is part of a log line that describes location (function or - * method name, file name and line number, e.g. "runloop@main.cpp:68") of a - * log statement that produced it. - * Source location formats are: - * - BT_LOG_SRCLOC_NONE - don't add source location to log line. - * - BT_LOG_SRCLOC_SHORT - add source location in short form (file and line - * number, e.g. "@main.cpp:68"). - * - BT_LOG_SRCLOC_LONG - add source location in long form (function or method - * name, file and line number, e.g. "runloop@main.cpp:68"). - */ -#define BT_LOG_SRCLOC_NONE 0 -#define BT_LOG_SRCLOC_SHORT 1 -#define BT_LOG_SRCLOC_LONG 2 - -/* Source location format is configured per compilation module (.c/.cpp/.m - * file) by defining BT_LOG_DEF_SRCLOC or BT_LOG_SRCLOC. BT_LOG_SRCLOC has - * higer priority and when defined overrides value provided by - * BT_LOG_DEF_SRCLOC. - * - * Common practice is to define default format with BT_LOG_DEF_SRCLOC in - * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire - * project or target: - * - * CC_ARGS := -DBT_LOG_DEF_SRCLOC=BT_LOG_SRCLOC_LONG - * - * And when necessary to override it with BT_LOG_SRCLOC in .c/.cpp/.m files - * before including bt_log.h: - * - * #define BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE - * #include - * - * If both BT_LOG_DEF_SRCLOC and BT_LOG_SRCLOC are undefined, then - * BT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined) and - * BT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined). - */ -#if defined(BT_LOG_SRCLOC) - #define _BT_LOG_SRCLOC BT_LOG_SRCLOC -#elif defined(BT_LOG_DEF_SRCLOC) - #define _BT_LOG_SRCLOC BT_LOG_DEF_SRCLOC -#else - #ifdef NDEBUG - #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE - #else - #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_LONG - #endif -#endif -#if BT_LOG_SRCLOC_LONG == _BT_LOG_SRCLOC - #define _BT_LOG_SRCLOC_FUNCTION _BT_LOG_FUNCTION -#else - #define _BT_LOG_SRCLOC_FUNCTION 0 -#endif - -/* Censoring provides conditional logging of secret information, also known as - * Personally Identifiable Information (PII) or Sensitive Personal Information - * (SPI). Censoring can be either enabled (BT_LOG_CENSORED) or disabled - * (BT_LOG_UNCENSORED). When censoring is enabled, log statements marked as - * "secrets" will be ignored and will have zero overhead (arguments also will - * not be evaluated). - */ -#define BT_LOG_CENSORED 1 -#define BT_LOG_UNCENSORED 0 - -/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining - * BT_LOG_DEF_CENSORING or BT_LOG_CENSORING. BT_LOG_CENSORING has higer priority - * and when defined overrides value provided by BT_LOG_DEF_CENSORING. - * - * Common practice is to define default censoring with BT_LOG_DEF_CENSORING in - * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire - * project or target: - * - * CC_ARGS := -DBT_LOG_DEF_CENSORING=BT_LOG_CENSORED - * - * And when necessary to override it with BT_LOG_CENSORING in .c/.cpp/.m files - * before including bt_log.h (consider doing it only for debug purposes and be - * very careful not to push such temporary changes to source control): - * - * #define BT_LOG_CENSORING BT_LOG_UNCENSORED - * #include - * - * If both BT_LOG_DEF_CENSORING and BT_LOG_CENSORING are undefined, then - * BT_LOG_CENSORED will be used for release builds (NDEBUG is defined) and - * BT_LOG_UNCENSORED otherwise (NDEBUG is not defined). - */ -#if defined(BT_LOG_CENSORING) - #define _BT_LOG_CENSORING BT_LOG_CENSORING -#elif defined(BT_LOG_DEF_CENSORING) - #define _BT_LOG_CENSORING BT_LOG_DEF_CENSORING -#else - #ifdef NDEBUG - #define _BT_LOG_CENSORING BT_LOG_CENSORED - #else - #define _BT_LOG_CENSORING BT_LOG_UNCENSORED - #endif -#endif - -/* Check censoring at compile time. Evaluates to true when censoring is disabled - * (i.e. when secrets will be logged). For example: - * - * #if BT_LOG_SECRETS - * char ssn[16]; - * getSocialSecurityNumber(ssn); - * BT_LOGI("Customer ssn: %s", ssn); - * #endif - * - * See BT_LOG_SECRET() macro for a more convenient way of guarding single log - * statement. - */ -#define BT_LOG_SECRETS (BT_LOG_UNCENSORED == _BT_LOG_CENSORING) - -/* Static (compile-time) initialization support allows to configure logging - * before entering main() function. This mostly useful in C++ where functions - * and methods could be called during initialization of global objects. Those - * functions and methods could record log messages too and for that reason - * static initialization of logging configuration is customizable. - * - * Macros below allow to specify values to use for initial configuration: - * - BT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none) - * - BT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see - * BT_LOG_MEM_WIDTH in bt_log.c) - * - BT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: stderr or - * platform specific, see BT_LOG_USE_XXX macros in bt_log.c) - * - BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level (default: 0 - - * all levals are "turned on") - * - * For example, in log_config.c: - * - * #include - * BT_LOG_DEFINE_TAG_PREFIX = "MyApp"; - * BT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH}; - * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback, 0}; - * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_INFO; - * - * However, to use any of those macros bt_log library must be compiled with - * following macros defined: - * - to use BT_LOG_DEFINE_TAG_PREFIX define BT_LOG_EXTERN_TAG_PREFIX - * - to use BT_LOG_DEFINE_GLOBAL_FORMAT define BT_LOG_EXTERN_GLOBAL_FORMAT - * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT define BT_LOG_EXTERN_GLOBAL_OUTPUT - * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define - * BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - * - * When bt_log library compiled with one of BT_LOG_EXTERN_XXX macros defined, - * corresponding BT_LOG_DEFINE_XXX macro MUST be used exactly once somewhere. - * Otherwise build will fail with link error (undefined symbol). - */ -#define BT_LOG_DEFINE_TAG_PREFIX BT_HIDDEN const char *_bt_log_tag_prefix -#define BT_LOG_DEFINE_GLOBAL_FORMAT BT_HIDDEN bt_log_format _bt_log_global_format -#define BT_LOG_DEFINE_GLOBAL_OUTPUT BT_HIDDEN bt_log_output _bt_log_global_output -#define BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL BT_HIDDEN int _bt_log_global_output_lvl - -/* Pointer to global format options. Direct modification is not allowed. Use - * bt_log_set_mem_width() instead. Could be used to initialize bt_log_spec - * structure: - * - * const bt_log_output g_output = {BT_LOG_PUT_STD, output_callback, 0}; - * const bt_log_spec g_spec = {BT_LOG_GLOBAL_FORMAT, &g_output}; - * BT_LOGI_AUX(&g_spec, "Hello"); - */ -#define BT_LOG_GLOBAL_FORMAT ((const bt_log_format *)&_bt_log_global_format) - -/* Pointer to global output variable. Direct modification is not allowed. Use - * bt_log_set_output_v() or bt_log_set_output_p() instead. Could be used to - * initialize bt_log_spec structure: - * - * const bt_log_format g_format = {40}; - * const bt_log_spec g_spec = {g_format, BT_LOG_GLOBAL_OUTPUT}; - * BT_LOGI_AUX(&g_spec, "Hello"); - */ -#define BT_LOG_GLOBAL_OUTPUT ((const bt_log_output *)&_bt_log_global_output) - -/* When defined, all library symbols produced by linker will be prefixed with - * provided value. That allows to use bt_log library privately in another - * libraries without exposing bt_log symbols in their original form (to avoid - * possible conflicts with other libraries / components that also could use - * bt_log for logging). Value must be without quotes, for example: - * - * CC_ARGS := -DBT_LOG_LIBRARY_PREFIX=my_lib_ - * - * Note, that in this mode BT_LOG_LIBRARY_PREFIX must be defined when building - * bt_log library AND it also must be defined to the same value when building - * a library that uses it. For example, consider fictional KittyHttp library - * that wants to use bt_log for logging. First approach that could be taken is - * to add bt_log.h and bt_log.c to the KittyHttp's source code tree directly. - * In that case it will be enough just to define BT_LOG_LIBRARY_PREFIX in - * KittyHttp's build script: - * - * // KittyHttp/CMakeLists.txt - * target_compile_definitions(KittyHttp PRIVATE - * "BT_LOG_LIBRARY_PREFIX=KittyHttp_") - * - * If KittyHttp doesn't want to include bt_log source code in its source tree - * and wants to build bt_log as a separate library than bt_log library must be - * built with BT_LOG_LIBRARY_PREFIX defined to KittyHttp_ AND KittyHttp library - * itself also needs to define BT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do - * so either in its build script, as in example above, or by providing a - * wrapper header that KittyHttp library will need to use instead of bt_log.h: - * - * // KittyHttpLogging.h - * #define BT_LOG_LIBRARY_PREFIX KittyHttp_ - * #include - * - * Regardless of the method chosen, the end result is that bt_log symbols will - * be prefixed with "KittyHttp_", so if a user of KittyHttp (say DogeBrowser) - * also uses bt_log for logging, they will not interferer with each other. Both - * will have their own log level, output facility, format options etc. - */ -#ifdef BT_LOG_LIBRARY_PREFIX - #define _BT_LOG_DECOR__(prefix, name) prefix ## name - #define _BT_LOG_DECOR_(prefix, name) _BT_LOG_DECOR__(prefix, name) - #define _BT_LOG_DECOR(name) _BT_LOG_DECOR_(BT_LOG_LIBRARY_PREFIX, name) - - #define bt_log_set_tag_prefix _BT_LOG_DECOR(bt_log_set_tag_prefix) - #define bt_log_set_mem_width _BT_LOG_DECOR(bt_log_set_mem_width) - #define bt_log_set_output_level _BT_LOG_DECOR(bt_log_set_output_level) - #define bt_log_set_output_v _BT_LOG_DECOR(bt_log_set_output_v) - #define bt_log_set_output_p _BT_LOG_DECOR(bt_log_set_output_p) - #define bt_log_out_stderr_callback _BT_LOG_DECOR(bt_log_out_stderr_callback) - #define _bt_log_tag_prefix _BT_LOG_DECOR(_bt_log_tag_prefix) - #define _bt_log_global_format _BT_LOG_DECOR(_bt_log_global_format) - #define _bt_log_global_output _BT_LOG_DECOR(_bt_log_global_output) - #define _bt_log_global_output_lvl _BT_LOG_DECOR(_bt_log_global_output_lvl) - #define _bt_log_write_d _BT_LOG_DECOR(_bt_log_write_d) - #define _bt_log_write_aux_d _BT_LOG_DECOR(_bt_log_write_aux_d) - #define _bt_log_write _BT_LOG_DECOR(_bt_log_write) - #define _bt_log_write_aux _BT_LOG_DECOR(_bt_log_write_aux) - #define _bt_log_write_mem_d _BT_LOG_DECOR(_bt_log_write_mem_d) - #define _bt_log_write_mem_aux_d _BT_LOG_DECOR(_bt_log_write_mem_aux_d) - #define _bt_log_write_mem _BT_LOG_DECOR(_bt_log_write_mem) - #define _bt_log_write_mem_aux _BT_LOG_DECOR(_bt_log_write_mem_aux) - #define _bt_log_stderr_spec _BT_LOG_DECOR(_bt_log_stderr_spec) -#endif - -#if defined(__printflike) - #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ - __printflike(str_index, first_to_check) -#elif defined(__MINGW_PRINTF_FORMAT) - #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ - __attribute__((format(__MINGW_PRINTF_FORMAT, str_index, first_to_check))) -#elif defined(__GNUC__) - #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ - __attribute__((format(__printf__, str_index, first_to_check))) -#else - #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) -#endif - -#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__) - #define _BT_LOG_FUNCTION __FUNCTION__ -#else - #define _BT_LOG_FUNCTION __func__ -#endif - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #define _BT_LOG_INLINE __inline - #define _BT_LOG_IF(cond) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - if(cond) \ - __pragma(warning(pop)) - #define _BT_LOG_WHILE(cond) \ - __pragma(warning(push)) \ - __pragma(warning(disable:4127)) \ - while(cond) \ - __pragma(warning(pop)) -#else - #define _BT_LOG_INLINE inline - #define _BT_LOG_IF(cond) if(cond) - #define _BT_LOG_WHILE(cond) while(cond) -#endif -#define _BT_LOG_NEVER _BT_LOG_IF(0) -#define _BT_LOG_ONCE _BT_LOG_WHILE(0) - -#ifdef __cplusplus -extern "C" { -#endif - -/* Set tag prefix. Prefix will be separated from the tag with dot ('.'). - * Use 0 or empty string to disable (default). Common use is to set it to - * the process (or build target) name (e.g. to separate client and server - * processes). Function will NOT copy provided prefix string, but will store the - * pointer. Hence specified prefix string must remain valid. See - * BT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() function. - * See BT_LOG_TAG for more information about tag and tag prefix. - */ -void bt_log_set_tag_prefix(const char *const prefix); - -/* Set number of bytes per log line in memory (ASCII-HEX) output. Example: - * - * I hello.MAIN 4c6f72656d20697073756d20646f6c6f Lorem ipsum dolo - * |<- w bytes ->| |<- w chars ->| - * - * See BT_LOGF_MEM and BT_LOGF_MEM_AUX for more details. - */ -void bt_log_set_mem_width(const unsigned w); - -/* Set "output" log level. See BT_LOG_LEVEL and BT_LOG_OUTPUT_LEVEL for more - * info about log levels. - */ -void bt_log_set_output_level(const int lvl); - -/* Put mask is a set of flags that define what fields will be added to each - * log message. Default value is BT_LOG_PUT_STD and other flags could be used to - * alter its behavior. See bt_log_set_output_v() for more details. - * - * Note about BT_LOG_PUT_SRC: it will be added only in debug builds (NDEBUG is - * not defined). - */ -enum -{ - BT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */ - BT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */ - BT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */ - BT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */ - BT_LOG_PUT_STD = 0xffff, /* everything (default) */ -}; - -typedef struct bt_log_message -{ - int lvl; /* Log level of the message */ - const char *tag; /* Associated tag (without tag prefix) */ - char *buf; /* Buffer start */ - char *e; /* Buffer end (last position where EOL with 0 could be written) */ - char *p; /* Buffer content end (append position) */ - char *tag_b; /* Prefixed tag start */ - char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */ - char *msg_b; /* Message start (expanded format string) */ -} -bt_log_message; - -/* Type of output callback function. It will be called for each log line allowed - * by both "current" and "output" log levels ("enabled" and "turned on"). - * Callback function is allowed to modify content of the buffers pointed by the - * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg - * is UTF-8 encoded (no BOM mark). - */ -typedef void (*bt_log_output_cb)(const bt_log_message *msg, void *arg); - -/* Format options. For more details see bt_log_set_mem_width(). - */ -typedef struct bt_log_format -{ - unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */ -} -bt_log_format; - -/* Output facility. - */ -typedef struct bt_log_output -{ - unsigned mask; /* What to put into log line buffer (see BT_LOG_PUT_XXX) */ - void *arg; /* User provided output callback argument */ - bt_log_output_cb callback; /* Output callback function */ -} -bt_log_output; - -/* Set output callback function. - * - * Mask allows to control what information will be added to the log line buffer - * before callback function is invoked. Default mask value is BT_LOG_PUT_STD. - */ -void bt_log_set_output_v(const unsigned mask, void *const arg, - const bt_log_output_cb callback); -static _BT_LOG_INLINE void bt_log_set_output_p(const bt_log_output *const output) -{ - bt_log_set_output_v(output->mask, output->arg, output->callback); -} - -/* Used with _AUX macros and allows to override global format and output - * facility. Use BT_LOG_GLOBAL_FORMAT and BT_LOG_GLOBAL_OUTPUT for values from - * global configuration. Example: - * - * static const bt_log_output module_output = { - * BT_LOG_PUT_STD, 0, custom_output_callback - * }; - * static const bt_log_spec module_spec = { - * BT_LOG_GLOBAL_FORMAT, &module_output - * }; - * BT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y); - * - * See BT_LOGF_AUX and BT_LOGF_MEM_AUX for details. - */ -typedef struct bt_log_spec -{ - const bt_log_format *format; - const bt_log_output *output; -} -bt_log_spec; - -#ifdef __cplusplus -} -#endif - -/* Execute log statement if condition is true. Example: - * - * BT_LOG_IF(1 < 2, BT_LOGI("Log this")); - * BT_LOG_IF(1 > 2, BT_LOGI("Don't log this")); - * - * Keep in mind though, that if condition can't be evaluated at compile time, - * then it will be evaluated at run time. This will increase exectuable size - * and can have noticeable performance overhead. Try to limit conditions to - * expressions that can be evaluated at compile time. - */ -#define BT_LOG_IF(cond, f) do { _BT_LOG_IF((cond)) { f; } } _BT_LOG_ONCE - -/* Mark log statement as "secret". Log statements that are marked as secrets - * will NOT be executed when censoring is enabled (see BT_LOG_CENSORED). - * Example: - * - * BT_LOG_SECRET(BT_LOGI("Credit card: %s", credit_card)); - * BT_LOG_SECRET(BT_LOGD_MEM(cipher, cipher_sz, "Cipher bytes:")); - */ -#define BT_LOG_SECRET(f) BT_LOG_IF(BT_LOG_SECRETS, f) - -/* Check "current" log level at compile time (ignoring "output" log level). - * Evaluates to true when specified log level is enabled. For example: - * - * #if BT_LOG_ENABLED_DEBUG - * const char *const g_enum_strings[] = { - * "enum_value_0", "enum_value_1", "enum_value_2" - * }; - * #endif - * // ... - * #if BT_LOG_ENABLED_DEBUG - * BT_LOGD("enum value: %s", g_enum_strings[v]); - * #endif - * - * See BT_LOG_LEVEL for details. - */ -#define BT_LOG_ENABLED(lvl) ((lvl) >= _BT_LOG_LEVEL) -#define BT_LOG_ENABLED_VERBOSE BT_LOG_ENABLED(BT_LOG_VERBOSE) -#define BT_LOG_ENABLED_DEBUG BT_LOG_ENABLED(BT_LOG_DEBUG) -#define BT_LOG_ENABLED_INFO BT_LOG_ENABLED(BT_LOG_INFO) -#define BT_LOG_ENABLED_WARN BT_LOG_ENABLED(BT_LOG_WARN) -#define BT_LOG_ENABLED_ERROR BT_LOG_ENABLED(BT_LOG_ERROR) -#define BT_LOG_ENABLED_FATAL BT_LOG_ENABLED(BT_LOG_FATAL) - -/* Check "output" log level at run time (taking into account "current" log - * level as well). Evaluates to true when specified log level is turned on AND - * enabled. For example: - * - * if (BT_LOG_ON_DEBUG) - * { - * char hash[65]; - * sha256(data_ptr, data_sz, hash); - * BT_LOGD("data: len=%u, sha256=%s", data_sz, hash); - * } - * - * See BT_LOG_OUTPUT_LEVEL for details. - */ -#define BT_LOG_ON(lvl) \ - (BT_LOG_ENABLED((lvl)) && (lvl) >= _BT_LOG_OUTPUT_LEVEL) -#define BT_LOG_ON_VERBOSE BT_LOG_ON(BT_LOG_VERBOSE) -#define BT_LOG_ON_DEBUG BT_LOG_ON(BT_LOG_DEBUG) -#define BT_LOG_ON_INFO BT_LOG_ON(BT_LOG_INFO) -#define BT_LOG_ON_WARN BT_LOG_ON(BT_LOG_WARN) -#define BT_LOG_ON_ERROR BT_LOG_ON(BT_LOG_ERROR) -#define BT_LOG_ON_FATAL BT_LOG_ON(BT_LOG_FATAL) - -#ifdef __cplusplus -extern "C" { -#endif - -extern const char *_bt_log_tag_prefix; -extern bt_log_format _bt_log_global_format; -extern bt_log_output _bt_log_global_output; -extern int _bt_log_global_output_lvl; -extern const bt_log_spec _bt_log_stderr_spec; - -BT_HIDDEN -void _bt_log_write_d( - const char *const func, const char *const file, const unsigned line, - const int lvl, const char *const tag, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7); - -BT_HIDDEN -void _bt_log_write_aux_d( - const char *const func, const char *const file, const unsigned line, - const bt_log_spec *const log, const int lvl, const char *const tag, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(7, 8); - -BT_HIDDEN -void _bt_log_write( - const int lvl, const char *const tag, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(3, 4); - -BT_HIDDEN -void _bt_log_write_aux( - const bt_log_spec *const log, const int lvl, const char *const tag, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(4, 5); - -BT_HIDDEN -void _bt_log_write_mem_d( - const char *const func, const char *const file, const unsigned line, - const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(8, 9); - -BT_HIDDEN -void _bt_log_write_mem_aux_d( - const char *const func, const char *const file, const unsigned line, - const bt_log_spec *const log, const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(9, 10); - -BT_HIDDEN -void _bt_log_write_mem( - const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(5, 6); - -BT_HIDDEN -void _bt_log_write_mem_aux( - const bt_log_spec *const log, const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7); - -#ifdef __cplusplus -} -#endif - -/* Message logging macros: - * - BT_LOGV("format string", args, ...) - * - BT_LOGD("format string", args, ...) - * - BT_LOGI("format string", args, ...) - * - BT_LOGW("format string", args, ...) - * - BT_LOGE("format string", args, ...) - * - BT_LOGF("format string", args, ...) - * - * Message and error string (errno) logging macros: - * - BT_LOGV_ERRNO("initial message", "format string", args, ...) - * - BT_LOGD_ERRNO("initial message", "format string", args, ...) - * - BT_LOGI_ERRNO("initial message", "format string", args, ...) - * - BT_LOGW_ERRNO("initial message", "format string", args, ...) - * - BT_LOGE_ERRNO("initial message", "format string", args, ...) - * - BT_LOGF_ERRNO("initial message", "format string", args, ...) - * - * Memory logging macros: - * - BT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...) - * - BT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...) - * - BT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...) - * - BT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...) - * - BT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...) - * - BT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...) - * - * Auxiliary logging macros: - * - BT_LOGV_AUX(&log_instance, "format string", args, ...) - * - BT_LOGD_AUX(&log_instance, "format string", args, ...) - * - BT_LOGI_AUX(&log_instance, "format string", args, ...) - * - BT_LOGW_AUX(&log_instance, "format string", args, ...) - * - BT_LOGE_AUX(&log_instance, "format string", args, ...) - * - BT_LOGF_AUX(&log_instance, "format string", args, ...) - * - * Auxiliary memory logging macros: - * - BT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - BT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - BT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - BT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - BT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - BT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) - * - * Preformatted string logging macros: - * - BT_LOGV_STR("preformatted string"); - * - BT_LOGD_STR("preformatted string"); - * - BT_LOGI_STR("preformatted string"); - * - BT_LOGW_STR("preformatted string"); - * - BT_LOGE_STR("preformatted string"); - * - BT_LOGF_STR("preformatted string"); - * - * Explicit log level and tag macros: - * - BT_LOG_WRITE(level, tag, "format string", args, ...) - * - BT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", args, ...) - * - BT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, ...) - * - BT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz, - * "format string", args, ...) - * - * Format string follows printf() conventions. Both data_ptr and data_sz could - * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments - * match format specifiers in format string. - * - * Library assuming UTF-8 encoding for all strings (char *), including format - * string itself. - */ -#if BT_LOG_SRCLOC_NONE == _BT_LOG_SRCLOC - #define BT_LOG_WRITE(lvl, tag, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write(lvl, tag, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_aux(log, lvl, tag, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \ - } _BT_LOG_ONCE -#else - #define BT_LOG_WRITE(lvl, tag, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ - lvl, tag, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_mem_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ - lvl, tag, d, d_sz, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ - log, lvl, tag, __VA_ARGS__); \ - } _BT_LOG_ONCE - #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ - do { \ - if (BT_LOG_ON(lvl)) \ - _bt_log_write_mem_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ - log, lvl, tag, d, d_sz, __VA_ARGS__); \ - } _BT_LOG_ONCE -#endif - -#define BT_LOG_WRITE_ERRNO(lvl, tag, _msg, _fmt, args...) \ - do { \ - const char *error_str; \ - error_str = g_strerror(errno); \ - BT_LOG_WRITE(lvl, tag, _msg ": %s" _fmt, error_str, ## args); \ - } _BT_LOG_ONCE - -static _BT_LOG_INLINE void _bt_log_unused(const int dummy, ...) {(void)dummy;} - -#define _BT_LOG_UNUSED(...) \ - do { _BT_LOG_NEVER _bt_log_unused(0, __VA_ARGS__); } _BT_LOG_ONCE - -#if BT_LOG_ENABLED_VERBOSE - #define BT_LOGV(...) \ - BT_LOG_WRITE(BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGV_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGV_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGV_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGV_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM(log, BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGV(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGV_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGV_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGV_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#if BT_LOG_ENABLED_DEBUG - #define BT_LOGD(...) \ - BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGD_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGD_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGD_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGD_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGD(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGD_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGD_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGD_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#if BT_LOG_ENABLED_INFO - #define BT_LOGI(...) \ - BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGI_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGI_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGI_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGI_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGI(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGI_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGI_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGI_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#if BT_LOG_ENABLED_WARN - #define BT_LOGW(...) \ - BT_LOG_WRITE(BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGW_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGW_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGW_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGW_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGW(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGW_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGW_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGW_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#if BT_LOG_ENABLED_ERROR - #define BT_LOGE(...) \ - BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGE_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGE_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGE_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGE_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGE(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGE_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGE_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGE_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#if BT_LOG_ENABLED_FATAL - #define BT_LOGF(...) \ - BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGF_ERRNO(...) \ - BT_LOG_WRITE_ERRNO(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGF_AUX(log, ...) \ - BT_LOG_WRITE_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) - #define BT_LOGF_MEM(d, d_sz, ...) \ - BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) - #define BT_LOGF_MEM_AUX(log, d, d_sz, ...) \ - BT_LOG_WRITE_MEM_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) -#else - #define BT_LOGF(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGF_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGF_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) - #define BT_LOGF_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) -#endif - -#define BT_LOGV_STR(s) BT_LOGV("%s", (s)) -#define BT_LOGD_STR(s) BT_LOGD("%s", (s)) -#define BT_LOGI_STR(s) BT_LOGI("%s", (s)) -#define BT_LOGW_STR(s) BT_LOGW("%s", (s)) -#define BT_LOGE_STR(s) BT_LOGE("%s", (s)) -#define BT_LOGF_STR(s) BT_LOGF("%s", (s)) - -#ifdef __cplusplus -extern "C" { -#endif - -/* Output to standard error stream. Library uses it by default, though in few - * cases it could be necessary to specify it explicitly. For example, when - * bt_log library is compiled with BT_LOG_EXTERN_GLOBAL_OUTPUT, application must - * define and initialize global output variable: - * - * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR}; - * - * Another example is when using custom output, stderr could be used as a - * fallback when custom output facility failed to initialize: - * - * bt_log_set_output_v(BT_LOG_OUT_STDERR); - */ -enum { BT_LOG_OUT_STDERR_MASK = BT_LOG_PUT_STD }; - -BT_HIDDEN -void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg); -#define BT_LOG_OUT_STDERR BT_LOG_OUT_STDERR_MASK, 0, bt_log_out_stderr_callback - -/* Predefined spec for stderr. Uses global format options (BT_LOG_GLOBAL_FORMAT) - * and BT_LOG_OUT_STDERR. Could be used to force output to stderr for a - * particular message. Example: - * - * f = fopen("foo.log", "w"); - * if (!f) - * BT_LOGE_AUX(BT_LOG_STDERR, "Failed to open log file"); - */ -#define BT_LOG_STDERR (&_bt_log_stderr_spec) - -static inline -int bt_log_get_level_from_env(const char *var) -{ - const char *varval = getenv(var); - int level = BT_LOG_NONE; - - if (!varval) { - goto end; - } - - if (strcmp(varval, "VERBOSE") == 0 || - strcmp(varval, "V") == 0) { - level = BT_LOG_VERBOSE; - } else if (strcmp(varval, "DEBUG") == 0 || - strcmp(varval, "D") == 0) { - level = BT_LOG_DEBUG; - } else if (strcmp(varval, "INFO") == 0 || - strcmp(varval, "I") == 0) { - level = BT_LOG_INFO; - } else if (strcmp(varval, "WARN") == 0 || - strcmp(varval, "WARNING") == 0 || - strcmp(varval, "W") == 0) { - level = BT_LOG_WARN; - } else if (strcmp(varval, "ERROR") == 0 || - strcmp(varval, "E") == 0) { - level = BT_LOG_ERROR; - } else if (strcmp(varval, "FATAL") == 0 || - strcmp(varval, "F") == 0) { - level = BT_LOG_FATAL; - } else if (strcmp(varval, "NONE") == 0 || - strcmp(varval, "N") == 0) { - level = BT_LOG_NONE; - } else { - /* Should we warn here? How? */ - } - -end: - return level; -} - -#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym) \ - extern int _level_sym - -#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var) \ - BT_HIDDEN int _level_sym = BT_LOG_NONE; \ - static \ - void __attribute__((constructor)) _bt_log_level_ctor(void) \ - { \ - _level_sym = bt_log_get_level_from_env(_env_var); \ - } - -#ifdef __cplusplus -} -#endif - -#endif /* BABELTRACE_LOGGING_INTERNAL_H */ diff --git a/include/babeltrace2/mmap-align-internal.h b/include/babeltrace2/mmap-align-internal.h deleted file mode 100644 index 1f99ca6c..00000000 --- a/include/babeltrace2/mmap-align-internal.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef _BABELTRACE_MMAP_ALIGN_H -#define _BABELTRACE_MMAP_ALIGN_H - -/* - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -/* - * This header implements a wrapper over mmap (mmap_align) that memory - * maps a file region that is not necessarily multiple of the page size. - * It returns a structure (instead of a pointer) that contains the mmap - * pointer (page-aligned) and a pointer to the offset requested within - * that page. Note: in the current implementation, the "addr" parameter - * cannot be forced, so we allocate at an address chosen by the OS. - */ - -struct mmap_align { - void *page_aligned_addr; /* mmap address, aligned to floor */ - size_t page_aligned_length; /* mmap length, containing range */ - - void *addr; /* virtual mmap address */ - size_t length; /* virtual mmap length */ -}; - -#ifdef __WIN32__ -#include - -/* - * On windows the memory mapping offset must be aligned to the memory - * allocator allocation granularity and not the page size. - */ -static inline -off_t get_page_aligned_offset(off_t offset, size_t page_size) -{ - SYSTEM_INFO sysinfo; - - GetNativeSystemInfo(&sysinfo); - - return ALIGN_FLOOR(offset, sysinfo.dwAllocationGranularity); -} -#else -static inline -off_t get_page_aligned_offset(off_t offset, size_t page_size) -{ - return ALIGN_FLOOR(offset, page_size); -} -#endif - -static inline -struct mmap_align *mmap_align(size_t length, int prot, - int flags, int fd, off_t offset) -{ - struct mmap_align *mma; - off_t page_aligned_offset; /* mmap offset, aligned to floor */ - size_t page_size; - - page_size = bt_common_get_page_size(); - - mma = malloc(sizeof(*mma)); - if (!mma) - return MAP_FAILED; - mma->length = length; - page_aligned_offset = get_page_aligned_offset(offset, page_size); - /* - * Page aligned length needs to contain the requested range. - * E.g., for a small range that fits within a single page, we might - * require a 2 pages page_aligned_length if the range crosses a page - * boundary. - */ - mma->page_aligned_length = ALIGN(length + offset - page_aligned_offset, page_size); - mma->page_aligned_addr = bt_mmap(NULL, mma->page_aligned_length, - prot, flags, fd, page_aligned_offset); - if (mma->page_aligned_addr == MAP_FAILED) { - free(mma); - return MAP_FAILED; - } - mma->addr = ((uint8_t *) mma->page_aligned_addr) + (offset - page_aligned_offset); - return mma; -} - -static inline -int munmap_align(struct mmap_align *mma) -{ - void *page_aligned_addr; - size_t page_aligned_length; - - page_aligned_addr = mma->page_aligned_addr; - page_aligned_length = mma->page_aligned_length; - free(mma); - return bt_munmap(page_aligned_addr, page_aligned_length); -} - -static inline -void *mmap_align_addr(struct mmap_align *mma) -{ - return mma->addr; -} - -/* - * Helper for special-cases, normally unused. - */ -static inline -void mmap_align_set_addr(struct mmap_align *mma, void *addr) -{ - mma->addr = addr; -} - -#endif /* _BABELTRACE_MMAP_ALIGN_H */ diff --git a/include/babeltrace2/object-internal.h b/include/babeltrace2/object-internal.h deleted file mode 100644 index 9573fb27..00000000 --- a/include/babeltrace2/object-internal.h +++ /dev/null @@ -1,374 +0,0 @@ -#ifndef BABELTRACE_OBJECT_INTERNAL_H -#define BABELTRACE_OBJECT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_object; - -typedef void (*bt_object_release_func)(struct bt_object *); -typedef void (*bt_object_parent_is_owner_listener_func)( - struct bt_object *); - -static inline -void bt_object_get_no_null_check(const void *obj); - -static inline -void bt_object_put_no_null_check(const void *obj); - -/* - * Babeltrace object base. - * - * All objects publicly exposed by Babeltrace APIs must contain this - * object as their first member. - */ -struct bt_object { - /* - * True if this object is shared, that is, it has a reference - * count. - */ - bool is_shared; - - /* - * Current reference count. - */ - unsigned long long ref_count; - - /* - * Release function called when the object's reference count - * falls to zero. For an object with a parent, this function is - * bt_object_with_parent_release_func(), which calls - * `spec_release_func` below if there's no current parent. - */ - bt_object_release_func release_func; - - /* - * Specific release function called by - * bt_object_with_parent_release_func() or directly by a - * parent object. - */ - bt_object_release_func spec_release_func; - - /* - * Optional callback for an object with a parent, called by - * bt_object_with_parent_release_func() to indicate to the - * object that its parent is its owner. - */ - bt_object_parent_is_owner_listener_func - parent_is_owner_listener_func; - - /* - * Optional parent object. - */ - struct bt_object *parent; -}; - -static inline -unsigned long long bt_object_get_ref_count(const struct bt_object *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - return obj->ref_count; -} - -static inline -struct bt_object *bt_object_borrow_parent(const struct bt_object *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - return obj->parent; -} - -static inline -struct bt_object *bt_object_get_parent(const struct bt_object *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - struct bt_object *parent = bt_object_borrow_parent(obj); - - if (parent) { - bt_object_get_no_null_check(parent); - } - - return parent; -} - -static inline -void bt_object_set_parent(struct bt_object *child, struct bt_object *parent) -{ - BT_ASSERT(child); - BT_ASSERT(child->is_shared); - -#ifdef BT_LOGV - BT_LOGV("Setting object's parent: addr=%p, parent-addr=%p", - child, parent); -#endif - - /* - * It is assumed that a "child" having a parent is publicly - * reachable. Therefore, a reference to its parent must be - * taken. The reference to the parent will be released once the - * object's reference count falls to zero. - */ - if (parent) { - BT_ASSERT(!child->parent); - child->parent = parent; - bt_object_get_no_null_check(parent); - } else { - if (child->parent) { - bt_object_put_no_null_check(child->parent); - } - - child->parent = NULL; - } -} - -static inline -void bt_object_try_spec_release(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->spec_release_func); - - if (bt_object_get_ref_count(obj) == 0) { - obj->spec_release_func(obj); - } -} - -static inline -void bt_object_with_parent_release_func(struct bt_object *obj) -{ - if (obj->parent) { - /* - * Keep our own copy of the parent address because `obj` - * could be destroyed in - * obj->parent_is_owner_listener_func(). - */ - struct bt_object *parent = obj->parent; - -#ifdef BT_LOGV - BT_LOGV("Releasing parented object: addr=%p, ref-count=%llu, " - "parent-addr=%p, parent-ref-count=%llu", - obj, obj->ref_count, - parent, parent->ref_count); -#endif - - if (obj->parent_is_owner_listener_func) { - /* - * Object has a chance to destroy itself here - * under certain conditions and notify its - * parent. At this point the parent is - * guaranteed to exist because it's not put yet. - */ - obj->parent_is_owner_listener_func(obj); - } - - /* The release function will be invoked by the parent. */ - bt_object_put_no_null_check(parent); - } else { - bt_object_try_spec_release(obj); - } -} - -static inline -void bt_object_init(struct bt_object *obj, bool is_shared, - bt_object_release_func release_func) -{ - BT_ASSERT(obj); - BT_ASSERT(!is_shared || release_func); - obj->is_shared = is_shared; - obj->release_func = release_func; - obj->parent_is_owner_listener_func = NULL; - obj->spec_release_func = NULL; - obj->parent = NULL; - obj->ref_count = 1; -} - -static inline -void bt_object_init_shared(struct bt_object *obj, - bt_object_release_func release_func) -{ - bt_object_init(obj, true, release_func); -} - -static inline -void bt_object_init_unique(struct bt_object *obj) -{ - bt_object_init(obj, false, NULL); -} - -static inline -void bt_object_init_shared_with_parent(struct bt_object *obj, - bt_object_release_func spec_release_func) -{ - BT_ASSERT(obj); - BT_ASSERT(spec_release_func); - bt_object_init_shared(obj, bt_object_with_parent_release_func); - obj->spec_release_func = spec_release_func; -} - -static inline -void bt_object_set_parent_is_owner_listener_func(struct bt_object *obj, - bt_object_parent_is_owner_listener_func func) -{ - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->spec_release_func); - ((struct bt_object *) obj)->parent_is_owner_listener_func = func; -} - -static inline -void bt_object_inc_ref_count(const struct bt_object *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - obj->ref_count++; - BT_ASSERT(obj->ref_count != 0); -} - -static inline -void bt_object_get_no_null_check_no_parent_check(const struct bt_object *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - -#ifdef BT_LOGV - BT_LOGV("Incrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count + 1, - obj, obj->ref_count, obj->ref_count + 1); -#endif - - bt_object_inc_ref_count(obj); -} - -static inline -void bt_object_get_no_null_check(const void *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - - if (unlikely(obj->parent && bt_object_get_ref_count(obj) == 0)) { -#ifdef BT_LOGV - BT_LOGV("Incrementing object's parent's reference count: " - "addr=%p, parent-addr=%p", obj, obj->parent); -#endif - - bt_object_get_no_null_check(obj->parent); - } - -#ifdef BT_LOGV - BT_LOGV("Incrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count + 1, - obj, obj->ref_count, obj->ref_count + 1); -#endif - - bt_object_inc_ref_count(obj); -} - -static inline -void bt_object_put_no_null_check(const void *c_obj) -{ - struct bt_object *obj = (void *) c_obj; - - BT_ASSERT(obj); - BT_ASSERT(obj->is_shared); - BT_ASSERT(obj->ref_count > 0); - -#ifdef BT_LOGV - BT_LOGV("Decrementing object's reference count: %llu -> %llu: " - "addr=%p, cur-count=%llu, new-count=%llu", - obj->ref_count, obj->ref_count - 1, - obj, obj->ref_count, obj->ref_count - 1); -#endif - - obj->ref_count--; - - if (obj->ref_count == 0) { - BT_ASSERT(obj->release_func); - obj->release_func(obj); - } -} - -static inline -void bt_object_get_ref(const void *ptr) -{ - struct bt_object *obj = (void *) ptr; - - if (unlikely(!obj)) { - return; - } - -#ifdef BT_ASSERT_PRE - BT_ASSERT_PRE(obj->is_shared, "Object is not shared: %!+O", obj); -#endif - - bt_object_get_no_null_check(obj); -} - -static inline -void bt_object_put_ref(const void *ptr) -{ - struct bt_object *obj = (void *) ptr; - - if (unlikely(!obj)) { - return; - } - -#ifdef BT_ASSERT_PRE - BT_ASSERT_PRE(obj->is_shared, "Object is not shared: %!+O", obj); - BT_ASSERT_PRE(bt_object_get_ref_count(obj) > 0, - "Decrementing a reference count set to 0: %!+O", ptr); -#endif - - bt_object_put_no_null_check(obj); -} - -#define BT_OBJECT_PUT_REF_AND_RESET(_var) \ - do { \ - bt_object_put_ref(_var); \ - (_var) = NULL; \ - } while (0) - -#define BT_OBJECT_MOVE_REF(_var_dst, _var_src) \ - do { \ - bt_object_put_ref(_var_dst); \ - (_var_dst) = (_var_src); \ - (_var_src) = NULL; \ - } while (0) - -#endif /* BABELTRACE_OBJECT_INTERNAL_H */ diff --git a/include/babeltrace2/object-pool-internal.h b/include/babeltrace2/object-pool-internal.h deleted file mode 100644 index 5a99f962..00000000 --- a/include/babeltrace2/object-pool-internal.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef BABELTRACE_OBJECT_POOL_INTERNAL_H -#define BABELTRACE_OBJECT_POOL_INTERNAL_H - -/* - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * This is a generic object pool to avoid memory allocation/deallocation - * for objects of which the lifespan is typically short, but which are - * created a lot. - * - * The object pool, thanks to two user functions, knows how to allocate - * a brand new object in memory when the pool is empty and how to - * destroy an object when we destroy the pool. - * - * The object pool's user is responsible for: - * - * * Setting whatever references the object needs to keep and reset some - * properties _after_ calling bt_object_pool_create_object(). This is - * typically done in the bt_*_create() function which calls - * bt_object_pool_create_object() (which could call the user-provided - * allocation function if the pool is empty) and then sets the - * appropriate properties on the possibly recycled object. - * - * * Releasing whatever references the object keeps _before_ calling - * bt_object_pool_recycle_object(). This is typically done in a custom - * bt_*_recycle() function which does the necessary before calling - * bt_object_pool_recycle_object() with an object ready to be reused - * at any time. - */ - -#include -#include - -typedef void *(*bt_object_pool_new_object_func)(void *data); -typedef void *(*bt_object_pool_destroy_object_func)(void *obj, void *data); - -struct bt_object_pool { - /* - * Container of recycled objects, owned by this. The array's size - * is the pool's capacity. - */ - GPtrArray *objects; - - /* - * Pool's size, that is, number of elements in the array above, - * starting at index 0, which exist as recycled objects. - */ - size_t size; - - /* User functions */ - struct { - /* Allocate a new object in memory */ - bt_object_pool_new_object_func new_object; - - /* Free direct and indirect memory occupied by object */ - bt_object_pool_destroy_object_func destroy_object; - } funcs; - - /* User data passed to user functions */ - void *data; -}; - -/* - * Initializes an object pool which is already allocated. - */ -int bt_object_pool_initialize(struct bt_object_pool *pool, - bt_object_pool_new_object_func new_object_func, - bt_object_pool_destroy_object_func destroy_object_func, - void *data); - -/* - * Finalizes an object pool without deallocating it. - */ -void bt_object_pool_finalize(struct bt_object_pool *pool); - -/* - * Creates an object from an object pool. If the pool is empty, this - * function calls the "new" user function to allocate a new object - * before returning it. Otherwise this function returns a recycled - * object, removing it from the pool. - * - * The returned object is owned by the caller. - */ -static inline -void *bt_object_pool_create_object(struct bt_object_pool *pool) -{ - struct bt_object *obj; - - BT_ASSERT(pool); - -#ifdef BT_LOGV - BT_LOGV("Creating object from pool: pool-addr=%p, pool-size=%zu, pool-cap=%u", - pool, pool->size, pool->objects->len); -#endif - - if (pool->size > 0) { - /* Pick one from the pool */ - pool->size--; - obj = pool->objects->pdata[pool->size]; - pool->objects->pdata[pool->size] = NULL; - goto end; - } - - /* Pool is empty: create a brand new object */ -#ifdef BT_LOGV - BT_LOGV("Pool is empty: allocating new object: pool-addr=%p", - pool); -#endif - - obj = pool->funcs.new_object(pool->data); - -end: -#ifdef BT_LOGV - BT_LOGV("Created one object from pool: pool-addr=%p, obj-addr=%p", - pool, obj); -#endif - - return obj; -} - -/* - * Recycles an object, that is, puts it back into the pool. - * - * The pool becomes the sole owner of the object to recycle. - */ -static inline -void bt_object_pool_recycle_object(struct bt_object_pool *pool, void *obj) -{ - struct bt_object *bt_obj = obj; - - BT_ASSERT(pool); - BT_ASSERT(obj); - -#ifdef BT_LOGV - BT_LOGV("Recycling object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", - pool, pool->size, pool->objects->len, obj); -#endif - - if (pool->size == pool->objects->len) { - /* Backing array is full: make place for recycled object */ -#ifdef BT_LOGV - BT_LOGV("Object pool is full: increasing object pool capacity: " - "pool-addr=%p, old-pool-cap=%u, new-pool-cap=%u", - pool, pool->objects->len, pool->objects->len + 1); -#endif - g_ptr_array_set_size(pool->objects, pool->size + 1); - } - - /* Reset reference count to 1 since it could be 0 now */ - bt_obj->ref_count = 1; - - /* Back to the pool */ - pool->objects->pdata[pool->size] = obj; - pool->size++; - -#ifdef BT_LOGV - BT_LOGV("Recycled object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", - pool, pool->size, pool->objects->len, obj); -#endif -} - -#endif /* BABELTRACE_OBJECT_POOL_INTERNAL_H */ diff --git a/include/babeltrace2/plugin/plugin-internal.h b/include/babeltrace2/plugin/plugin-internal.h deleted file mode 100644 index f2a78fe4..00000000 --- a/include/babeltrace2/plugin/plugin-internal.h +++ /dev/null @@ -1,442 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H -#define BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum bt_plugin_type { - BT_PLUGIN_TYPE_SO = 0, - BT_PLUGIN_TYPE_PYTHON = 1, -}; - -enum bt_plugin_status { - BT_PLUGIN_STATUS_OK = 0, - BT_PLUGIN_STATUS_ERROR = -1, - BT_PLUGIN_STATUS_NOMEM = -12, -}; - -struct bt_plugin { - struct bt_object base; - enum bt_plugin_type type; - - /* Arrays of `struct bt_component_class *` (owned by this) */ - GPtrArray *src_comp_classes; - GPtrArray *flt_comp_classes; - GPtrArray *sink_comp_classes; - - /* Info (owned by this) */ - struct { - GString *path; - GString *name; - GString *author; - GString *license; - GString *description; - struct { - unsigned int major; - unsigned int minor; - unsigned int patch; - GString *extra; - } version; - bool path_set; - bool name_set; - bool author_set; - bool license_set; - bool description_set; - bool version_set; - } info; - - /* Value depends on the specific plugin type */ - void *spec_data; - void (*destroy_spec_data)(struct bt_plugin *); -}; - -struct bt_plugin_set { - struct bt_object base; - - /* Array of struct bt_plugin * */ - GPtrArray *plugins; -}; - -static inline -const char *bt_plugin_status_string(enum bt_plugin_status status) -{ - switch (status) { - case BT_PLUGIN_STATUS_OK: - return "BT_PLUGIN_STATUS_OK"; - case BT_PLUGIN_STATUS_ERROR: - return "BT_PLUGIN_STATUS_ERROR"; - case BT_PLUGIN_STATUS_NOMEM: - return "BT_PLUGIN_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_plugin_type_string(enum bt_plugin_type type) -{ - switch (type) { - case BT_PLUGIN_TYPE_SO: - return "BT_PLUGIN_TYPE_SO"; - case BT_PLUGIN_TYPE_PYTHON: - return "BT_PLUGIN_TYPE_PYTHON"; - default: - return "(unknown)"; - } -} - -static inline -void bt_plugin_destroy(struct bt_object *obj) -{ - struct bt_plugin *plugin; - - BT_ASSERT(obj); - plugin = container_of(obj, struct bt_plugin, base); - BT_LIB_LOGD("Destroying plugin object: %!+l", plugin); - - if (plugin->destroy_spec_data) { - plugin->destroy_spec_data(plugin); - } - - if (plugin->src_comp_classes) { - BT_LOGD_STR("Putting source component classes."); - g_ptr_array_free(plugin->src_comp_classes, TRUE); - plugin->src_comp_classes = NULL; - } - - if (plugin->flt_comp_classes) { - BT_LOGD_STR("Putting filter component classes."); - g_ptr_array_free(plugin->flt_comp_classes, TRUE); - plugin->flt_comp_classes = NULL; - } - - if (plugin->sink_comp_classes) { - BT_LOGD_STR("Putting sink component classes."); - g_ptr_array_free(plugin->sink_comp_classes, TRUE); - plugin->sink_comp_classes = NULL; - } - - if (plugin->info.name) { - g_string_free(plugin->info.name, TRUE); - plugin->info.name = NULL; - } - - if (plugin->info.path) { - g_string_free(plugin->info.path, TRUE); - plugin->info.path = NULL; - } - - if (plugin->info.description) { - g_string_free(plugin->info.description, TRUE); - plugin->info.description = NULL; - } - - if (plugin->info.author) { - g_string_free(plugin->info.author, TRUE); - plugin->info.author = NULL; - } - - if (plugin->info.license) { - g_string_free(plugin->info.license, TRUE); - plugin->info.license = NULL; - } - - if (plugin->info.version.extra) { - g_string_free(plugin->info.version.extra, TRUE); - plugin->info.version.extra = NULL; - } - - g_free(plugin); -} - -static inline -struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type) -{ - struct bt_plugin *plugin = NULL; - - BT_LOGD("Creating empty plugin object: type=%s", - bt_plugin_type_string(type)); - - plugin = g_new0(struct bt_plugin, 1); - if (!plugin) { - BT_LOGE_STR("Failed to allocate one plugin."); - goto error; - } - - bt_object_init_shared(&plugin->base, bt_plugin_destroy); - plugin->type = type; - - /* Create empty arrays of component classes */ - plugin->src_comp_classes = - g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!plugin->src_comp_classes) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - plugin->flt_comp_classes = - g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!plugin->flt_comp_classes) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - plugin->sink_comp_classes = - g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!plugin->sink_comp_classes) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - /* Create empty info */ - plugin->info.name = g_string_new(NULL); - if (!plugin->info.name) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - plugin->info.path = g_string_new(NULL); - if (!plugin->info.path) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - plugin->info.description = g_string_new(NULL); - if (!plugin->info.description) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - plugin->info.author = g_string_new(NULL); - if (!plugin->info.author) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - plugin->info.license = g_string_new(NULL); - if (!plugin->info.license) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - plugin->info.version.extra = g_string_new(NULL); - if (!plugin->info.version.extra) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - BT_LIB_LOGD("Created empty plugin object: %!+l", plugin); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(plugin); - -end: - return plugin; -} - -static inline -void bt_plugin_set_path(struct bt_plugin *plugin, const char *path) -{ - BT_ASSERT(plugin); - BT_ASSERT(path); - g_string_assign(plugin->info.path, path); - plugin->info.path_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's path: %![plugin-]+l, path=\"%s\"", - plugin, path); -} - -static inline -void bt_plugin_set_name(struct bt_plugin *plugin, const char *name) -{ - BT_ASSERT(plugin); - BT_ASSERT(name); - g_string_assign(plugin->info.name, name); - plugin->info.name_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's name: %![plugin-]+l, name=\"%s\"", - plugin, name); -} - -static inline -void bt_plugin_set_description(struct bt_plugin *plugin, - const char *description) -{ - BT_ASSERT(plugin); - BT_ASSERT(description); - g_string_assign(plugin->info.description, description); - plugin->info.description_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's description: %![plugin-]+l", plugin); -} - -static inline -void bt_plugin_set_author(struct bt_plugin *plugin, const char *author) -{ - BT_ASSERT(plugin); - BT_ASSERT(author); - g_string_assign(plugin->info.author, author); - plugin->info.author_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's author: %![plugin-]+l, author=\"%s\"", - plugin, author); -} - -static inline -void bt_plugin_set_license(struct bt_plugin *plugin, const char *license) -{ - BT_ASSERT(plugin); - BT_ASSERT(license); - g_string_assign(plugin->info.license, license); - plugin->info.license_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's path: %![plugin-]+l, license=\"%s\"", - plugin, license); -} - -static inline -void bt_plugin_set_version(struct bt_plugin *plugin, unsigned int major, - unsigned int minor, unsigned int patch, const char *extra) -{ - BT_ASSERT(plugin); - plugin->info.version.major = major; - plugin->info.version.minor = minor; - plugin->info.version.patch = patch; - - if (extra) { - g_string_assign(plugin->info.version.extra, extra); - } - - plugin->info.version_set = BT_TRUE; - BT_LIB_LOGV("Set plugin's version: %![plugin-]+l, " - "major=%u, minor=%u, patch=%u, extra=\"%s\"", - plugin, major, minor, patch, extra); -} - -static inline -enum bt_plugin_status bt_plugin_add_component_class( - struct bt_plugin *plugin, struct bt_component_class *comp_class) -{ - GPtrArray *comp_classes; - - BT_ASSERT(plugin); - BT_ASSERT(comp_class); - - switch (comp_class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - comp_classes = plugin->src_comp_classes; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - comp_classes = plugin->flt_comp_classes; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - comp_classes = plugin->sink_comp_classes; - break; - default: - abort(); - } - - /* Add new component class */ - bt_object_get_ref(comp_class); - g_ptr_array_add(comp_classes, comp_class); - - /* Special case for a shared object plugin */ - if (plugin->type == BT_PLUGIN_TYPE_SO) { - bt_plugin_so_on_add_component_class(plugin, comp_class); - } - - BT_LIB_LOGD("Added component class to plugin: " - "%![plugin-]+l, %![cc-]+C", plugin, comp_class); - return BT_PLUGIN_STATUS_OK; -} - -static -void bt_plugin_set_destroy(struct bt_object *obj) -{ - struct bt_plugin_set *plugin_set = - container_of(obj, struct bt_plugin_set, base); - - if (!plugin_set) { - return; - } - - BT_LOGD("Destroying plugin set: addr=%p", plugin_set); - - if (plugin_set->plugins) { - BT_LOGD_STR("Putting plugins."); - g_ptr_array_free(plugin_set->plugins, TRUE); - } - - g_free(plugin_set); -} - -static inline -struct bt_plugin_set *bt_plugin_set_create(void) -{ - struct bt_plugin_set *plugin_set = g_new0(struct bt_plugin_set, 1); - - if (!plugin_set) { - goto end; - } - - BT_LOGD_STR("Creating empty plugin set."); - bt_object_init_shared(&plugin_set->base, bt_plugin_set_destroy); - - plugin_set->plugins = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!plugin_set->plugins) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - BT_OBJECT_PUT_REF_AND_RESET(plugin_set); - goto end; - } - - BT_LOGD("Created empty plugin set: addr=%p", plugin_set); - -end: - return plugin_set; -} - -static inline -void bt_plugin_set_add_plugin(struct bt_plugin_set *plugin_set, - struct bt_plugin *plugin) -{ - BT_ASSERT(plugin_set); - BT_ASSERT(plugin); - bt_object_get_ref(plugin); - g_ptr_array_add(plugin_set->plugins, plugin); - BT_LIB_LOGV("Added plugin to plugin set: " - "plugin-set-addr=%p, %![plugin-]+l", - plugin_set, plugin); -} - -#endif /* BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H */ diff --git a/include/babeltrace2/plugin/plugin-so-internal.h b/include/babeltrace2/plugin/plugin-so-internal.h deleted file mode 100644 index 094b87ad..00000000 --- a/include/babeltrace2/plugin/plugin-so-internal.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H -#define BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_plugin; -struct bt_component_class; - -struct bt_plugin_so_shared_lib_handle { - struct bt_object base; - GString *path; - GModule *module; - - /* True if initialization function was called */ - bt_bool init_called; - bt_plugin_exit_func exit; -}; - -struct bt_plugin_so_spec_data { - /* Shared lib. handle: owned by this */ - struct bt_plugin_so_shared_lib_handle *shared_lib_handle; - - /* Pointers to plugin's memory: do NOT free */ - const struct __bt_plugin_descriptor *descriptor; - bt_plugin_init_func init; - const struct __bt_plugin_descriptor_version *version; -}; - -BT_HIDDEN -struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path); - -BT_HIDDEN -struct bt_plugin_set *bt_plugin_so_create_all_from_static(void); - -void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin, - struct bt_component_class *comp_class); - -#endif /* BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H */ diff --git a/include/babeltrace2/plugin/python-plugin-provider-internal.h b/include/babeltrace2/plugin/python-plugin-provider-internal.h deleted file mode 100644 index 1d7aa2ce..00000000 --- a/include/babeltrace2/plugin/python-plugin-provider-internal.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H -#define BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -extern -struct bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path); - -#endif /* BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H */ diff --git a/include/babeltrace2/prio-heap-internal.h b/include/babeltrace2/prio-heap-internal.h deleted file mode 100644 index e6c6a1a3..00000000 --- a/include/babeltrace2/prio-heap-internal.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef _BABELTRACE_PRIO_HEAP_H -#define _BABELTRACE_PRIO_HEAP_H - -/* - * Static-sized priority heap containing pointers. Based on CLRS, - * chapter 6. - * - * Copyright 2011 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -struct ptr_heap { - size_t len, alloc_len; - void **ptrs; - int (*gt)(void *a, void *b); -}; - -#ifdef DEBUG_HEAP -void check_heap(const struct ptr_heap *heap); -#else -static inline -void check_heap(const struct ptr_heap *heap) -{ -} -#endif - -/** - * bt_heap_maximum - return the largest element in the heap - * @heap: the heap to be operated on - * - * Returns the largest element in the heap, without performing any modification - * to the heap structure. Returns NULL if the heap is empty. - */ -static inline void *bt_heap_maximum(const struct ptr_heap *heap) -{ - check_heap(heap); - return likely(heap->len) ? heap->ptrs[0] : NULL; -} - -/** - * bt_heap_init - initialize the heap - * @heap: the heap to initialize - * @alloc_len: number of elements initially allocated - * @gt: function to compare the elements - * - * Returns -ENOMEM if out of memory. - */ -extern int bt_heap_init(struct ptr_heap *heap, - size_t alloc_len, - int gt(void *a, void *b)); - -/** - * bt_heap_free - free the heap - * @heap: the heap to free - */ -extern void bt_heap_free(struct ptr_heap *heap); - -/** - * bt_heap_insert - insert an element into the heap - * @heap: the heap to be operated on - * @p: the element to add - * - * Insert an element into the heap. - * - * Returns -ENOMEM if out of memory. - */ -extern int bt_heap_insert(struct ptr_heap *heap, void *p); - -/** - * bt_heap_remove - remove the largest element from the heap - * @heap: the heap to be operated on - * - * Returns the largest element in the heap. It removes this element from the - * heap. Returns NULL if the heap is empty. - */ -extern void *bt_heap_remove(struct ptr_heap *heap); - -/** - * bt_heap_cherrypick - remove a given element from the heap - * @heap: the heap to be operated on - * @p: the element - * - * Remove the given element from the heap. Return the element if present, else - * return NULL. This algorithm has a complexity of O(n), which is higher than - * O(log(n)) provided by the rest of this API. - */ -extern void *bt_heap_cherrypick(struct ptr_heap *heap, void *p); - -/** - * bt_heap_replace_max - replace the the largest element from the heap - * @heap: the heap to be operated on - * @p: the pointer to be inserted as topmost element replacement - * - * Returns the largest element in the heap. It removes this element from the - * heap. The heap is rebalanced only once after the insertion. Returns NULL if - * the heap is empty. - * - * This is the equivalent of calling bt_heap_remove() and then bt_heap_insert(), but - * it only rebalances the heap once. It never allocates memory. - */ -extern void *bt_heap_replace_max(struct ptr_heap *heap, void *p); - -/** - * bt_heap_copy - copy a heap - * @dst: the destination heap (must be allocated) - * @src: the source heap - * - * Returns -ENOMEM if out of memory. - */ -extern int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src); - -#endif /* _BABELTRACE_PRIO_HEAP_H */ diff --git a/include/babeltrace2/property-internal.h b/include/babeltrace2/property-internal.h deleted file mode 100644 index 575bf6ee..00000000 --- a/include/babeltrace2/property-internal.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef BABELTRACE_PROPERTY_INTERNAL_H -#define BABELTRACE_PROPERTY_INTERNAL_H - -/* - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -struct bt_property { - enum bt_property_availability avail; -}; - -struct bt_property_uint { - struct bt_property base; - uint64_t value; -}; - -static inline -void bt_property_uint_set(struct bt_property_uint *prop, uint64_t value) -{ - BT_ASSERT(prop); - prop->base.avail = BT_PROPERTY_AVAILABILITY_AVAILABLE; - prop->value = value; -} - -static inline -void bt_property_uint_init(struct bt_property_uint *prop, - enum bt_property_availability avail, uint64_t value) -{ - BT_ASSERT(prop); - prop->base.avail = avail; - prop->value = value; -} - -#endif /* BABELTRACE_PROPERTY_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/attributes-internal.h b/include/babeltrace2/trace-ir/attributes-internal.h deleted file mode 100644 index 1784b35f..00000000 --- a/include/babeltrace2/trace-ir/attributes-internal.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_ATTRIBUTES_H -#define BABELTRACE_TRACE_IR_ATTRIBUTES_H - -/* - * Copyright (c) 2015-2018 Philippe Proulx - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -BT_HIDDEN -struct bt_value *bt_attributes_create(void); - -BT_HIDDEN -void bt_attributes_destroy(struct bt_value *attr_obj); - -BT_HIDDEN -int64_t bt_attributes_get_count(const struct bt_value *attr_obj); - -BT_HIDDEN -const char *bt_attributes_get_field_name(const struct bt_value *attr_obj, - uint64_t index); - -BT_HIDDEN -struct bt_value *bt_attributes_borrow_field_value( - struct bt_value *attr_obj, - uint64_t index); - -BT_HIDDEN -int bt_attributes_set_field_value(struct bt_value *attr_obj, - const char *name, struct bt_value *value_obj); - -BT_HIDDEN -struct bt_value *bt_attributes_borrow_field_value_by_name( - struct bt_value *attr_obj, const char *name); - -BT_HIDDEN -int bt_attributes_freeze(const struct bt_value *attr_obj); - -#ifdef __cplusplus -} -#endif - -#endif /* BABELTRACE_TRACE_IR_ATTRIBUTES_H */ diff --git a/include/babeltrace2/trace-ir/clock-class-internal.h b/include/babeltrace2/trace-ir/clock-class-internal.h deleted file mode 100644 index 7b748305..00000000 --- a/include/babeltrace2/trace-ir/clock-class-internal.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H -#define BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_clock_class { - struct bt_object base; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } description; - - uint64_t frequency; - uint64_t precision; - int64_t offset_seconds; - uint64_t offset_cycles; - - struct { - uint8_t uuid[BABELTRACE_UUID_LEN]; - - /* NULL or `uuid` above */ - bt_uuid value; - } uuid; - - bool origin_is_unix_epoch; - - /* - * This is computed every time you call - * bt_clock_class_set_frequency() or - * bt_clock_class_set_offset(), as well as initially. It is the - * base offset in nanoseconds including both `offset_seconds` - * and `offset_cycles` above in the result. It is used to - * accelerate future calls to - * bt_clock_snapshot_get_ns_from_origin() and - * bt_clock_class_cycles_to_ns_from_origin(). - * - * `overflows` is true if the base offset cannot be computed - * because of an overflow. - */ - struct { - int64_t value_ns; - bool overflows; - } base_offset; - - /* Pool of `struct bt_clock_snapshot *` */ - struct bt_object_pool cs_pool; - - bool frozen; -}; - -BT_HIDDEN -void _bt_clock_class_freeze(const struct bt_clock_class *clock_class); - -#ifdef BT_DEV_MODE -# define bt_clock_class_freeze _bt_clock_class_freeze -#else -# define bt_clock_class_freeze(_cc) -#endif - -BT_HIDDEN -bt_bool bt_clock_class_is_valid(struct bt_clock_class *clock_class); - -static inline -int bt_clock_class_clock_value_from_ns_from_origin( - struct bt_clock_class *cc, int64_t ns_from_origin, - uint64_t *raw_value) -{ - BT_ASSERT(cc); - - return bt_common_clock_value_from_ns_from_origin(cc->offset_seconds, - cc->offset_cycles, cc->frequency, ns_from_origin, - raw_value); -} - -#endif /* BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/clock-snapshot-internal.h b/include/babeltrace2/trace-ir/clock-snapshot-internal.h deleted file mode 100644 index 049d7fdd..00000000 --- a/include/babeltrace2/trace-ir/clock-snapshot-internal.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H -#define BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -struct bt_clock_class; - -struct bt_clock_snapshot { - struct bt_object base; - struct bt_clock_class *clock_class; - uint64_t value_cycles; - bool ns_from_origin_overflows; - int64_t ns_from_origin; - bool is_set; -}; - -static inline -void bt_clock_snapshot_set(struct bt_clock_snapshot *clock_snapshot) -{ - BT_ASSERT(clock_snapshot); - clock_snapshot->is_set = true; -} - -static inline -void bt_clock_snapshot_reset(struct bt_clock_snapshot *clock_snapshot) -{ - BT_ASSERT(clock_snapshot); - clock_snapshot->is_set = false; -} - -static inline -void set_ns_from_origin(struct bt_clock_snapshot *clock_snapshot) -{ - if (bt_util_ns_from_origin_clock_class(clock_snapshot->clock_class, - clock_snapshot->value_cycles, - &clock_snapshot->ns_from_origin)) { - clock_snapshot->ns_from_origin_overflows = true; - } -} - -static inline -void bt_clock_snapshot_set_raw_value(struct bt_clock_snapshot *clock_snapshot, - uint64_t cycles) -{ - BT_ASSERT(clock_snapshot); - clock_snapshot->value_cycles = cycles; - set_ns_from_origin(clock_snapshot); - bt_clock_snapshot_set(clock_snapshot); -} - -BT_HIDDEN -void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot); - -BT_HIDDEN -struct bt_clock_snapshot *bt_clock_snapshot_new(struct bt_clock_class *clock_class); - -BT_HIDDEN -struct bt_clock_snapshot *bt_clock_snapshot_create( - struct bt_clock_class *clock_class); - -BT_HIDDEN -void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot); - -#endif /* BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/clock-snapshot-set-internal.h b/include/babeltrace2/trace-ir/clock-snapshot-set-internal.h deleted file mode 100644 index 59e51b82..00000000 --- a/include/babeltrace2/trace-ir/clock-snapshot-set-internal.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H -#define BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H - -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_clock_snapshot_set { - /* Unique objects owned by this */ - GPtrArray *clock_snapshots; - - /* Weak; points to one of the clock snapshots above */ - struct bt_clock_snapshot *default_cs; -}; - -static inline -int bt_clock_snapshot_set_initialize(struct bt_clock_snapshot_set *cs_set) -{ - int ret = 0; - - cs_set->clock_snapshots = g_ptr_array_sized_new(1); - if (!cs_set->clock_snapshots) { -#ifdef BT_LOGE_STR - BT_LOGE_STR("Failed to allocate one GPtrArray."); -#endif - - ret = -1; - goto end; - } - - cs_set->default_cs = NULL; - -end: - return ret; -} - -static inline -void bt_clock_snapshot_set_reset(struct bt_clock_snapshot_set *cs_set) -{ - uint64_t i; - - BT_ASSERT(cs_set); - BT_ASSERT(cs_set->clock_snapshots); - - for (i = 0; i < cs_set->clock_snapshots->len; i++) { - struct bt_clock_snapshot *cs = cs_set->clock_snapshots->pdata[i]; - - BT_ASSERT(cs); - bt_clock_snapshot_reset(cs); - } - - cs_set->default_cs = NULL; -} - -static inline -void bt_clock_snapshot_set_finalize(struct bt_clock_snapshot_set *cs_set) -{ - uint64_t i; - - BT_ASSERT(cs_set); - - if (cs_set->clock_snapshots) { - for (i = 0; i < cs_set->clock_snapshots->len; i++) { - struct bt_clock_snapshot *cs = - cs_set->clock_snapshots->pdata[i]; - - BT_ASSERT(cs); - bt_clock_snapshot_recycle(cs); - } - - g_ptr_array_free(cs_set->clock_snapshots, TRUE); - } - - cs_set->default_cs = NULL; -} - -static inline -int bt_clock_snapshot_set_set_clock_snapshot(struct bt_clock_snapshot_set *cs_set, - struct bt_clock_class *cc, uint64_t raw_value) -{ - int ret = 0; - struct bt_clock_snapshot *clock_snapshot = NULL; - uint64_t i; - - BT_ASSERT(cs_set); - BT_ASSERT(cc); - - /* - * Check if we already have a value for this clock class. - * - * TODO: When we have many clock classes, make this more - * efficient. - */ - for (i = 0; i < cs_set->clock_snapshots->len; i++) { - struct bt_clock_snapshot *cs = cs_set->clock_snapshots->pdata[i]; - - BT_ASSERT(cs); - - if (cs->clock_class == cc) { - clock_snapshot = cs; - break; - } - } - - if (!clock_snapshot) { - clock_snapshot = bt_clock_snapshot_create(cc); - if (!clock_snapshot) { -#ifdef BT_LIB_LOGE - BT_LIB_LOGE("Cannot create a clock snapshot from a clock class: " - "%![cc-]+K", cc); -#endif - - ret = -1; - goto end; - } - - g_ptr_array_add(cs_set->clock_snapshots, clock_snapshot); - } - - bt_clock_snapshot_set_raw_value(clock_snapshot, raw_value); - -end: - return ret; -} - -static inline -void bt_clock_snapshot_set_set_default_clock_snapshot( - struct bt_clock_snapshot_set *cs_set, uint64_t raw_value) -{ - BT_ASSERT(cs_set); - BT_ASSERT(cs_set->default_cs); - bt_clock_snapshot_set_raw_value(cs_set->default_cs, raw_value); -} - -#endif /* BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H */ diff --git a/include/babeltrace2/trace-ir/event-class-internal.h b/include/babeltrace2/trace-ir/event-class-internal.h deleted file mode 100644 index 5d673a93..00000000 --- a/include/babeltrace2/trace-ir/event-class-internal.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H -#define BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_event_class { - struct bt_object base; - struct bt_field_class *specific_context_fc; - struct bt_field_class *payload_fc; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - uint64_t id; - struct bt_property_uint log_level; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } emf_uri; - - /* Pool of `struct bt_event *` */ - struct bt_object_pool event_pool; - - bool frozen; -}; - -BT_HIDDEN -void _bt_event_class_freeze(const struct bt_event_class *event_class); - -#ifdef BT_DEV_MODE -# define bt_event_class_freeze _bt_event_class_freeze -#else -# define bt_event_class_freeze(_ec) -#endif - -static inline -struct bt_stream_class *bt_event_class_borrow_stream_class_inline( - const struct bt_event_class *event_class) -{ - BT_ASSERT(event_class); - return (void *) bt_object_borrow_parent(&event_class->base); -} - -#endif /* BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/event-internal.h b/include/babeltrace2/trace-ir/event-internal.h deleted file mode 100644 index 46533e86..00000000 --- a/include/babeltrace2/trace-ir/event-internal.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_EVENT_INTERNAL_H -#define BABELTRACE_TRACE_IR_EVENT_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Protection: this file uses BT_LIB_LOG*() macros directly */ -#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H -# error Please define include before including this file. -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_EVENT_HOT(_event) \ - BT_ASSERT_PRE_HOT(((const struct bt_event *) (_event)), \ - "Event", ": %!+e", (_event)) - -struct bt_event { - struct bt_object base; - - /* Owned by this */ - struct bt_event_class *class; - - /* Owned by this */ - struct bt_packet *packet; - - struct bt_field *common_context_field; - struct bt_field *specific_context_field; - struct bt_field *payload_field; - bool frozen; -}; - -BT_HIDDEN -void bt_event_destroy(struct bt_event *event); - -BT_HIDDEN -struct bt_event *bt_event_new(struct bt_event_class *event_class); - -BT_HIDDEN -void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen); - -#ifdef BT_DEV_MODE -# define bt_event_set_is_frozen _bt_event_set_is_frozen -#else -# define bt_event_set_is_frozen(_event, _is_frozen) -#endif - -BT_UNUSED -static inline -void _bt_event_reset_dev_mode(struct bt_event *event) -{ - BT_ASSERT(event); - - if (event->common_context_field) { - bt_field_set_is_frozen( - event->common_context_field, false); - bt_field_reset( - event->common_context_field); - } - - if (event->specific_context_field) { - bt_field_set_is_frozen( - event->specific_context_field, false); - bt_field_reset(event->specific_context_field); - } - - if (event->payload_field) { - bt_field_set_is_frozen( - event->payload_field, false); - bt_field_reset(event->payload_field); - } -} - -#ifdef BT_DEV_MODE -# define bt_event_reset_dev_mode _bt_event_reset_dev_mode -#else -# define bt_event_reset_dev_mode(_x) -#endif - -static inline -void bt_event_reset(struct bt_event *event) -{ - BT_ASSERT(event); - BT_LIB_LOGD("Resetting event: %!+e", event); - bt_event_set_is_frozen(event, false); - bt_object_put_no_null_check(&event->packet->base); - event->packet = NULL; -} - -static inline -void bt_event_recycle(struct bt_event *event) -{ - struct bt_event_class *event_class; - - BT_ASSERT(event); - BT_LIB_LOGD("Recycling event: %!+e", event); - - /* - * Those are the important ordered steps: - * - * 1. Reset the event object (put any permanent reference it - * has, unfreeze it and its fields in developer mode, etc.), - * but do NOT put its class's reference. This event class - * contains the pool to which we're about to recycle this - * event object, so we must guarantee its existence thanks - * to this existing reference. - * - * 2. Move the event class reference to our `event_class` - * variable so that we can set the event's class member - * to NULL before recycling it. We CANNOT do this after - * we put the event class reference because this bt_object_put_ref() - * could destroy the event class, also destroying its - * event pool, thus also destroying our event object (this - * would result in an invalid write access). - * - * 3. Recycle the event object. - * - * 4. Put our event class reference. - */ - bt_event_reset(event); - event_class = event->class; - BT_ASSERT(event_class); - event->class = NULL; - bt_object_pool_recycle_object(&event_class->event_pool, event); - bt_object_put_no_null_check(&event_class->base); -} - -static inline -void bt_event_set_packet(struct bt_event *event, struct bt_packet *packet) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - BT_ASSERT_PRE_EVENT_HOT(event); - BT_ASSERT_PRE(bt_event_class_borrow_stream_class( - event->class) == packet->stream->class, - "Packet's stream class and event's stream class differ: " - "%![event-]+e, %![packet-]+a", event, packet); - - BT_ASSERT(!event->packet); - event->packet = packet; - bt_object_get_no_null_check_no_parent_check(&event->packet->base); - BT_LIB_LOGV("Set event's packet: %![event-]+e, %![packet-]+a", - event, packet); -} - -static inline -struct bt_event *bt_event_create(struct bt_event_class *event_class, - struct bt_packet *packet) -{ - struct bt_event *event = NULL; - - BT_ASSERT(event_class); - event = bt_object_pool_create_object(&event_class->event_pool); - if (unlikely(!event)) { - BT_LIB_LOGE("Cannot allocate one event from event class's event pool: " - "%![ec-]+E", event_class); - goto end; - } - - if (likely(!event->class)) { - event->class = event_class; - bt_object_get_no_null_check(&event_class->base); - } - - BT_ASSERT(packet); - bt_event_set_packet(event, packet); - goto end; - -end: - return event; -} - -#endif /* BABELTRACE_TRACE_IR_EVENT_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/field-class-internal.h b/include/babeltrace2/trace-ir/field-class-internal.h deleted file mode 100644 index b6ba7291..00000000 --- a/include/babeltrace2/trace-ir/field-class-internal.h +++ /dev/null @@ -1,270 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H -#define BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_FC_IS_INT(_fc, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ - _name " is not an integer field class: %![fc-]+F", (_fc)) - -#define BT_ASSERT_PRE_FC_IS_UNSIGNED_INT(_fc, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, \ - _name " is not an unsigned integer field class: %![fc-]+F", (_fc)) - -#define BT_ASSERT_PRE_FC_IS_ENUM(_fc, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ - _name " is not an enumeration field class: %![fc-]+F", (_fc)) - -#define BT_ASSERT_PRE_FC_IS_ARRAY(_fc, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || \ - ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, \ - _name " is not an array field class: %![fc-]+F", (_fc)) - -#define BT_ASSERT_PRE_FC_HAS_ID(_fc, _type, _name) \ - BT_ASSERT_PRE(((const struct bt_field_class *) (_fc))->type == (_type), \ - _name " has the wrong type: expected-type=%s, " \ - "%![fc-]+F", bt_common_field_class_type_string(_type), (_fc)) - -#define BT_ASSERT_PRE_FC_HOT(_fc, _name) \ - BT_ASSERT_PRE_HOT((const struct bt_field_class *) (_fc), \ - (_name), ": %!+F", (_fc)) - -#define BT_FIELD_CLASS_NAMED_FC_AT_INDEX(_fc, _index) \ - (&g_array_index(((struct bt_field_class_named_field_class_container *) (_fc))->named_fcs, \ - struct bt_named_field_class, (_index))) - -#define BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(_fc, _index) \ - (&g_array_index(((struct bt_field_class_enumeration *) (_fc))->mappings, \ - struct bt_field_class_enumeration_mapping, (_index))) - -#define BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(_mapping, _index) \ - (&g_array_index((_mapping)->ranges, \ - struct bt_field_class_enumeration_mapping_range, (_index))) - -struct bt_field; -struct bt_field_class; - -struct bt_field_class { - struct bt_object base; - enum bt_field_class_type type; - bool frozen; - - /* - * Only used in developer mode, this flag indicates whether or - * not this field class is part of a trace class. - */ - bool part_of_trace_class; -}; - -struct bt_field_class_integer { - struct bt_field_class common; - - /* - * Value range of fields built from this integer field class: - * this is an equivalent integer size in bits. More formally, - * `range` is `n` in: - * - * Unsigned range: [0, 2^n - 1] - * Signed range: [-2^(n - 1), 2^(n - 1) - 1] - */ - uint64_t range; - - enum bt_field_class_integer_preferred_display_base base; -}; - -struct bt_field_class_enumeration_mapping_range { - union { - uint64_t u; - int64_t i; - } lower; - - union { - uint64_t u; - int64_t i; - } upper; -}; - -struct bt_field_class_enumeration_mapping { - GString *label; - - /* Array of `struct bt_field_class_enumeration_mapping_range` */ - GArray *ranges; -}; - -struct bt_field_class_unsigned_enumeration_mapping; -struct bt_field_class_signed_enumeration_mapping; - -struct bt_field_class_enumeration { - struct bt_field_class_integer common; - - /* Array of `struct bt_field_class_enumeration_mapping *` */ - GArray *mappings; - - /* - * This is an array of `const char *` which acts as a temporary - * (potentially growing) buffer for - * bt_field_class_unsigned_enumeration_get_mapping_labels_by_value() - * and - * bt_field_class_signed_enumeration_get_mapping_labels_by_value(). - * - * The actual strings are owned by the mappings above. - */ - GPtrArray *label_buf; -}; - -struct bt_field_class_real { - struct bt_field_class common; - bool is_single_precision; -}; - -struct bt_field_class_string { - struct bt_field_class common; -}; - -/* A named field class is a (name, field class) pair */ -struct bt_named_field_class { - GString *name; - - /* Owned by this */ - struct bt_field_class *fc; - - bool frozen; -}; - -struct bt_field_class_structure_member; -struct bt_field_class_variant_option; - -/* - * This is the base field class for a container of named field classes. - * Structure and variant field classes inherit this. - */ -struct bt_field_class_named_field_class_container { - struct bt_field_class common; - - /* - * Key: `const char *`, not owned by this (owned by named field - * type objects contained in `named_fcs` below). - */ - GHashTable *name_to_index; - - /* Array of `struct bt_named_field_class` */ - GArray *named_fcs; -}; - -struct bt_field_class_structure { - struct bt_field_class_named_field_class_container common; -}; - -struct bt_field_class_array { - struct bt_field_class common; - - /* Owned by this */ - struct bt_field_class *element_fc; -}; - -struct bt_field_class_static_array { - struct bt_field_class_array common; - uint64_t length; -}; - -struct bt_field_class_dynamic_array { - struct bt_field_class_array common; - - /* Weak: never dereferenced, only use to find it elsewhere */ - struct bt_field_class *length_fc; - - /* Owned by this */ - struct bt_field_path *length_field_path; -}; - -struct bt_field_class_variant { - struct bt_field_class_named_field_class_container common; - - /* Weak: never dereferenced, only use to find it elsewhere */ - struct bt_field_class *selector_fc; - - /* Owned by this */ - struct bt_field_path *selector_field_path; -}; - -static inline -bool bt_field_class_has_known_type(const struct bt_field_class *fc) -{ - return fc->type >= BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER && - fc->type <= BT_FIELD_CLASS_TYPE_VARIANT; -} - -BT_HIDDEN -void _bt_field_class_freeze(const struct bt_field_class *field_class); - -#ifdef BT_DEV_MODE -# define bt_field_class_freeze _bt_field_class_freeze -#else -# define bt_field_class_freeze(_fc) ((void) _fc) -#endif - -BT_HIDDEN -void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc); - -#ifdef BT_DEV_MODE -# define bt_named_field_class_freeze _bt_named_field_class_freeze -#else -# define bt_named_field_class_freeze(_named_fc) ((void) _named_fc) -#endif - -/* - * This function recursively marks `field_class` and its children as - * being part of a trace. This is used to validate that all field classes - * are used at a single location within trace objects even if they are - * shared objects for other purposes. - */ -BT_HIDDEN -void _bt_field_class_make_part_of_trace_class( - const struct bt_field_class *field_class); - -#ifdef BT_DEV_MODE -# define bt_field_class_make_part_of_trace_class _bt_field_class_make_part_of_trace_class -#else -# define bt_field_class_make_part_of_trace_class(_fc) ((void) _fc) -#endif - -#endif /* BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/field-internal.h b/include/babeltrace2/trace-ir/field-internal.h deleted file mode 100644 index 3da46b5f..00000000 --- a/include/babeltrace2/trace-ir/field-internal.h +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H -#define BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(_field, _cls_type, _name) \ - BT_ASSERT_PRE(((const struct bt_field *) (_field))->class->type == (_cls_type), \ - _name " has the wrong class type: expected-class-type=%s, " \ - "%![field-]+f", \ - bt_common_field_class_type_string(_cls_type), (_field)) - -#define BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(_field, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, \ - _name " is not an unsigned integer field: %![field-]+f", \ - (_field)) - -#define BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(_field, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ - _name " is not a signed integer field: %![field-]+f", \ - (_field)) - -#define BT_ASSERT_PRE_FIELD_IS_ARRAY(_field, _name) \ - BT_ASSERT_PRE( \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || \ - ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, \ - _name " is not an array field: %![field-]+f", (_field)) - -#define BT_ASSERT_PRE_FIELD_IS_SET(_field, _name) \ - BT_ASSERT_PRE(bt_field_is_set(_field), \ - _name " is not set: %!+f", (_field)) - -#define BT_ASSERT_PRE_FIELD_HOT(_field, _name) \ - BT_ASSERT_PRE_HOT((const struct bt_field *) (_field), (_name), \ - ": %!+f", (_field)) - -struct bt_field; - -typedef struct bt_field *(* bt_field_create_func)(struct bt_field_class *); -typedef void (*bt_field_method_set_is_frozen)(struct bt_field *, bool); -typedef bool (*bt_field_method_is_set)(const struct bt_field *); -typedef void (*bt_field_method_reset)(struct bt_field *); - -struct bt_field_methods { - bt_field_method_set_is_frozen set_is_frozen; - bt_field_method_is_set is_set; - bt_field_method_reset reset; -}; - -struct bt_field { - struct bt_object base; - - /* Owned by this */ - struct bt_field_class *class; - - /* Virtual table for slow path (dev mode) operations */ - struct bt_field_methods *methods; - - bool is_set; - bool frozen; -}; - -struct bt_field_integer { - struct bt_field common; - - union { - uint64_t u; - int64_t i; - } value; -}; - -struct bt_field_real { - struct bt_field common; - double value; -}; - -struct bt_field_structure { - struct bt_field common; - - /* Array of `struct bt_field *`, owned by this */ - GPtrArray *fields; -}; - -struct bt_field_variant { - struct bt_field common; - - /* Weak: belongs to `fields` below */ - struct bt_field *selected_field; - - /* Index of currently selected field */ - uint64_t selected_index; - - /* Array of `struct bt_field *`, owned by this */ - GPtrArray *fields; -}; - -struct bt_field_array { - struct bt_field common; - - /* Array of `struct bt_field *`, owned by this */ - GPtrArray *fields; - - /* Current effective length */ - uint64_t length; -}; - -struct bt_field_string { - struct bt_field common; - GArray *buf; - uint64_t length; -}; - -#ifdef BT_DEV_MODE -# define bt_field_set_is_frozen _bt_field_set_is_frozen -# define bt_field_is_set _bt_field_is_set -# define bt_field_reset _bt_field_reset -# define bt_field_set_single _bt_field_set_single -#else -# define bt_field_set_is_frozen(_field, _is_frozen) -# define bt_field_is_set(_field) (BT_FALSE) -# define bt_field_reset(_field) -# define bt_field_set_single(_field, _val) -#endif - -BT_HIDDEN -void _bt_field_set_is_frozen(const struct bt_field *field, bool is_frozen); - -static inline -void _bt_field_reset(const struct bt_field *field) -{ - BT_ASSERT(field); - BT_ASSERT(field->methods->reset); - field->methods->reset((void *) field); -} - -static inline -void _bt_field_set_single(struct bt_field *field, bool value) -{ - BT_ASSERT(field); - field->is_set = value; -} - -static inline -bt_bool _bt_field_is_set(const struct bt_field *field) -{ - bt_bool is_set = BT_FALSE; - - if (!field) { - goto end; - } - - BT_ASSERT(bt_field_class_has_known_type(field->class)); - BT_ASSERT(field->methods->is_set); - is_set = field->methods->is_set(field); - -end: - return is_set; -} - -BT_HIDDEN -struct bt_field *bt_field_create(struct bt_field_class *class); - -BT_HIDDEN -void bt_field_destroy(struct bt_field *field); - -#endif /* BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/field-path-internal.h b/include/babeltrace2/trace-ir/field-path-internal.h deleted file mode 100644 index ef98835a..00000000 --- a/include/babeltrace2/trace-ir/field-path-internal.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL -#define BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL - -/* - * Copyright 2016-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include - -struct bt_field_path_item { - enum bt_field_path_item_type type; - uint64_t index; -}; - -struct bt_field_path { - struct bt_object base; - enum bt_scope root; - - /* Array of `struct bt_field_path_item` (items) */ - GArray *items; -}; - -BT_HIDDEN -struct bt_field_path *bt_field_path_create(void); - -static inline -struct bt_field_path_item *bt_field_path_borrow_item_by_index_inline( - const struct bt_field_path *field_path, uint64_t index) -{ - BT_ASSERT(field_path); - BT_ASSERT(index < field_path->items->len); - return &g_array_index(field_path->items, struct bt_field_path_item, - index); -} - -static inline -void bt_field_path_append_item(struct bt_field_path *field_path, - struct bt_field_path_item *item) -{ - BT_ASSERT(field_path); - BT_ASSERT(item); - g_array_append_val(field_path->items, *item); -} - -static inline -void bt_field_path_remove_last_item(struct bt_field_path *field_path) -{ - BT_ASSERT(field_path); - BT_ASSERT(field_path->items->len > 0); - g_array_set_size(field_path->items, field_path->items->len - 1); -} - -static inline -const char *bt_field_path_item_type_string(enum bt_field_path_item_type type) -{ - switch (type) { - case BT_FIELD_PATH_ITEM_TYPE_INDEX: - return "BT_FIELD_PATH_ITEM_TYPE_INDEX"; - case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: - return "BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT"; - default: - return "(unknown)"; - } -}; - -#endif /* BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL */ diff --git a/include/babeltrace2/trace-ir/field-wrapper-internal.h b/include/babeltrace2/trace-ir/field-wrapper-internal.h deleted file mode 100644 index ed16c3fd..00000000 --- a/include/babeltrace2/trace-ir/field-wrapper-internal.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H -#define BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H - -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -struct bt_field_wrapper { - struct bt_object base; - - /* Owned by this */ - struct bt_field *field; -}; - -BT_HIDDEN -struct bt_field_wrapper *bt_field_wrapper_new(void *data); - -BT_HIDDEN -void bt_field_wrapper_destroy(struct bt_field_wrapper *field); - -BT_HIDDEN -struct bt_field_wrapper *bt_field_wrapper_create( - struct bt_object_pool *pool, struct bt_field_class *fc); - -#endif /* BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/packet-internal.h b/include/babeltrace2/trace-ir/packet-internal.h deleted file mode 100644 index 677c9fb0..00000000 --- a/include/babeltrace2/trace-ir/packet-internal.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_PACKET_INTERNAL_H -#define BABELTRACE_TRACE_IR_PACKET_INTERNAL_H - -/* - * Copyright 2016-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_packet { - struct bt_object base; - struct bt_field_wrapper *context_field; - struct bt_stream *stream; - bool frozen; -}; - -BT_HIDDEN -void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen); - -#ifdef BT_DEV_MODE -# define bt_packet_set_is_frozen _bt_packet_set_is_frozen -#else -# define bt_packet_set_is_frozen(_packet, _is_frozen) -#endif /* BT_DEV_MODE */ - -BT_HIDDEN -struct bt_packet *bt_packet_new(struct bt_stream *stream); - -BT_HIDDEN -void bt_packet_recycle(struct bt_packet *packet); - -BT_HIDDEN -void bt_packet_destroy(struct bt_packet *packet); - -#endif /* BABELTRACE_TRACE_IR_PACKET_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/resolve-field-path-internal.h b/include/babeltrace2/trace-ir/resolve-field-path-internal.h deleted file mode 100644 index 2b6052fa..00000000 --- a/include/babeltrace2/trace-ir/resolve-field-path-internal.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL -#define BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL - -/* - * Copyright 2016-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * The Common Trace Format (CTF) Specification is available at - * http://www.efficios.com/ctf - */ - -#include -#include -#include -#include - -struct bt_resolve_field_path_context { - struct bt_field_class *packet_context; - struct bt_field_class *event_common_context; - struct bt_field_class *event_specific_context; - struct bt_field_class *event_payload; -}; - -BT_HIDDEN -int bt_resolve_field_paths(struct bt_field_class *field_class, - struct bt_resolve_field_path_context *ctx); - -#endif /* BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL */ diff --git a/include/babeltrace2/trace-ir/stream-class-internal.h b/include/babeltrace2/trace-ir/stream-class-internal.h deleted file mode 100644 index f573c014..00000000 --- a/include/babeltrace2/trace-ir/stream-class-internal.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H -#define BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_stream_class { - struct bt_object base; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - uint64_t id; - bool assigns_automatic_event_class_id; - bool assigns_automatic_stream_id; - bool packets_have_beginning_default_clock_snapshot; - bool packets_have_end_default_clock_snapshot; - bool supports_discarded_events; - bool supports_discarded_packets; - bool discarded_events_have_default_clock_snapshots; - bool discarded_packets_have_default_clock_snapshots; - struct bt_field_class *packet_context_fc; - struct bt_field_class *event_common_context_fc; - struct bt_clock_class *default_clock_class; - - /* Array of `struct bt_event_class *` */ - GPtrArray *event_classes; - - /* Pool of `struct bt_field_wrapper *` */ - struct bt_object_pool packet_context_field_pool; - - bool frozen; -}; - -BT_HIDDEN -void _bt_stream_class_freeze(const struct bt_stream_class *stream_class); - -#ifdef BT_DEV_MODE -# define bt_stream_class_freeze _bt_stream_class_freeze -#else -# define bt_stream_class_freeze(_sc) -#endif - -static inline -struct bt_trace_class *bt_stream_class_borrow_trace_class_inline( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT(stream_class); - return (void *) bt_object_borrow_parent(&stream_class->base); -} - -#endif /* BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/stream-internal.h b/include/babeltrace2/trace-ir/stream-internal.h deleted file mode 100644 index 820a5a24..00000000 --- a/include/babeltrace2/trace-ir/stream-internal.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_STREAM_INTERNAL_H -#define BABELTRACE_TRACE_IR_STREAM_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -struct bt_stream_class; -struct bt_stream; - -struct bt_stream { - struct bt_object base; - - /* Owned by this */ - struct bt_stream_class *class; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - uint64_t id; - - /* Pool of `struct bt_packet *` */ - struct bt_object_pool packet_pool; - - bool frozen; -}; - -BT_HIDDEN -void _bt_stream_freeze(const struct bt_stream *stream); - -#ifdef BT_DEV_MODE -# define bt_stream_freeze _bt_stream_freeze -#else -# define bt_stream_freeze(_stream) -#endif - -static inline -struct bt_trace *bt_stream_borrow_trace_inline(const struct bt_stream *stream) -{ - BT_ASSERT(stream); - return (void *) bt_object_borrow_parent(&stream->base); -} - -#endif /* BABELTRACE_TRACE_IR_STREAM_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/trace-class-internal.h b/include/babeltrace2/trace-ir/trace-class-internal.h deleted file mode 100644 index 50bb1a8d..00000000 --- a/include/babeltrace2/trace-ir/trace-class-internal.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H -#define BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_trace_class { - struct bt_object base; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - struct { - uint8_t uuid[BABELTRACE_UUID_LEN]; - - /* NULL or `uuid` above */ - bt_uuid value; - } uuid; - - struct bt_value *environment; - - /* Array of `struct bt_stream_class *` */ - GPtrArray *stream_classes; - - bool assigns_automatic_stream_class_id; - GArray *destruction_listeners; - bool frozen; -}; - -BT_HIDDEN -void _bt_trace_class_freeze(const struct bt_trace_class *trace_class); - -#ifdef BT_DEV_MODE -# define bt_trace_class_freeze _bt_trace_class_freeze -#else -# define bt_trace_class_freeze(_tc) -#endif - -#endif /* BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/trace-internal.h b/include/babeltrace2/trace-ir/trace-internal.h deleted file mode 100644 index 2476aa45..00000000 --- a/include/babeltrace2/trace-ir/trace-internal.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_TRACE_INTERNAL_H -#define BABELTRACE_TRACE_IR_TRACE_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_trace { - struct bt_object base; - - /* Owned by this */ - struct bt_trace_class *class; - - struct { - GString *str; - - /* NULL or `str->str` above */ - const char *value; - } name; - - /* Array of `struct bt_stream *` */ - GPtrArray *streams; - - /* - * Stream class (weak, owned by owned trace class) to number of - * instantiated streams, used to automatically assign stream IDs - * per stream class within this trace. - */ - GHashTable *stream_classes_stream_count; - - GArray *destruction_listeners; - bool frozen; -}; - -BT_HIDDEN -void _bt_trace_freeze(const struct bt_trace *trace); - -#ifdef BT_DEV_MODE -# define bt_trace_freeze _bt_trace_freeze -#else -# define bt_trace_freeze(_trace) -#endif - -BT_HIDDEN -void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream); - -BT_HIDDEN -uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace, - const struct bt_stream_class *stream_class); - -#endif /* BABELTRACE_TRACE_IR_TRACE_INTERNAL_H */ diff --git a/include/babeltrace2/trace-ir/utils-internal.h b/include/babeltrace2/trace-ir/utils-internal.h deleted file mode 100644 index 18014d6a..00000000 --- a/include/babeltrace2/trace-ir/utils-internal.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef BABELTRACE_TRACE_IR_UTILS_INTERNAL_H -#define BABELTRACE_TRACE_IR_UTILS_INTERNAL_H - -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct search_query { - gpointer value; - int found; -}; - -static inline -uint64_t bt_util_ns_from_value(uint64_t frequency, uint64_t value_cycles) -{ - uint64_t ns; - - if (frequency == UINT64_C(1000000000)) { - ns = value_cycles; - } else { - double dblres = ((1e9 * (double) value_cycles) / (double) frequency); - - if (dblres >= (double) UINT64_MAX) { - /* Overflows uint64_t */ - ns = UINT64_C(-1); - } else { - ns = (uint64_t) dblres; - } - } - - return ns; -} - -static inline -bool bt_util_get_base_offset_ns(int64_t offset_seconds, uint64_t offset_cycles, - uint64_t frequency, int64_t *base_offset_ns) -{ - bool overflows = false; - uint64_t offset_cycles_ns; - - BT_ASSERT(base_offset_ns); - - /* Initialize nanosecond timestamp to clock's offset in seconds */ - if (offset_seconds <= (INT64_MIN / INT64_C(1000000000) - 1) || - offset_seconds >= (INT64_MAX / INT64_C(1000000000)) - 1) { - /* - * Overflow: offset in seconds converted to nanoseconds - * is outside the int64_t range. We also subtract 1 here - * to leave "space" for the offset in cycles converted - * to nanoseconds (which is always less than 1 second by - * contract). - */ - overflows = true; - goto end; - } - - /* Offset (seconds) to nanoseconds */ - *base_offset_ns = offset_seconds * INT64_C(1000000000); - - /* Add offset in cycles */ - BT_ASSERT(offset_cycles < frequency); - offset_cycles_ns = bt_util_ns_from_value(frequency, - offset_cycles); - BT_ASSERT(offset_cycles_ns < 1000000000); - *base_offset_ns += (int64_t) offset_cycles_ns; - -end: - return overflows; -} - -static inline -int bt_util_ns_from_origin_inline(int64_t base_offset_ns, - int64_t offset_seconds, uint64_t offset_cycles, - uint64_t frequency, uint64_t value, int64_t *ns_from_origin) -{ - int ret = 0; - uint64_t value_ns_unsigned; - int64_t value_ns_signed; - - /* Initialize to clock class's base offset */ - *ns_from_origin = base_offset_ns; - - /* Add given value in cycles */ - value_ns_unsigned = bt_util_ns_from_value(frequency, value); - if (value_ns_unsigned >= (uint64_t) INT64_MAX) { - /* - * FIXME: `value_ns_unsigned` could be greater than - * `INT64_MAX` in fact: in this case, we need to - * subtract `INT64_MAX` from `value_ns_unsigned`, make - * sure that the difference is less than `INT64_MAX`, - * and try to add them one after the other to - * `*ns_from_origin`. - */ - ret = -1; - goto end; - } - - value_ns_signed = (int64_t) value_ns_unsigned; - BT_ASSERT(value_ns_signed >= 0); - - if (*ns_from_origin <= 0) { - goto add_value; - } - - if (value_ns_signed > INT64_MAX - *ns_from_origin) { - ret = -1; - goto end; - } - -add_value: - *ns_from_origin += value_ns_signed; - -end: - return ret; -} - -static inline -int bt_util_ns_from_origin_clock_class(const struct bt_clock_class *clock_class, - uint64_t value, int64_t *ns_from_origin) -{ - int ret = 0; - - if (clock_class->base_offset.overflows) { - ret = -1; - goto end; - } - - ret = bt_util_ns_from_origin_inline(clock_class->base_offset.value_ns, - clock_class->offset_seconds, clock_class->offset_cycles, - clock_class->frequency, value, ns_from_origin); - -end: - return ret; -} - -static inline -bool bt_util_value_is_in_range_signed(uint64_t size, int64_t value) -{ - int64_t min_value = UINT64_C(-1) << (size - 1); - int64_t max_value = (UINT64_C(1) << (size - 1)) - 1; - return value >= min_value && value <= max_value; -} - -static inline -bool bt_util_value_is_in_range_unsigned(unsigned int size, uint64_t value) -{ - uint64_t max_value = (size == 64) ? UINT64_MAX : - (UINT64_C(1) << size) - 1; - return value <= max_value; -} - -#endif /* BABELTRACE_TRACE_IR_UTILS_INTERNAL_H */ diff --git a/include/babeltrace2/value-internal.h b/include/babeltrace2/value-internal.h deleted file mode 100644 index abd607ab..00000000 --- a/include/babeltrace2/value-internal.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef BABELTRACE_VALUES_INTERNAL_H -#define BABELTRACE_VALUES_INTERNAL_H - -/* - * Copyright (c) 2015-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -struct bt_value { - struct bt_object base; - enum bt_value_type type; - bt_bool frozen; -}; - -struct bt_value_bool { - struct bt_value base; - bt_bool value; -}; - -struct bt_value_integer { - struct bt_value base; - union { - uint64_t i; - int64_t u; - } value; -}; - -struct bt_value_real { - struct bt_value base; - double value; -}; - -struct bt_value_string { - struct bt_value base; - GString *gstr; -}; - -struct bt_value_array { - struct bt_value base; - GPtrArray *garray; -}; - -struct bt_value_map { - struct bt_value base; - GHashTable *ght; -}; - -BT_HIDDEN -enum bt_value_status _bt_value_freeze(const struct bt_value *object); - -#ifdef BT_DEV_MODE -# define bt_value_freeze _bt_value_freeze -#else -# define bt_value_freeze(_value) -#endif /* BT_DEV_MODE */ - -#endif /* BABELTRACE_VALUES_INTERNAL_H */ diff --git a/include/version.h b/include/version.h deleted file mode 100644 index 4f802517..00000000 --- a/include/version.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef VERSION_H -#define VERSION_H - -/* - * Copyright (C) 2018 Michael Jeanson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "version.i" - -#endif /* VERSION_H */ diff --git a/lib/Makefile.am b/lib/Makefile.am deleted file mode 100644 index 39b956a9..00000000 --- a/lib/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -SUBDIRS = trace-ir prio_heap plugin graph - -lib_LTLIBRARIES = libbabeltrace2.la - -libbabeltrace2_la_SOURCES = \ - babeltrace2.c \ - value.c \ - util.c \ - lib-logging.c \ - logging.c \ - object-pool.c -libbabeltrace2_la_LDFLAGS = $(LT_NO_UNDEFINED) \ - -version-info $(BABELTRACE_LIBRARY_VERSION) - -libbabeltrace2_la_LIBADD = \ - prio_heap/libprio_heap.la \ - graph/libgraph.la \ - plugin/libplugin.la \ - trace-ir/libtrace-ir.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/compat/libcompat.la - -if ENABLE_BUILT_IN_PYTHON_PLUGIN_SUPPORT -libbabeltrace2_la_LIBADD += $(top_builddir)/python-plugin-provider/libbabeltrace2-python-plugin-provider.la -endif diff --git a/lib/babeltrace2.c b/lib/babeltrace2.c deleted file mode 100644 index 4a74214a..00000000 --- a/lib/babeltrace2.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation - * - * Author: Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -int bt_version_get_major(void) -{ - return BT_VERSION_MAJOR; -} - -int bt_version_get_minor(void) -{ - return BT_VERSION_MINOR; -} - -int bt_version_get_patch(void) { - return BT_VERSION_PATCH; -} - -const char *bt_version_get_extra(void) -{ - return BT_VERSION_EXTRA; -} diff --git a/lib/graph/Makefile.am b/lib/graph/Makefile.am deleted file mode 100644 index bd373ef7..00000000 --- a/lib/graph/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -SUBDIRS = message - -noinst_LTLIBRARIES = libgraph.la - -# Graph library -libgraph_la_SOURCES = \ - component-class-sink-colander.c \ - component-class.c \ - component-filter.c \ - component-sink.c \ - component-source.c \ - component.c \ - connection.c \ - graph.c \ - iterator.c \ - port.c \ - query-executor.c - -libgraph_la_LIBADD = \ - message/libgraph-message.la diff --git a/lib/graph/component-class-sink-colander.c b/lib/graph/component-class-sink-colander.c deleted file mode 100644 index 6a9d287e..00000000 --- a/lib/graph/component-class-sink-colander.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COLANDER" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -struct bt_component_class_sink *colander_comp_cls; - -static -enum bt_self_component_status colander_init( - struct bt_self_component_sink *self_comp, - const struct bt_value *params, void *init_method_data) -{ - enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct bt_component_class_sink_colander_priv_data *colander_data = NULL; - struct bt_component_class_sink_colander_data *user_provided_data = - init_method_data; - - if (!init_method_data) { - BT_LOGW_STR("Component initialization method data is NULL."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - colander_data = g_new0( - struct bt_component_class_sink_colander_priv_data, 1); - if (!colander_data) { - BT_LOGE_STR("Failed to allocate colander data."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - colander_data->msgs = user_provided_data->msgs; - colander_data->count_addr = user_provided_data->count_addr; - status = bt_self_component_sink_add_input_port(self_comp, "in", - NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGE_STR("Cannot add input port."); - goto end; - } - - bt_self_component_set_data( - bt_self_component_sink_as_self_component(self_comp), - colander_data); - -end: - return status; -} - -static -void colander_finalize(struct bt_self_component_sink *self_comp) -{ - struct bt_component_class_sink_colander_priv_data *colander_data = - bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - - if (!colander_data) { - return; - } - - BT_OBJECT_PUT_REF_AND_RESET(colander_data->msg_iter); - g_free(colander_data); -} - -static -enum bt_self_component_status colander_graph_is_configured( - bt_self_component_sink *self_comp) -{ - enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct bt_component_class_sink_colander_priv_data *colander_data = - bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - - struct bt_self_component_port_input *self_port = - bt_self_component_sink_borrow_input_port_by_name(self_comp, "in"); - BT_ASSERT(self_port); - - BT_ASSERT(colander_data); - BT_OBJECT_PUT_REF_AND_RESET(colander_data->msg_iter); - colander_data->msg_iter = - bt_self_component_port_input_message_iterator_create( - self_port); - if (!colander_data->msg_iter) { - BT_LIB_LOGE("Cannot create message iterator on " - "self component input port: %![port-]+p", - self_port); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - -end: - return status; -} - -static -enum bt_self_component_status colander_consume( - struct bt_self_component_sink *self_comp) -{ - enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - enum bt_message_iterator_status msg_iter_status; - struct bt_component_class_sink_colander_priv_data *colander_data = - bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - bt_message_array_const msgs; - - BT_ASSERT(colander_data); - - if (!colander_data->msg_iter) { - BT_LIB_LOGW("Trying to consume without an " - "upstream message iterator: %![comp-]+c", - self_comp); - goto end; - } - - msg_iter_status = - bt_self_component_port_input_message_iterator_next( - colander_data->msg_iter, &msgs, - colander_data->count_addr); - switch (msg_iter_status) { - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - status = BT_SELF_COMPONENT_STATUS_AGAIN; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_END: - status = BT_SELF_COMPONENT_STATUS_END; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_OK: - /* Move messages to user (count already set) */ - memcpy(colander_data->msgs, msgs, - sizeof(*msgs) * *colander_data->count_addr); - break; - default: - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -struct bt_component_class_sink *bt_component_class_sink_colander_get(void) -{ - if (colander_comp_cls) { - goto end; - } - - colander_comp_cls = bt_component_class_sink_create("colander", - colander_consume); - if (!colander_comp_cls) { - BT_LOGE_STR("Cannot create sink colander component class."); - goto end; - } - - (void) bt_component_class_sink_set_init_method( - colander_comp_cls, colander_init); - (void) bt_component_class_sink_set_finalize_method( - colander_comp_cls, colander_finalize); - (void) bt_component_class_sink_set_graph_is_configured_method( - colander_comp_cls, colander_graph_is_configured); - -end: - bt_object_get_ref(colander_comp_cls); - return (void *) colander_comp_cls; -} - -__attribute__((destructor)) static -void put_colander(void) { - BT_OBJECT_PUT_REF_AND_RESET(colander_comp_cls); -} diff --git a/lib/graph/component-class.c b/lib/graph/component-class.c deleted file mode 100644 index 4cfc3dc7..00000000 --- a/lib/graph/component-class.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMP-CLASS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_COMP_CLS_HOT(_cc) \ - BT_ASSERT_PRE_HOT(((const struct bt_component_class *) (_cc)), \ - "Component class", ": %!+C", (_cc)) - -static -void destroy_component_class(struct bt_object *obj) -{ - struct bt_component_class *class; - int i; - - BT_ASSERT(obj); - class = container_of(obj, struct bt_component_class, base); - - BT_LIB_LOGD("Destroying component class: %!+C", class); - - /* Call destroy listeners in reverse registration order */ - for (i = class->destroy_listeners->len - 1; i >= 0; i--) { - struct bt_component_class_destroy_listener *listener = - &g_array_index(class->destroy_listeners, - struct bt_component_class_destroy_listener, - i); - - BT_LOGD("Calling destroy listener: func-addr=%p, data-addr=%p", - listener->func, listener->data); - listener->func(class, listener->data); - } - - if (class->name) { - g_string_free(class->name, TRUE); - class->name = NULL; - } - - if (class->description) { - g_string_free(class->description, TRUE); - class->description = NULL; - } - - if (class->help) { - g_string_free(class->help, TRUE); - class->help = NULL; - } - - if (class->destroy_listeners) { - g_array_free(class->destroy_listeners, TRUE); - class->destroy_listeners = NULL; - } - - g_free(class); -} - -static -int bt_component_class_init(struct bt_component_class *class, - enum bt_component_class_type type, const char *name) -{ - int ret = 0; - - bt_object_init_shared(&class->base, destroy_component_class); - class->type = type; - class->name = g_string_new(name); - if (!class->name) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - class->description = g_string_new(NULL); - if (!class->description) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - class->help = g_string_new(NULL); - if (!class->help) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - class->destroy_listeners = g_array_new(FALSE, TRUE, - sizeof(struct bt_component_class_destroy_listener)); - if (!class->destroy_listeners) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(class); - ret = -1; - -end: - return ret; -} - -struct bt_component_class_source *bt_component_class_source_create( - const char *name, - bt_component_class_source_message_iterator_next_method method) -{ - struct bt_component_class_source *source_class = NULL; - int ret; - - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(method, "Message iterator next method"); - BT_LOGD("Creating source component class: " - "name=\"%s\", msg-iter-next-method-addr=%p", - name, method); - source_class = g_new0(struct bt_component_class_source, 1); - if (!source_class) { - BT_LOGE_STR("Failed to allocate one source component class."); - goto end; - } - - /* bt_component_class_init() logs errors */ - ret = bt_component_class_init(&source_class->parent, - BT_COMPONENT_CLASS_TYPE_SOURCE, name); - if (ret) { - /* - * If bt_component_class_init() fails, the component - * class is put, therefore its memory is already - * freed. - */ - source_class = NULL; - goto end; - } - - source_class->methods.msg_iter_next = method; - BT_LIB_LOGD("Created source component class: %!+C", source_class); - -end: - return (void *) source_class; -} - -struct bt_component_class_filter *bt_component_class_filter_create( - const char *name, - bt_component_class_filter_message_iterator_next_method method) -{ - struct bt_component_class_filter *filter_class = NULL; - int ret; - - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(method, "Message iterator next method"); - BT_LOGD("Creating filter component class: " - "name=\"%s\", msg-iter-next-method-addr=%p", - name, method); - filter_class = g_new0(struct bt_component_class_filter, 1); - if (!filter_class) { - BT_LOGE_STR("Failed to allocate one filter component class."); - goto end; - } - - /* bt_component_class_init() logs errors */ - ret = bt_component_class_init(&filter_class->parent, - BT_COMPONENT_CLASS_TYPE_FILTER, name); - if (ret) { - /* - * If bt_component_class_init() fails, the component - * class is put, therefore its memory is already - * freed. - */ - filter_class = NULL; - goto end; - } - - filter_class->methods.msg_iter_next = method; - BT_LIB_LOGD("Created filter component class: %!+C", filter_class); - -end: - return (void *) filter_class; -} - -struct bt_component_class_sink *bt_component_class_sink_create( - const char *name, bt_component_class_sink_consume_method method) -{ - struct bt_component_class_sink *sink_class = NULL; - int ret; - - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(method, "Consume next method"); - BT_LOGD("Creating sink component class: " - "name=\"%s\", consume-method-addr=%p", - name, method); - sink_class = g_new0(struct bt_component_class_sink, 1); - if (!sink_class) { - BT_LOGE_STR("Failed to allocate one sink component class."); - goto end; - } - - /* bt_component_class_init() logs errors */ - ret = bt_component_class_init(&sink_class->parent, - BT_COMPONENT_CLASS_TYPE_SINK, name); - if (ret) { - /* - * If bt_component_class_init() fails, the component - * class is put, therefore its memory is already - * freed. - */ - sink_class = NULL; - goto end; - } - - sink_class->methods.consume = method; - BT_LIB_LOGD("Created sink component class: %!+C", sink_class); - -end: - return (void *) sink_class; -} - -enum bt_component_class_status -bt_component_class_source_set_init_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_init_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.init = method; - BT_LIB_LOGV("Set source component class's initialization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_init_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_init_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.init = method; - BT_LIB_LOGV("Set filter component class's initialization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_init_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_init_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.init = method; - BT_LIB_LOGV("Set sink component class's initialization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_finalize_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_finalize_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.finalize = method; - BT_LIB_LOGV("Set source component class's finalization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_finalize_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_finalize_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.finalize = method; - BT_LIB_LOGV("Set filter component class's finalization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_finalize_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_finalize_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.finalize = method; - BT_LIB_LOGV("Set sink component class's finalization method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_query_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_query_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.query = method; - BT_LIB_LOGV("Set source component class's query method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_query_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_query_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.query = method; - BT_LIB_LOGV("Set filter component class's query method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_query_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_query_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.query = method; - BT_LIB_LOGV("Set sink component class's query method: " - "%!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_accept_input_port_connection_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_accept_input_port_connection_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.accept_input_port_connection = method; - BT_LIB_LOGV("Set filter component class's \"accept input port connection\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_accept_input_port_connection_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_accept_input_port_connection_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.accept_input_port_connection = method; - BT_LIB_LOGV("Set sink component class's \"accept input port connection\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_accept_output_port_connection_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_accept_output_port_connection_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.accept_output_port_connection = method; - BT_LIB_LOGV("Set source component class's \"accept output port connection\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_accept_output_port_connection_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_accept_output_port_connection_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.accept_output_port_connection = method; - BT_LIB_LOGV("Set filter component class's \"accept output port connection\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_input_port_connected_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_input_port_connected_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.input_port_connected = method; - BT_LIB_LOGV("Set filter component class's \"input port connected\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_input_port_connected_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_input_port_connected_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.input_port_connected = method; - BT_LIB_LOGV("Set sink component class's \"input port connected\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_output_port_connected_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_output_port_connected_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.output_port_connected = method; - BT_LIB_LOGV("Set source component class's \"output port connected\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_output_port_connected_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_output_port_connected_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.output_port_connected = method; - BT_LIB_LOGV("Set filter component class's \"output port connected\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_sink_set_graph_is_configured_method( - struct bt_component_class_sink *comp_cls, - bt_component_class_sink_graph_is_configured_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.graph_is_configured = method; - BT_LIB_LOGV("Set sink component class's \"graph is configured\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -int bt_component_class_source_set_message_iterator_init_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_init_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_init = method; - BT_LIB_LOGV("Set source component class's message iterator initialization method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_init_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_init_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_init = method; - BT_LIB_LOGV("Set filter component class's message iterator initialization method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_message_iterator_finalize_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_finalize_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_finalize = method; - BT_LIB_LOGV("Set source component class's message iterator finalization method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_finalize_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_finalize_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_finalize = method; - BT_LIB_LOGV("Set filter component class's message iterator finalization method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_seek_ns_from_origin_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_seek_ns_from_origin = method; - BT_LIB_LOGV("Set filter component class's message iterator \"seek nanoseconds from origin\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_seek_ns_from_origin_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_seek_ns_from_origin = method; - BT_LIB_LOGV("Set source component class's message iterator \"seek nanoseconds from origin\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_seek_beginning_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_seek_beginning_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_seek_beginning = method; - BT_LIB_LOGV("Set filter component class's message iterator \"seek beginning\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_message_iterator_seek_beginning_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_seek_beginning_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_seek_beginning = method; - BT_LIB_LOGV("Set source component class's message iterator \"seek beginning\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_can_seek_beginning_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_can_seek_beginning_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_can_seek_beginning = method; - BT_LIB_LOGV("Set filter component class's message iterator \"can seek beginning\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_message_iterator_can_seek_beginning_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_can_seek_beginning_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_can_seek_beginning = method; - BT_LIB_LOGV("Set source component class's message iterator \"can seek beginning\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( - struct bt_component_class_filter *comp_cls, - bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_can_seek_ns_from_origin = method; - BT_LIB_LOGV("Set filter component class's message iterator \"can seek nanoseconds from origin\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -enum bt_component_class_status -bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( - struct bt_component_class_source *comp_cls, - bt_component_class_source_message_iterator_can_seek_ns_from_origin_method method) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(method, "Method"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - comp_cls->methods.msg_iter_can_seek_ns_from_origin = method; - BT_LIB_LOGV("Set source component class's message iterator \"can seek nanoseconds from origin\" method" - ": %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -bt_component_class_status bt_component_class_set_description( - struct bt_component_class *comp_cls, - const char *description) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(description, "Description"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - g_string_assign(comp_cls->description, description); - BT_LIB_LOGV("Set component class's description: " - "addr=%p, name=\"%s\", type=%s", - comp_cls, - bt_component_class_get_name(comp_cls), - bt_component_class_type_string(comp_cls->type)); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -bt_component_class_status bt_component_class_set_help( - struct bt_component_class *comp_cls, - const char *help) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(help, "Help"); - BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); - g_string_assign(comp_cls->help, help); - BT_LIB_LOGV("Set component class's help text: %!+C", comp_cls); - return BT_COMPONENT_CLASS_STATUS_OK; -} - -const char *bt_component_class_get_name(const struct bt_component_class *comp_cls) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return comp_cls->name->str; -} - -enum bt_component_class_type bt_component_class_get_type( - const struct bt_component_class *comp_cls) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return comp_cls->type; -} - -const char *bt_component_class_get_description( - const struct bt_component_class *comp_cls) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return comp_cls->description && - comp_cls->description->str[0] != '\0' ? - comp_cls->description->str : NULL; -} - -const char *bt_component_class_get_help( - const struct bt_component_class *comp_cls) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return comp_cls->help && - comp_cls->help->str[0] != '\0' ? comp_cls->help->str : NULL; -} - -BT_HIDDEN -void bt_component_class_add_destroy_listener( - struct bt_component_class *comp_cls, - bt_component_class_destroy_listener_func func, void *data) -{ - struct bt_component_class_destroy_listener listener; - - BT_ASSERT(comp_cls); - BT_ASSERT(func); - listener.func = func; - listener.data = data; - g_array_append_val(comp_cls->destroy_listeners, listener); - BT_LIB_LOGV("Added destroy listener to component class: " - "%![cc-]+C, listener-func-addr=%p", comp_cls, func); -} - -BT_HIDDEN -void _bt_component_class_freeze(const struct bt_component_class *comp_cls) -{ - BT_ASSERT(comp_cls); - BT_LIB_LOGD("Freezing component class: %!+C", comp_cls); - ((struct bt_component_class *) comp_cls)->frozen = true; -} - -void bt_component_class_get_ref( - const struct bt_component_class *component_class) -{ - bt_object_get_ref(component_class); -} - -void bt_component_class_put_ref( - const struct bt_component_class *component_class) -{ - bt_object_put_ref(component_class); -} - -void bt_component_class_source_get_ref( - const struct bt_component_class_source *component_class_source) -{ - bt_object_get_ref(component_class_source); -} - -void bt_component_class_source_put_ref( - const struct bt_component_class_source *component_class_source) -{ - bt_object_put_ref(component_class_source); -} - -void bt_component_class_filter_get_ref( - const struct bt_component_class_filter *component_class_filter) -{ - bt_object_get_ref(component_class_filter); -} - -void bt_component_class_filter_put_ref( - const struct bt_component_class_filter *component_class_filter) -{ - bt_object_put_ref(component_class_filter); -} - -void bt_component_class_sink_get_ref( - const struct bt_component_class_sink *component_class_sink) -{ - bt_object_get_ref(component_class_sink); -} - -void bt_component_class_sink_put_ref( - const struct bt_component_class_sink *component_class_sink) -{ - bt_object_put_ref(component_class_sink); -} diff --git a/lib/graph/component-filter.c b/lib/graph/component-filter.c deleted file mode 100644 index 8c79b638..00000000 --- a/lib/graph/component-filter.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMP-FILTER" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_component_filter_destroy(struct bt_component *component) -{ -} - -BT_HIDDEN -struct bt_component *bt_component_filter_create( - const struct bt_component_class *class) -{ - struct bt_component_filter *filter = NULL; - - filter = g_new0(struct bt_component_filter, 1); - if (!filter) { - BT_LOGE_STR("Failed to allocate one filter component."); - goto end; - } - -end: - return (void *) filter; -} - -const bt_component_class_filter * -bt_component_filter_borrow_class_const( - const bt_component_filter *component) -{ - struct bt_component_class *cls; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - - cls = component->parent.class; - - BT_ASSERT(cls); - BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_FILTER); - - return (bt_component_class_filter *) cls; -} - -uint64_t bt_component_filter_get_output_port_count( - const struct bt_component_filter *comp) -{ - return bt_component_get_output_port_count((void *) comp); -} - -const struct bt_port_output * -bt_component_filter_borrow_output_port_by_name_const( - const struct bt_component_filter *comp, const char *name) -{ - return bt_component_borrow_output_port_by_name( - (void *) comp, name); -} - -struct bt_self_component_port_output * -bt_self_component_filter_borrow_output_port_by_name( - struct bt_self_component_filter *comp, const char *name) -{ - return (void *) bt_component_borrow_output_port_by_name( - (void *) comp, name); -} - -const struct bt_port_output * -bt_component_filter_borrow_output_port_by_index_const( - const struct bt_component_filter *comp, uint64_t index) -{ - return bt_component_borrow_output_port_by_index( - (void *) comp, index); -} - -struct bt_self_component_port_output * -bt_self_component_filter_borrow_output_port_by_index( - struct bt_self_component_filter *comp, uint64_t index) -{ - return (void *) bt_component_borrow_output_port_by_index( - (void *) comp, index); -} - -enum bt_self_component_status bt_self_component_filter_add_output_port( - struct bt_self_component_filter *self_comp, - const char *name, void *user_data, - struct bt_self_component_port_output **self_port) -{ - struct bt_component *comp = (void *) self_comp; - enum bt_self_component_status status; - struct bt_port *port = NULL; - - /* bt_component_add_output_port() logs details and errors */ - status = bt_component_add_output_port(comp, name, user_data, &port); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - if (self_port) { - /* Move reference to user */ - *self_port = (void *) port; - port = NULL; - } - -end: - bt_object_put_ref(port); - return status; -} - -uint64_t bt_component_filter_get_input_port_count( - const struct bt_component_filter *component) -{ - /* bt_component_get_input_port_count() logs details/errors */ - return bt_component_get_input_port_count((void *) component); -} - -const struct bt_port_input *bt_component_filter_borrow_input_port_by_name_const( - const struct bt_component_filter *component, const char *name) -{ - /* bt_component_borrow_input_port_by_name() logs details/errors */ - return bt_component_borrow_input_port_by_name( - (void *) component, name); -} - -struct bt_self_component_port_input * -bt_self_component_filter_borrow_input_port_by_name( - struct bt_self_component_filter *component, const char *name) -{ - /* bt_component_borrow_input_port_by_name() logs details/errors */ - return (void *) bt_component_borrow_input_port_by_name( - (void *) component, name); -} - -const struct bt_port_input * -bt_component_filter_borrow_input_port_by_index_const( - const struct bt_component_filter *component, uint64_t index) -{ - /* bt_component_borrow_input_port_by_index() logs details/errors */ - return bt_component_borrow_input_port_by_index( - (void *) component, index); -} - -struct bt_self_component_port_input * -bt_self_component_filter_borrow_input_port_by_index( - struct bt_self_component_filter *component, uint64_t index) -{ - /* bt_component_borrow_input_port_by_index() logs details/errors */ - return (void *) bt_component_borrow_input_port_by_index( - (void *) component, index); -} - -enum bt_self_component_status bt_self_component_filter_add_input_port( - struct bt_self_component_filter *self_comp, - const char *name, void *user_data, - struct bt_self_component_port_input **self_port) -{ - enum bt_self_component_status status; - struct bt_port *port = NULL; - struct bt_component *comp = (void *) self_comp; - - /* bt_component_add_input_port() logs details/errors */ - status = bt_component_add_input_port(comp, name, user_data, &port); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - if (self_port) { - /* Move reference to user */ - *self_port = (void *) port; - port = NULL; - } - -end: - bt_object_put_ref(port); - return status; -} - -void bt_component_filter_get_ref( - const struct bt_component_filter *component_filter) -{ - bt_object_get_ref(component_filter); -} - -void bt_component_filter_put_ref( - const struct bt_component_filter *component_filter) -{ - bt_object_put_ref(component_filter); -} diff --git a/lib/graph/component-sink.c b/lib/graph/component-sink.c deleted file mode 100644 index 1887fbfd..00000000 --- a/lib/graph/component-sink.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMP-SINK" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_component_sink_destroy(struct bt_component *component) -{ -} - -BT_HIDDEN -struct bt_component *bt_component_sink_create( - const struct bt_component_class *class) -{ - struct bt_component_sink *sink = NULL; - - sink = g_new0(struct bt_component_sink, 1); - if (!sink) { - BT_LOGE_STR("Failed to allocate one sink component."); - goto end; - } - -end: - return (void *) sink; -} - -const bt_component_class_sink * -bt_component_sink_borrow_class_const( - const bt_component_sink *component) -{ - struct bt_component_class *cls; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - - cls = component->parent.class; - - BT_ASSERT(cls); - BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_SINK); - - return (bt_component_class_sink *) cls; -} - -uint64_t bt_component_sink_get_input_port_count( - const struct bt_component_sink *component) -{ - /* bt_component_get_input_port_count() logs details/errors */ - return bt_component_get_input_port_count((void *) component); -} - -const struct bt_port_input * -bt_component_sink_borrow_input_port_by_name_const( - const struct bt_component_sink *component, const char *name) -{ - /* bt_component_borrow_input_port_by_name() logs details/errors */ - return bt_component_borrow_input_port_by_name((void *) component, name); -} - -struct bt_self_component_port_input * -bt_self_component_sink_borrow_input_port_by_name( - struct bt_self_component_sink *component, const char *name) -{ - /* bt_component_borrow_input_port_by_name() logs details/errors */ - return (void *) bt_component_borrow_input_port_by_name( - (void *) component, name); -} - -const struct bt_port_input *bt_component_sink_borrow_input_port_by_index_const( - const struct bt_component_sink *component, uint64_t index) -{ - /* bt_component_borrow_input_port_by_index() logs details/errors */ - return bt_component_borrow_input_port_by_index( - (void *) component, index); -} - -struct bt_self_component_port_input * -bt_self_component_sink_borrow_input_port_by_index( - struct bt_self_component_sink *component, uint64_t index) -{ - /* bt_component_borrow_input_port_by_index() logs details/errors */ - return (void *) bt_component_borrow_input_port_by_index( - (void *) component, index); -} - -enum bt_self_component_status bt_self_component_sink_add_input_port( - struct bt_self_component_sink *self_comp, - const char *name, void *user_data, - struct bt_self_component_port_input **self_port) -{ - enum bt_self_component_status status; - struct bt_port *port = NULL; - struct bt_component *comp = (void *) self_comp; - - /* bt_component_add_input_port() logs details/errors */ - status = bt_component_add_input_port(comp, name, user_data, &port); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - if (self_port) { - /* Move reference to user */ - *self_port = (void *) port; - port = NULL; - } - -end: - bt_object_put_ref(port); - return status; -} - -void bt_component_sink_get_ref( - const struct bt_component_sink *component_sink) -{ - bt_object_get_ref(component_sink); -} - -void bt_component_sink_put_ref( - const struct bt_component_sink *component_sink) -{ - bt_object_put_ref(component_sink); -} diff --git a/lib/graph/component-source.c b/lib/graph/component-source.c deleted file mode 100644 index 73740cda..00000000 --- a/lib/graph/component-source.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMP-SOURCE" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_component_source_destroy(struct bt_component *component) -{ -} - -BT_HIDDEN -struct bt_component *bt_component_source_create( - const struct bt_component_class *class) -{ - struct bt_component_source *source = NULL; - - source = g_new0(struct bt_component_source, 1); - if (!source) { - BT_LOGE_STR("Failed to allocate one source component."); - goto end; - } - -end: - return (void *) source; -} - -const bt_component_class_source * -bt_component_source_borrow_class_const( - const bt_component_source *component) -{ - struct bt_component_class *cls; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - - cls = component->parent.class; - - BT_ASSERT(cls); - BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_SOURCE); - - return (bt_component_class_source *) cls; -} - -uint64_t bt_component_source_get_output_port_count( - const struct bt_component_source *comp) -{ - return bt_component_get_output_port_count((void *) comp); -} - -const struct bt_port_output * -bt_component_source_borrow_output_port_by_name_const( - const struct bt_component_source *comp, const char *name) -{ - return bt_component_borrow_output_port_by_name((void *) comp, name); -} - -struct bt_self_component_port_output * -bt_self_component_source_borrow_output_port_by_name( - struct bt_self_component_source *comp, const char *name) -{ - return (void *) bt_component_borrow_output_port_by_name( - (void *) comp, name); -} - -const struct bt_port_output * -bt_component_source_borrow_output_port_by_index_const( - const struct bt_component_source *comp, uint64_t index) -{ - return bt_component_borrow_output_port_by_index((void *) comp, index); -} - -struct bt_self_component_port_output * -bt_self_component_source_borrow_output_port_by_index( - struct bt_self_component_source *comp, uint64_t index) -{ - return (void *) bt_component_borrow_output_port_by_index( - (void *) comp, index); -} - -enum bt_self_component_status bt_self_component_source_add_output_port( - struct bt_self_component_source *self_comp, - const char *name, void *user_data, - struct bt_self_component_port_output **self_port) -{ - struct bt_component *comp = (void *) self_comp; - enum bt_self_component_status status; - struct bt_port *port = NULL; - - /* bt_component_add_output_port() logs details and errors */ - status = bt_component_add_output_port(comp, name, user_data, &port); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - if (self_port) { - /* Move reference to user */ - *self_port = (void *) port; - port = NULL; - } - -end: - bt_object_put_ref(port); - return status; -} - -void bt_component_source_get_ref( - const struct bt_component_source *component_source) -{ - bt_object_get_ref(component_source); -} - -void bt_component_source_put_ref( - const struct bt_component_source *component_source) -{ - bt_object_put_ref(component_source); -} diff --git a/lib/graph/component.c b/lib/graph/component.c deleted file mode 100644 index 6494f648..00000000 --- a/lib/graph/component.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "COMP" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -struct bt_component * (* const component_create_funcs[])( - const struct bt_component_class *) = { - [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_create, - [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_create, - [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_create, -}; - -static -void (*component_destroy_funcs[])(struct bt_component *) = { - [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_destroy, - [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_destroy, - [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_destroy, -}; - -static -void finalize_component(struct bt_component *comp) -{ - typedef void (*method_t)(void *); - - method_t method = NULL; - - BT_ASSERT(comp); - - switch (comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_cc = (void *) comp->class; - - method = (method_t) src_cc->methods.finalize; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_cc = (void *) comp->class; - - method = (method_t) flt_cc->methods.finalize; - break; - } - case BT_COMPONENT_CLASS_TYPE_SINK: - { - struct bt_component_class_sink *sink_cc = (void *) comp->class; - - method = (method_t) sink_cc->methods.finalize; - break; - } - default: - abort(); - } - - if (method) { - BT_LIB_LOGD("Calling user's finalization method: " - "%![comp-]+c", comp); - method(comp); - } -} - -static -void destroy_component(struct bt_object *obj) -{ - struct bt_component *component = NULL; - int i; - - if (!obj) { - return; - } - - /* - * The component's reference count is 0 if we're here. Increment - * it to avoid a double-destroy (possibly infinitely recursive). - * This could happen for example if the component's finalization - * function does bt_object_get_ref() (or anything that causes - * bt_object_get_ref() to be called) on itself (ref. count goes - * from 0 to 1), and then bt_object_put_ref(): the reference - * count would go from 1 to 0 again and this function would be - * called again. - */ - obj->ref_count++; - component = container_of(obj, struct bt_component, base); - BT_LIB_LOGD("Destroying component: %![comp-]+c, %![graph-]+g", - component, bt_component_borrow_graph(component)); - - /* Call destroy listeners in reverse registration order */ - BT_LOGD_STR("Calling destroy listeners."); - - for (i = component->destroy_listeners->len - 1; i >= 0; i--) { - struct bt_component_destroy_listener *listener = - &g_array_index(component->destroy_listeners, - struct bt_component_destroy_listener, i); - - listener->func(component, listener->data); - } - - /* - * User data is destroyed first, followed by the concrete - * component instance. Do not finalize if the component's user - * initialization method failed in the first place. - */ - if (component->initialized) { - finalize_component(component); - } - - if (component->destroy) { - BT_LOGD_STR("Destroying type-specific data."); - component->destroy(component); - } - - if (component->input_ports) { - BT_LOGD_STR("Destroying input ports."); - g_ptr_array_free(component->input_ports, TRUE); - component->input_ports = NULL; - } - - if (component->output_ports) { - BT_LOGD_STR("Destroying output ports."); - g_ptr_array_free(component->output_ports, TRUE); - component->output_ports = NULL; - } - - if (component->destroy_listeners) { - g_array_free(component->destroy_listeners, TRUE); - component->destroy_listeners = NULL; - } - - if (component->name) { - g_string_free(component->name, TRUE); - component->name = NULL; - } - - BT_LOGD_STR("Putting component class."); - BT_OBJECT_PUT_REF_AND_RESET(component->class); - g_free(component); -} - -enum bt_component_class_type bt_component_get_class_type( - const struct bt_component *component) -{ - BT_ASSERT_PRE_NON_NULL(component, "Component"); - return component->class->type; -} - -static -enum bt_self_component_status add_port( - struct bt_component *component, GPtrArray *ports, - enum bt_port_type port_type, const char *name, void *user_data, - struct bt_port **port) -{ - struct bt_port *new_port = NULL; - struct bt_graph *graph = NULL; - enum bt_self_component_status status; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE(strlen(name) > 0, "Name is empty"); - graph = bt_component_borrow_graph(component); - BT_ASSERT_PRE(graph && !bt_graph_is_canceled(graph), - "Component's graph is canceled: %![comp-]+c, %![graph-]+g", - component, graph); - BT_ASSERT_PRE( - graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Component's graph is already configured: " - "%![comp-]+c, %![graph-]+g", component, graph); - - // TODO: Validate that the name is not already used. - - BT_LIB_LOGD("Adding port to component: %![comp-]+c, " - "port-type=%s, port-name=\"%s\"", component, - bt_port_type_string(port_type), name); - - new_port = bt_port_create(component, port_type, name, user_data); - if (!new_port) { - BT_LOGE_STR("Cannot create port object."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto error; - } - - /* - * No name clash, add the port. - * The component is now the port's parent; it should _not_ - * hold a reference to the port since the port's lifetime - * is now protected by the component's own lifetime. - */ - g_ptr_array_add(ports, new_port); - - /* - * Notify the graph's creator that a new port was added. - */ - graph = bt_component_borrow_graph(component); - if (graph) { - enum bt_graph_listener_status listener_status; - - listener_status = bt_graph_notify_port_added(graph, new_port); - if (listener_status != BT_GRAPH_LISTENER_STATUS_OK) { - bt_graph_make_faulty(graph); - status = listener_status; - goto error; - } - } - - BT_LIB_LOGD("Created and added port to component: " - "%![comp-]+c, %![port-]+p", component, new_port); - - *port = new_port; - status = BT_SELF_COMPONENT_STATUS_OK; - - goto end; -error: - /* - * We need to release the reference that we would otherwise have - * returned to the caller. - */ - BT_PORT_PUT_REF_AND_RESET(new_port); - -end: - return status; -} - -BT_HIDDEN -uint64_t bt_component_get_input_port_count(const struct bt_component *comp) -{ - BT_ASSERT_PRE_NON_NULL(comp, "Component"); - return (uint64_t) comp->input_ports->len; -} - -BT_HIDDEN -uint64_t bt_component_get_output_port_count(const struct bt_component *comp) -{ - BT_ASSERT_PRE_NON_NULL(comp, "Component"); - return (uint64_t) comp->output_ports->len; -} - -BT_HIDDEN -int bt_component_create(struct bt_component_class *component_class, - const char *name, struct bt_component **user_component) -{ - int ret = 0; - struct bt_component *component = NULL; - enum bt_component_class_type type; - - BT_ASSERT(user_component); - BT_ASSERT(component_class); - BT_ASSERT(name); - type = bt_component_class_get_type(component_class); - BT_LIB_LOGD("Creating empty component from component class: %![cc-]+C, " - "comp-name=\"%s\"", component_class, name); - component = component_create_funcs[type](component_class); - if (!component) { - BT_LOGE_STR("Cannot create specific component object."); - ret = -1; - goto end; - } - - bt_object_init_shared_with_parent(&component->base, destroy_component); - component->class = component_class; - bt_object_get_no_null_check(component->class); - component->destroy = component_destroy_funcs[type]; - component->name = g_string_new(name); - if (!component->name) { - BT_LOGE_STR("Failed to allocate one GString."); - ret = -1; - goto end; - } - - component->input_ports = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!component->input_ports) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - ret = -1; - goto end; - } - - component->output_ports = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!component->output_ports) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - ret = -1; - goto end; - } - - component->destroy_listeners = g_array_new(FALSE, TRUE, - sizeof(struct bt_component_destroy_listener)); - if (!component->destroy_listeners) { - BT_LOGE_STR("Failed to allocate one GArray."); - ret = -1; - goto end; - } - - BT_LIB_LOGD("Created empty component from component class: " - "%![cc-]+C, %![comp-]+c", component_class, component); - BT_OBJECT_MOVE_REF(*user_component, component); - -end: - bt_object_put_ref(component); - return ret; -} - -const char *bt_component_get_name(const struct bt_component *component) -{ - BT_ASSERT_PRE_NON_NULL(component, "Component"); - return component->name->str; -} - -const struct bt_component_class *bt_component_borrow_class_const( - const struct bt_component *component) -{ - BT_ASSERT_PRE_NON_NULL(component, "Component"); - return component->class; -} - -void *bt_self_component_get_data(const struct bt_self_component *self_comp) -{ - struct bt_component *component = (void *) self_comp; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - return component->user_data; -} - -void bt_self_component_set_data(struct bt_self_component *self_comp, - void *data) -{ - struct bt_component *component = (void *) self_comp; - - BT_ASSERT_PRE_NON_NULL(component, "Component"); - component->user_data = data; - BT_LIB_LOGV("Set component's user data: %!+c", component); -} - -BT_HIDDEN -void bt_component_set_graph(struct bt_component *component, - struct bt_graph *graph) -{ - bt_object_set_parent(&component->base, - graph ? &graph->base : NULL); -} - -bt_bool bt_component_graph_is_canceled(const struct bt_component *component) -{ - return bt_graph_is_canceled( - (void *) bt_object_borrow_parent(&component->base)); -} - -static -struct bt_port *borrow_port_by_name(GPtrArray *ports, - const char *name) -{ - uint64_t i; - struct bt_port *ret_port = NULL; - - BT_ASSERT(name); - - for (i = 0; i < ports->len; i++) { - struct bt_port *port = g_ptr_array_index(ports, i); - - if (!strcmp(name, port->name->str)) { - ret_port = port; - break; - } - } - - return ret_port; -} - -BT_HIDDEN -struct bt_port_input *bt_component_borrow_input_port_by_name( - struct bt_component *comp, const char *name) -{ - BT_ASSERT(comp); - return (void *) borrow_port_by_name(comp->input_ports, name); -} - -BT_HIDDEN -struct bt_port_output *bt_component_borrow_output_port_by_name( - struct bt_component *comp, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(comp, "Component"); - return (void *) - borrow_port_by_name(comp->output_ports, name); -} - -static -struct bt_port *borrow_port_by_index(GPtrArray *ports, uint64_t index) -{ - BT_ASSERT(index < ports->len); - return g_ptr_array_index(ports, index); -} - -BT_HIDDEN -struct bt_port_input *bt_component_borrow_input_port_by_index( - struct bt_component *comp, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(comp, "Component"); - BT_ASSERT_PRE_VALID_INDEX(index, comp->input_ports->len); - return (void *) - borrow_port_by_index(comp->input_ports, index); -} - -BT_HIDDEN -struct bt_port_output *bt_component_borrow_output_port_by_index( - struct bt_component *comp, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(comp, "Component"); - BT_ASSERT_PRE_VALID_INDEX(index, comp->output_ports->len); - return (void *) - borrow_port_by_index(comp->output_ports, index); -} - -BT_HIDDEN -enum bt_self_component_status bt_component_add_input_port( - struct bt_component *component, const char *name, - void *user_data, struct bt_port **port) -{ - /* add_port() logs details */ - return add_port(component, component->input_ports, - BT_PORT_TYPE_INPUT, name, user_data, port); -} - -BT_HIDDEN -enum bt_self_component_status bt_component_add_output_port( - struct bt_component *component, const char *name, - void *user_data, struct bt_port **port) -{ - /* add_port() logs details */ - return add_port(component, component->output_ports, - BT_PORT_TYPE_OUTPUT, name, user_data, port); -} - -BT_HIDDEN -enum bt_self_component_status bt_component_accept_port_connection( - struct bt_component *comp, struct bt_port *self_port, - struct bt_port *other_port) -{ - typedef enum bt_self_component_status (*method_t)( - void *, void *, const void *); - - enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - method_t method = NULL; - - BT_ASSERT(comp); - BT_ASSERT(self_port); - BT_ASSERT(other_port); - - switch (comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_OUTPUT: - method = (method_t) src_cc->methods.accept_output_port_connection; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_INPUT: - method = (method_t) flt_cc->methods.accept_input_port_connection; - break; - case BT_PORT_TYPE_OUTPUT: - method = (method_t) flt_cc->methods.accept_output_port_connection; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_SINK: - { - struct bt_component_class_sink *sink_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_INPUT: - method = (method_t) sink_cc->methods.accept_input_port_connection; - break; - default: - abort(); - } - - break; - } - default: - abort(); - } - - if (method) { - BT_LIB_LOGD("Calling user's \"accept port connection\" method: " - "%![comp-]+c, %![self-port-]+p, %![other-port-]+p", - comp, self_port, other_port); - status = method(comp, self_port, (void *) other_port); - BT_LOGD("User method returned: status=%s", - bt_self_component_status_string(status)); - } - - return status; -} - -BT_HIDDEN -enum bt_self_component_status bt_component_port_connected( - struct bt_component *comp, struct bt_port *self_port, - struct bt_port *other_port) -{ - typedef enum bt_self_component_status (*method_t)( - void *, void *, const void *); - - enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - method_t method = NULL; - - BT_ASSERT(comp); - BT_ASSERT(self_port); - BT_ASSERT(other_port); - - switch (comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_OUTPUT: - method = (method_t) src_cc->methods.output_port_connected; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_INPUT: - method = (method_t) flt_cc->methods.input_port_connected; - break; - case BT_PORT_TYPE_OUTPUT: - method = (method_t) flt_cc->methods.output_port_connected; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_SINK: - { - struct bt_component_class_sink *sink_cc = (void *) comp->class; - - switch (self_port->type) { - case BT_PORT_TYPE_INPUT: - method = (method_t) sink_cc->methods.input_port_connected; - break; - default: - abort(); - } - - break; - } - default: - abort(); - } - - if (method) { - BT_LIB_LOGD("Calling user's \"port connected\" method: " - "%![comp-]+c, %![self-port-]+p, %![other-port-]+p", - comp, self_port, other_port); - status = method(comp, self_port, (void *) other_port); - BT_LOGD("User method returned: status=%s", - bt_self_component_status_string(status)); - BT_ASSERT_PRE(status == BT_SELF_COMPONENT_STATUS_OK || - status == BT_SELF_COMPONENT_STATUS_ERROR || - status == BT_SELF_COMPONENT_STATUS_NOMEM, - "Unexpected returned component status: status=%s", - bt_self_component_status_string(status)); - } - - return status; -} - -BT_HIDDEN -void bt_component_add_destroy_listener(struct bt_component *component, - bt_component_destroy_listener_func func, void *data) -{ - struct bt_component_destroy_listener listener; - - BT_ASSERT(component); - BT_ASSERT(func); - listener.func = func; - listener.data = data; - g_array_append_val(component->destroy_listeners, listener); - BT_LIB_LOGV("Added destroy listener: %![comp-]+c, " - "func-addr=%p, data-addr=%p", - component, func, data); -} - -BT_HIDDEN -void bt_component_remove_destroy_listener(struct bt_component *component, - bt_component_destroy_listener_func func, void *data) -{ - uint64_t i; - - BT_ASSERT(component); - BT_ASSERT(func); - - for (i = 0; i < component->destroy_listeners->len; i++) { - struct bt_component_destroy_listener *listener = - &g_array_index(component->destroy_listeners, - struct bt_component_destroy_listener, i); - - if (listener->func == func && listener->data == data) { - g_array_remove_index(component->destroy_listeners, i); - i--; - BT_LIB_LOGV("Removed destroy listener: %![comp-]+c, " - "func-addr=%p, data-addr=%p", - component, func, data); - } - } -} - -void bt_component_get_ref(const struct bt_component *component) -{ - bt_object_get_ref(component); -} - -void bt_component_put_ref(const struct bt_component *component) -{ - bt_object_put_ref(component); -} diff --git a/lib/graph/connection.c b/lib/graph/connection.c deleted file mode 100644 index 00b3a2a7..00000000 --- a/lib/graph/connection.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CONNECTION" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_connection(struct bt_object *obj) -{ - struct bt_connection *connection = container_of(obj, - struct bt_connection, base); - - BT_LIB_LOGD("Destroying connection: %!+x", connection); - - /* - * Make sure that each message iterator which was created for - * this connection is finalized before we destroy it. Once a - * message iterator is finalized, all its method return NULL or - * the BT_MESSAGE_ITERATOR_STATUS_CANCELED status. - * - * Because connections are destroyed before components within a - * graph, this ensures that message iterators are always - * finalized before their upstream component. - * - * Ending the connection does exactly this. We pass `false` to - * bt_connection_end() here to avoid removing this connection - * from the graph: if we're here, we're already in the graph's - * destructor. - */ - bt_connection_end(connection, false); - g_ptr_array_free(connection->iterators, TRUE); - connection->iterators = NULL; - - /* - * No bt_object_put_ref on ports as a connection only holds _weak_ - * references to them. - */ - g_free(connection); -} - -static -void try_remove_connection_from_graph(struct bt_connection *connection) -{ - void *graph = (void *) bt_object_borrow_parent(&connection->base); - - if (connection->base.ref_count > 0 || - connection->downstream_port || - connection->upstream_port || - connection->iterators->len > 0) { - return; - } - - /* - * At this point we know that: - * - * 1. The connection is ended (ports were disconnected). - * 2. All the message iterators that this connection - * created, if any, are finalized. - * 3. The connection's reference count is 0, so only the - * parent (graph) owns this connection after this call. - * - * In other words, no other object than the graph knows this - * connection. - * - * It is safe to remove the connection from the graph, therefore - * destroying it. - */ - BT_LIB_LOGD("Removing self from graph's connections: " - "%![graph-]+g, %![conn-]+x", graph, connection); - bt_graph_remove_connection(graph, connection); -} - -static -void parent_is_owner(struct bt_object *obj) -{ - struct bt_connection *connection = container_of(obj, - struct bt_connection, base); - - try_remove_connection_from_graph(connection); -} - -BT_HIDDEN -struct bt_connection *bt_connection_create(struct bt_graph *graph, - struct bt_port *upstream_port, - struct bt_port *downstream_port) -{ - struct bt_connection *connection = NULL; - - BT_LIB_LOGD("Creating connection: " - "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", - graph, upstream_port, downstream_port); - connection = g_new0(struct bt_connection, 1); - if (!connection) { - BT_LOGE_STR("Failed to allocate one connection."); - goto end; - } - - bt_object_init_shared_with_parent(&connection->base, - destroy_connection); - bt_object_set_parent_is_owner_listener_func(&connection->base, - parent_is_owner); - connection->iterators = g_ptr_array_new(); - if (!connection->iterators) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - BT_OBJECT_PUT_REF_AND_RESET(connection); - goto end; - } - - /* Weak references are taken, see comment in header. */ - connection->upstream_port = upstream_port; - connection->downstream_port = downstream_port; - BT_LIB_LOGD("Setting upstream port's connection: %!+p", upstream_port); - bt_port_set_connection(upstream_port, connection); - BT_LIB_LOGD("Setting downstream port's connection: %!+p", - downstream_port); - bt_port_set_connection(downstream_port, connection); - bt_object_set_parent(&connection->base, &graph->base); - BT_LIB_LOGD("Created connection: %!+x", connection); - -end: - return connection; -} - -BT_HIDDEN -void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph) -{ - struct bt_port *downstream_port = conn->downstream_port; - struct bt_port *upstream_port = conn->upstream_port; - size_t i; - - BT_LIB_LOGD("Ending connection: %!+x, try-remove-from-graph=%d", - conn, try_remove_from_graph); - - /* - * Any of the following message callback functions could - * remove one of the connection's ports from its component. To - * make sure that at least logging in called functions works - * with existing objects, get a local reference on both ports. - */ - bt_object_get_ref(downstream_port); - bt_object_get_ref(upstream_port); - - if (downstream_port) { - BT_LIB_LOGD("Disconnecting connection's downstream port: %!+p", - downstream_port); - bt_port_set_connection(downstream_port, NULL); - conn->downstream_port = NULL; - } - - if (upstream_port) { - BT_LIB_LOGD("Disconnecting connection's upstream port: %!+p", - upstream_port); - bt_port_set_connection(upstream_port, NULL); - conn->upstream_port = NULL; - } - - /* - * It is safe to put the local port references now that we don't - * need them anymore. This could indeed destroy them. - */ - bt_object_put_ref(downstream_port); - bt_object_put_ref(upstream_port); - - /* - * Because this connection is ended, finalize each message - * iterator created from it. - * - * In practice, this only happens when the connection is - * destroyed and not all its message iterators were finalized, - * which is on graph destruction. - */ - for (i = 0; i < conn->iterators->len; i++) { - struct bt_self_component_port_input_message_iterator *iterator = - g_ptr_array_index(conn->iterators, i); - - BT_LIB_LOGD("Finalizing message iterator created by " - "this ended connection: %![iter-]+i", iterator); - bt_self_component_port_input_message_iterator_try_finalize( - iterator); - - /* - * Make sure this iterator does not try to remove itself - * from this connection's iterators on destruction - * because this connection won't exist anymore. - */ - bt_self_component_port_input_message_iterator_set_connection( - iterator, NULL); - } - - g_ptr_array_set_size(conn->iterators, 0); - - if (try_remove_from_graph) { - try_remove_connection_from_graph(conn); - } -} - -const struct bt_port_output *bt_connection_borrow_upstream_port_const( - const struct bt_connection *connection) -{ - BT_ASSERT_PRE_NON_NULL(connection, "Connection"); - return (void *) connection->upstream_port; -} - -const struct bt_port_input *bt_connection_borrow_downstream_port_const( - const struct bt_connection *connection) -{ - BT_ASSERT_PRE_NON_NULL(connection, "Connection"); - return (void *) connection->downstream_port; -} - -BT_HIDDEN -void bt_connection_remove_iterator(struct bt_connection *conn, - struct bt_self_component_port_input_message_iterator *iterator) -{ - g_ptr_array_remove(conn->iterators, iterator); - BT_LIB_LOGV("Removed message iterator from connection: " - "%![conn-]+x, %![iter-]+i", conn, iterator); - try_remove_connection_from_graph(conn); -} - -void bt_connection_get_ref(const struct bt_connection *connection) -{ - bt_object_get_ref(connection); -} - -void bt_connection_put_ref(const struct bt_connection *connection) -{ - bt_object_put_ref(connection); -} diff --git a/lib/graph/graph.c b/lib/graph/graph.c deleted file mode 100644 index f7b02a10..00000000 --- a/lib/graph/graph.c +++ /dev/null @@ -1,1556 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "GRAPH" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef enum bt_graph_listener_status (*port_added_func_t)( - const void *, const void *, void *); - -typedef enum bt_graph_listener_status (*ports_connected_func_t)( - const void *, const void *, const void *, const void *, void *); - -typedef enum bt_self_component_status (*comp_init_method_t)(const void *, - const void *, void *); - -struct bt_graph_listener { - bt_graph_listener_removed_func removed; - void *data; -}; - -struct bt_graph_listener_port_added { - struct bt_graph_listener base; - port_added_func_t func; -}; - -struct bt_graph_listener_ports_connected { - struct bt_graph_listener base; - ports_connected_func_t func; -}; - -#define INIT_LISTENERS_ARRAY(_type, _listeners) \ - do { \ - _listeners = g_array_new(FALSE, TRUE, sizeof(_type)); \ - if (!(_listeners)) { \ - BT_LOGE_STR("Failed to allocate one GArray."); \ - } \ - } while (0) - -#define CALL_REMOVE_LISTENERS(_type, _listeners) \ - do { \ - size_t i; \ - \ - if (!_listeners) { \ - break; \ - } \ - for (i = 0; i < (_listeners)->len; i++) { \ - _type *listener = \ - &g_array_index((_listeners), _type, i); \ - \ - if (listener->base.removed) { \ - listener->base.removed(listener->base.data); \ - } \ - } \ - } while (0) - -static -void destroy_graph(struct bt_object *obj) -{ - struct bt_graph *graph = container_of(obj, struct bt_graph, base); - - /* - * The graph's reference count is 0 if we're here. Increment - * it to avoid a double-destroy (possibly infinitely recursive) - * in this situation: - * - * 1. We put and destroy a connection. - * 2. This connection's destructor finalizes its active message - * iterators. - * 3. A message iterator's finalization function gets a new - * reference on its component (reference count goes from 0 to - * 1). - * 4. Since this component's reference count goes to 1, it takes - * a reference on its parent (this graph). This graph's - * reference count goes from 0 to 1. - * 5. The message iterator's finalization function puts its - * component reference (reference count goes from 1 to 0). - * 6. Since this component's reference count goes from 1 to 0, - * it puts its parent (this graph). This graph's reference - * count goes from 1 to 0. - * 7. Since this graph's reference count goes from 1 to 0, its - * destructor is called (this function). - * - * With the incrementation below, the graph's reference count at - * step 4 goes from 1 to 2, and from 2 to 1 at step 6. This - * ensures that this function is not called two times. - */ - BT_LIB_LOGD("Destroying graph: %!+g", graph); - obj->ref_count++; - - /* - * Cancel the graph to disallow some operations, like creating - * message iterators and adding ports to components. - */ - (void) bt_graph_cancel((void *) graph); - - /* Call all remove listeners */ - CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, - graph->listeners.source_output_port_added); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, - graph->listeners.filter_output_port_added); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, - graph->listeners.filter_input_port_added); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, - graph->listeners.sink_input_port_added); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, - graph->listeners.source_filter_ports_connected); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, - graph->listeners.filter_filter_ports_connected); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, - graph->listeners.source_sink_ports_connected); - CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, - graph->listeners.filter_sink_ports_connected); - - if (graph->messages) { - g_ptr_array_free(graph->messages, TRUE); - graph->messages = NULL; - } - - if (graph->connections) { - BT_LOGD_STR("Destroying connections."); - g_ptr_array_free(graph->connections, TRUE); - graph->connections = NULL; - } - - if (graph->components) { - BT_LOGD_STR("Destroying components."); - g_ptr_array_free(graph->components, TRUE); - graph->components = NULL; - } - - if (graph->sinks_to_consume) { - g_queue_free(graph->sinks_to_consume); - graph->sinks_to_consume = NULL; - } - - if (graph->listeners.source_output_port_added) { - g_array_free(graph->listeners.source_output_port_added, TRUE); - graph->listeners.source_output_port_added = NULL; - } - - if (graph->listeners.filter_output_port_added) { - g_array_free(graph->listeners.filter_output_port_added, TRUE); - graph->listeners.filter_output_port_added = NULL; - } - - if (graph->listeners.filter_input_port_added) { - g_array_free(graph->listeners.filter_input_port_added, TRUE); - graph->listeners.filter_input_port_added = NULL; - } - - if (graph->listeners.sink_input_port_added) { - g_array_free(graph->listeners.sink_input_port_added, TRUE); - graph->listeners.sink_input_port_added = NULL; - } - - if (graph->listeners.source_filter_ports_connected) { - g_array_free(graph->listeners.source_filter_ports_connected, - TRUE); - graph->listeners.source_filter_ports_connected = NULL; - } - - if (graph->listeners.filter_filter_ports_connected) { - g_array_free(graph->listeners.filter_filter_ports_connected, - TRUE); - graph->listeners.filter_filter_ports_connected = NULL; - } - - if (graph->listeners.source_sink_ports_connected) { - g_array_free(graph->listeners.source_sink_ports_connected, - TRUE); - graph->listeners.source_sink_ports_connected = NULL; - } - - if (graph->listeners.filter_sink_ports_connected) { - g_array_free(graph->listeners.filter_sink_ports_connected, - TRUE); - graph->listeners.filter_sink_ports_connected = NULL; - } - - bt_object_pool_finalize(&graph->event_msg_pool); - bt_object_pool_finalize(&graph->packet_begin_msg_pool); - bt_object_pool_finalize(&graph->packet_end_msg_pool); - g_free(graph); -} - -static -void destroy_message_event(struct bt_message *msg, - struct bt_graph *graph) -{ - bt_message_event_destroy(msg); -} - -static -void destroy_message_packet_begin(struct bt_message *msg, - struct bt_graph *graph) -{ - bt_message_packet_destroy(msg); -} - -static -void destroy_message_packet_end(struct bt_message *msg, - struct bt_graph *graph) -{ - bt_message_packet_destroy(msg); -} - -static -void notify_message_graph_is_destroyed(struct bt_message *msg) -{ - bt_message_unlink_graph(msg); -} - -struct bt_graph *bt_graph_create(void) -{ - struct bt_graph *graph; - int ret; - - BT_LOGD_STR("Creating graph object."); - graph = g_new0(struct bt_graph, 1); - if (!graph) { - BT_LOGE_STR("Failed to allocate one graph."); - goto end; - } - - bt_object_init_shared(&graph->base, destroy_graph); - graph->connections = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!graph->connections) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - graph->components = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!graph->components) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - graph->sinks_to_consume = g_queue_new(); - if (!graph->sinks_to_consume) { - BT_LOGE_STR("Failed to allocate one GQueue."); - goto error; - } - - bt_graph_set_can_consume(graph, true); - INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, - graph->listeners.source_output_port_added); - - if (!graph->listeners.source_output_port_added) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, - graph->listeners.filter_output_port_added); - - if (!graph->listeners.filter_output_port_added) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, - graph->listeners.filter_input_port_added); - - if (!graph->listeners.filter_input_port_added) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, - graph->listeners.sink_input_port_added); - - if (!graph->listeners.sink_input_port_added) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, - graph->listeners.source_filter_ports_connected); - - if (!graph->listeners.source_filter_ports_connected) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, - graph->listeners.source_sink_ports_connected); - - if (!graph->listeners.source_sink_ports_connected) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, - graph->listeners.filter_filter_ports_connected); - - if (!graph->listeners.filter_filter_ports_connected) { - ret = -1; - goto error; - } - - INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, - graph->listeners.filter_sink_ports_connected); - - if (!graph->listeners.filter_sink_ports_connected) { - ret = -1; - goto error; - } - - ret = bt_object_pool_initialize(&graph->event_msg_pool, - (bt_object_pool_new_object_func) bt_message_event_new, - (bt_object_pool_destroy_object_func) destroy_message_event, - graph); - if (ret) { - BT_LOGE("Failed to initialize event message pool: ret=%d", - ret); - goto error; - } - - ret = bt_object_pool_initialize(&graph->packet_begin_msg_pool, - (bt_object_pool_new_object_func) bt_message_packet_beginning_new, - (bt_object_pool_destroy_object_func) destroy_message_packet_begin, - graph); - if (ret) { - BT_LOGE("Failed to initialize packet beginning message pool: ret=%d", - ret); - goto error; - } - - ret = bt_object_pool_initialize(&graph->packet_end_msg_pool, - (bt_object_pool_new_object_func) bt_message_packet_end_new, - (bt_object_pool_destroy_object_func) destroy_message_packet_end, - graph); - if (ret) { - BT_LOGE("Failed to initialize packet end message pool: ret=%d", - ret); - goto error; - } - - graph->messages = g_ptr_array_new_with_free_func( - (GDestroyNotify) notify_message_graph_is_destroyed); - BT_LIB_LOGD("Created graph object: %!+g", graph); - -end: - return (void *) graph; - -error: - BT_OBJECT_PUT_REF_AND_RESET(graph); - goto end; -} - -enum bt_graph_status bt_graph_connect_ports( - struct bt_graph *graph, - const struct bt_port_output *upstream_port_out, - const struct bt_port_input *downstream_port_in, - const struct bt_connection **user_connection) -{ - enum bt_graph_status status = BT_GRAPH_STATUS_OK; - enum bt_graph_listener_status listener_status; - struct bt_connection *connection = NULL; - struct bt_port *upstream_port = (void *) upstream_port_out; - struct bt_port *downstream_port = (void *) downstream_port_in; - struct bt_component *upstream_component = NULL; - struct bt_component *downstream_component = NULL; - enum bt_self_component_status component_status; - bool init_can_consume; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(upstream_port, "Upstream port"); - BT_ASSERT_PRE_NON_NULL(downstream_port, "Downstream port port"); - BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); - BT_ASSERT_PRE( - graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not in the \"configuring\" state: %!+g", graph); - BT_ASSERT_PRE(!bt_port_is_connected(upstream_port), - "Upstream port is already connected: %!+p", upstream_port); - BT_ASSERT_PRE(!bt_port_is_connected(downstream_port), - "Downstream port is already connected: %!+p", downstream_port); - BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) upstream_port), - "Upstream port does not belong to a component: %!+p", - upstream_port); - BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) downstream_port), - "Downstream port does not belong to a component: %!+p", - downstream_port); - init_can_consume = graph->can_consume; - BT_LIB_LOGD("Connecting component ports within graph: " - "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", - graph, upstream_port, downstream_port); - bt_graph_set_can_consume(graph, false); - upstream_component = bt_port_borrow_component_inline( - (void *) upstream_port); - downstream_component = bt_port_borrow_component_inline( - (void *) downstream_port); - - /* - * At this point the ports are not connected yet. Both - * components need to accept an eventual connection to their - * port by the other port before we continue. - */ - BT_LIB_LOGD("Asking upstream component to accept the connection: " - "%![comp-]+c", upstream_component); - component_status = bt_component_accept_port_connection( - upstream_component, (void *) upstream_port, - (void *) downstream_port); - if (component_status != BT_SELF_COMPONENT_STATUS_OK) { - if (component_status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { - BT_LOGD_STR("Upstream component refused the connection."); - } else { - BT_LOGW("Cannot ask upstream component to accept the connection: " - "status=%s", bt_self_component_status_string(component_status)); - } - - status = (int) component_status; - goto end; - } - - BT_LIB_LOGD("Asking downstream component to accept the connection: " - "%![comp-]+c", downstream_component); - component_status = bt_component_accept_port_connection( - downstream_component, (void *) downstream_port, - (void *) upstream_port); - if (component_status != BT_SELF_COMPONENT_STATUS_OK) { - if (component_status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { - BT_LOGD_STR("Downstream component refused the connection."); - } else { - BT_LOGW("Cannot ask downstream component to accept the connection: " - "status=%s", bt_self_component_status_string(component_status)); - } - - status = (int) component_status; - goto end; - } - - BT_LOGD_STR("Creating connection."); - connection = bt_connection_create(graph, (void *) upstream_port, - (void *) downstream_port); - if (!connection) { - BT_LOGW("Cannot create connection object."); - status = BT_GRAPH_STATUS_NOMEM; - goto end; - } - - BT_LIB_LOGD("Connection object created: %!+x", connection); - - /* - * Ownership of upstream_component/downstream_component and of - * the connection object is transferred to the graph. - */ - g_ptr_array_add(graph->connections, connection); - - /* - * Notify both components that their port is connected. - */ - BT_LIB_LOGD("Notifying upstream component that its port is connected: " - "%![comp-]+c, %![port-]+p", upstream_component, upstream_port); - component_status = bt_component_port_connected(upstream_component, - (void *) upstream_port, (void *) downstream_port); - if (component_status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LIB_LOGW("Error while notifying upstream component that its port is connected: " - "status=%s, %![graph-]+g, %![up-comp-]+c, " - "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", - bt_self_component_status_string(component_status), - graph, upstream_component, downstream_component, - upstream_port, downstream_port); - bt_connection_end(connection, true); - status = (int) component_status; - goto end; - } - - connection->notified_upstream_port_connected = true; - BT_LIB_LOGD("Notifying downstream component that its port is connected: " - "%![comp-]+c, %![port-]+p", downstream_component, - downstream_port); - component_status = bt_component_port_connected(downstream_component, - (void *) downstream_port, (void *) upstream_port); - if (component_status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LIB_LOGW("Error while notifying downstream component that its port is connected: " - "status=%s, %![graph-]+g, %![up-comp-]+c, " - "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", - bt_self_component_status_string(component_status), - graph, upstream_component, downstream_component, - upstream_port, downstream_port); - bt_connection_end(connection, true); - status = (int) component_status; - goto end; - } - - connection->notified_downstream_port_connected = true; - - /* - * Notify the graph's creator that both ports are connected. - */ - BT_LOGD_STR("Notifying graph's user that new component ports are connected."); - listener_status = bt_graph_notify_ports_connected(graph, upstream_port, downstream_port); - if (listener_status != BT_GRAPH_LISTENER_STATUS_OK) { - status = (int) listener_status; - goto end; - } - - connection->notified_graph_ports_connected = true; - BT_LIB_LOGD("Connected component ports within graph: " - "%![graph-]+g, %![up-comp-]+c, %![down-comp-]+c, " - "%![up-port-]+p, %![down-port-]+p", - graph, upstream_component, downstream_component, - upstream_port, downstream_port); - - if (user_connection) { - /* Move reference to user */ - *user_connection = connection; - connection = NULL; - } - -end: - if (status != BT_GRAPH_STATUS_OK) { - bt_graph_make_faulty(graph); - } - - bt_object_put_ref(connection); - (void) init_can_consume; - bt_graph_set_can_consume(graph, init_can_consume); - return status; -} - -static inline -enum bt_graph_status consume_graph_sink(struct bt_component_sink *comp) -{ - enum bt_self_component_status comp_status; - struct bt_component_class_sink *sink_class = NULL; - - BT_ASSERT(comp); - sink_class = (void *) comp->parent.class; - BT_ASSERT(sink_class->methods.consume); - BT_LIB_LOGD("Calling user's consume method: %!+c", comp); - comp_status = sink_class->methods.consume((void *) comp); - BT_LOGD("User method returned: status=%s", - bt_self_component_status_string(comp_status)); - BT_ASSERT_PRE(comp_status == BT_SELF_COMPONENT_STATUS_OK || - comp_status == BT_SELF_COMPONENT_STATUS_END || - comp_status == BT_SELF_COMPONENT_STATUS_AGAIN || - comp_status == BT_SELF_COMPONENT_STATUS_ERROR || - comp_status == BT_SELF_COMPONENT_STATUS_NOMEM, - "Invalid component status returned by consuming method: " - "status=%s", bt_self_component_status_string(comp_status)); - if (comp_status < 0) { - BT_LOGW_STR("Consume method failed."); - goto end; - } - - BT_LIB_LOGV("Consumed from sink: %![comp-]+c, status=%s", - comp, bt_self_component_status_string(comp_status)); - -end: - return (int) comp_status; -} - -/* - * `node` is removed from the queue of sinks to consume when passed to - * this function. This function adds it back to the queue if there's - * still something to consume afterwards. - */ -static inline -enum bt_graph_status consume_sink_node(struct bt_graph *graph, GList *node) -{ - enum bt_graph_status status; - struct bt_component_sink *sink; - - sink = node->data; - status = consume_graph_sink(sink); - if (unlikely(status != BT_GRAPH_STATUS_END)) { - g_queue_push_tail_link(graph->sinks_to_consume, node); - goto end; - } - - /* End reached, the node is not added back to the queue and free'd. */ - g_queue_delete_link(graph->sinks_to_consume, node); - - /* Don't forward an END status if there are sinks left to consume. */ - if (!g_queue_is_empty(graph->sinks_to_consume)) { - status = BT_GRAPH_STATUS_OK; - goto end; - } - -end: - BT_LIB_LOGV("Consumed sink node: %![comp-]+c, status=%s", - sink, bt_graph_status_string(status)); - return status; -} - -BT_HIDDEN -enum bt_graph_status bt_graph_consume_sink_no_check(struct bt_graph *graph, - struct bt_component_sink *sink) -{ - enum bt_graph_status status; - GList *sink_node; - int index; - - BT_LIB_LOGV("Making specific sink consume: %![comp-]+c", sink); - BT_ASSERT(bt_component_borrow_graph((void *) sink) == graph); - - if (g_queue_is_empty(graph->sinks_to_consume)) { - BT_LOGV_STR("Graph's sink queue is empty: end of graph."); - status = BT_GRAPH_STATUS_END; - goto end; - } - - index = g_queue_index(graph->sinks_to_consume, sink); - if (index < 0) { - BT_LOGV_STR("Sink is not marked as consumable: sink is ended."); - status = BT_GRAPH_STATUS_END; - goto end; - } - - sink_node = g_queue_pop_nth_link(graph->sinks_to_consume, index); - BT_ASSERT(sink_node); - status = consume_sink_node(graph, sink_node); - -end: - return status; -} - -static inline -enum bt_graph_status consume_no_check(struct bt_graph *graph) -{ - enum bt_graph_status status = BT_GRAPH_STATUS_OK; - struct bt_component *sink; - GList *current_node; - - BT_ASSERT_PRE(graph->has_sink, - "Graph has no sink component: %!+g", graph); - BT_LIB_LOGV("Making next sink consume: %![graph-]+g", graph); - - if (unlikely(g_queue_is_empty(graph->sinks_to_consume))) { - BT_LOGV_STR("Graph's sink queue is empty: end of graph."); - status = BT_GRAPH_STATUS_END; - goto end; - } - - current_node = g_queue_pop_head_link(graph->sinks_to_consume); - sink = current_node->data; - BT_LIB_LOGV("Chose next sink to consume: %!+c", sink); - status = consume_sink_node(graph, current_node); - -end: - return status; -} - -enum bt_graph_status bt_graph_consume(struct bt_graph *graph) -{ - enum bt_graph_status status; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); - BT_ASSERT_PRE(graph->can_consume, - "Cannot consume graph in its current state: %!+g", graph); - BT_ASSERT_PRE(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY, - "Graph is in a faulty state: %!+g", graph); - bt_graph_set_can_consume(graph, false); - status = bt_graph_configure(graph); - if (unlikely(status)) { - /* bt_graph_configure() logs errors */ - goto end; - } - - status = consume_no_check(graph); - bt_graph_set_can_consume(graph, true); - -end: - return status; -} - -enum bt_graph_status bt_graph_run(struct bt_graph *graph) -{ - enum bt_graph_status status; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); - BT_ASSERT_PRE(graph->can_consume, - "Cannot consume graph in its current state: %!+g", graph); - BT_ASSERT_PRE(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY, - "Graph is in a faulty state: %!+g", graph); - bt_graph_set_can_consume(graph, false); - status = bt_graph_configure(graph); - if (unlikely(status)) { - /* bt_graph_configure() logs errors */ - goto end; - } - - BT_LIB_LOGV("Running graph: %!+g", graph); - - do { - /* - * Check if the graph is canceled at each iteration. If - * the graph was canceled by another thread or by a - * signal handler, this is not a warning nor an error, - * it was intentional: log with a DEBUG level only. - */ - if (unlikely(graph->canceled)) { - BT_LIB_LOGD("Stopping the graph: graph is canceled: " - "%!+g", graph); - status = BT_GRAPH_STATUS_CANCELED; - goto end; - } - - status = consume_no_check(graph); - if (unlikely(status == BT_GRAPH_STATUS_AGAIN)) { - /* - * If AGAIN is received and there are multiple - * sinks, go ahead and consume from the next - * sink. - * - * However, in the case where a single sink is - * left, the caller can decide to busy-wait and - * call bt_graph_run() continuously - * until the source is ready or it can decide to - * sleep for an arbitrary amount of time. - */ - if (graph->sinks_to_consume->length > 1) { - status = BT_GRAPH_STATUS_OK; - } - } - } while (status == BT_GRAPH_STATUS_OK); - - if (g_queue_is_empty(graph->sinks_to_consume)) { - status = BT_GRAPH_STATUS_END; - } - -end: - BT_LIB_LOGV("Graph ran: %![graph-]+g, status=%s", graph, - bt_graph_status_string(status)); - bt_graph_set_can_consume(graph, true); - return status; -} - -enum bt_graph_status -bt_graph_add_source_component_output_port_added_listener( - struct bt_graph *graph, - bt_graph_source_component_output_port_added_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_port_added listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (port_added_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.source_output_port_added, listener); - listener_id = graph->listeners.source_output_port_added->len - 1; - BT_LIB_LOGV("Added \"source component output port added\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_filter_component_output_port_added_listener( - struct bt_graph *graph, - bt_graph_filter_component_output_port_added_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_port_added listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (port_added_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.filter_output_port_added, listener); - listener_id = graph->listeners.filter_output_port_added->len - 1; - BT_LIB_LOGV("Added \"filter component output port added\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_filter_component_input_port_added_listener( - struct bt_graph *graph, - bt_graph_filter_component_input_port_added_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_port_added listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (port_added_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.filter_input_port_added, listener); - listener_id = graph->listeners.filter_input_port_added->len - 1; - BT_LIB_LOGV("Added \"filter component input port added\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_sink_component_input_port_added_listener( - struct bt_graph *graph, - bt_graph_sink_component_input_port_added_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_port_added listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (port_added_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.sink_input_port_added, listener); - listener_id = graph->listeners.sink_input_port_added->len - 1; - BT_LIB_LOGV("Added \"sink component input port added\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_source_filter_component_ports_connected_listener( - struct bt_graph *graph, - bt_graph_source_filter_component_ports_connected_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_ports_connected listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (ports_connected_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.source_filter_ports_connected, - listener); - listener_id = graph->listeners.source_filter_ports_connected->len - 1; - BT_LIB_LOGV("Added \"source to filter component ports connected\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_source_sink_component_ports_connected_listener( - struct bt_graph *graph, - bt_graph_source_sink_component_ports_connected_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_ports_connected listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (ports_connected_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.source_sink_ports_connected, - listener); - listener_id = graph->listeners.source_sink_ports_connected->len - 1; - BT_LIB_LOGV("Added \"source to sink component ports connected\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_filter_filter_component_ports_connected_listener( - struct bt_graph *graph, - bt_graph_filter_filter_component_ports_connected_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_ports_connected listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (ports_connected_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.filter_filter_ports_connected, - listener); - listener_id = graph->listeners.filter_filter_ports_connected->len - 1; - BT_LIB_LOGV("Added \"filter to filter component ports connected\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -enum bt_graph_status -bt_graph_add_filter_sink_component_ports_connected_listener( - struct bt_graph *graph, - bt_graph_filter_sink_component_ports_connected_listener_func func, - bt_graph_listener_removed_func listener_removed, void *data, - int *out_listener_id) -{ - struct bt_graph_listener_ports_connected listener = { - .base = { - .removed = listener_removed, - .data = data, - }, - .func = (ports_connected_func_t) func, - }; - int listener_id; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(func, "Listener"); - BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); - BT_ASSERT_PRE(!graph->in_remove_listener, - "Graph currently executing a \"listener removed\" listener: " - "%!+g", graph); - g_array_append_val(graph->listeners.filter_sink_ports_connected, - listener); - listener_id = graph->listeners.filter_sink_ports_connected->len - 1; - BT_LIB_LOGV("Added \"filter to sink component ports connected\" listener to graph: " - "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, - listener_id); - - if (listener_id) { - *out_listener_id = listener_id; - } - - return BT_GRAPH_STATUS_OK; -} - -BT_HIDDEN -enum bt_graph_listener_status bt_graph_notify_port_added( - struct bt_graph *graph, struct bt_port *port) -{ - uint64_t i; - GArray *listeners; - struct bt_component *comp; - enum bt_graph_listener_status status = BT_GRAPH_LISTENER_STATUS_OK; - - BT_ASSERT(graph); - BT_ASSERT(port); - BT_LIB_LOGV("Notifying graph listeners that a port was added: " - "%![graph-]+g, %![port-]+p", graph, port); - comp = bt_port_borrow_component_inline(port); - BT_ASSERT(comp); - - switch (comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - switch (port->type) { - case BT_PORT_TYPE_OUTPUT: - listeners = graph->listeners.source_output_port_added; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - switch (port->type) { - case BT_PORT_TYPE_INPUT: - listeners = graph->listeners.filter_input_port_added; - break; - case BT_PORT_TYPE_OUTPUT: - listeners = graph->listeners.filter_output_port_added; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_SINK: - { - switch (port->type) { - case BT_PORT_TYPE_INPUT: - listeners = graph->listeners.sink_input_port_added; - break; - default: - abort(); - } - - break; - } - default: - abort(); - } - - for (i = 0; i < listeners->len; i++) { - struct bt_graph_listener_port_added *listener = - &g_array_index(listeners, - struct bt_graph_listener_port_added, i); - - - BT_ASSERT(listener->func); - status = listener->func(comp, port, listener->base.data); - if (status != BT_GRAPH_LISTENER_STATUS_OK) { - goto end; - } - } - -end: - return status; -} - -BT_HIDDEN -enum bt_graph_listener_status bt_graph_notify_ports_connected( - struct bt_graph *graph, struct bt_port *upstream_port, - struct bt_port *downstream_port) -{ - uint64_t i; - GArray *listeners; - struct bt_component *upstream_comp; - struct bt_component *downstream_comp; - enum bt_graph_listener_status status = BT_GRAPH_LISTENER_STATUS_OK; - - BT_ASSERT(graph); - BT_ASSERT(upstream_port); - BT_ASSERT(downstream_port); - BT_LIB_LOGV("Notifying graph listeners that ports were connected: " - "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", - graph, upstream_port, downstream_port); - upstream_comp = bt_port_borrow_component_inline(upstream_port); - BT_ASSERT(upstream_comp); - downstream_comp = bt_port_borrow_component_inline(downstream_port); - BT_ASSERT(downstream_comp); - - switch (upstream_comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - switch (downstream_comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_FILTER: - listeners = - graph->listeners.source_filter_ports_connected; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - listeners = - graph->listeners.source_sink_ports_connected; - break; - default: - abort(); - } - - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - switch (downstream_comp->class->type) { - case BT_COMPONENT_CLASS_TYPE_FILTER: - listeners = - graph->listeners.filter_filter_ports_connected; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - listeners = - graph->listeners.filter_sink_ports_connected; - break; - default: - abort(); - } - - break; - } - default: - abort(); - } - - for (i = 0; i < listeners->len; i++) { - struct bt_graph_listener_ports_connected *listener = - &g_array_index(listeners, - struct bt_graph_listener_ports_connected, i); - - BT_ASSERT(listener->func); - status = listener->func(upstream_comp, downstream_comp, - upstream_port, downstream_port, listener->base.data); - if (status != BT_GRAPH_LISTENER_STATUS_OK) { - goto end; - } - } - -end: - return status; -} - -enum bt_graph_status bt_graph_cancel(struct bt_graph *graph) -{ - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - graph->canceled = true; - BT_LIB_LOGV("Canceled graph: %!+i", graph); - return BT_GRAPH_STATUS_OK; -} - -bt_bool bt_graph_is_canceled(const struct bt_graph *graph) -{ - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - return graph->canceled ? BT_TRUE : BT_FALSE; -} - -BT_HIDDEN -void bt_graph_remove_connection(struct bt_graph *graph, - struct bt_connection *connection) -{ - BT_ASSERT(graph); - BT_ASSERT(connection); - BT_LIB_LOGV("Removing graph's connection: %![graph-]+g, %![conn-]+x", - graph, connection); - g_ptr_array_remove(graph->connections, connection); -} - -BT_ASSERT_PRE_FUNC -static inline -bool component_name_exists(struct bt_graph *graph, const char *name) -{ - bool exists = false; - uint64_t i; - - for (i = 0; i < graph->components->len; i++) { - struct bt_component *other_comp = graph->components->pdata[i]; - - if (strcmp(name, bt_component_get_name(other_comp)) == 0) { - BT_ASSERT_PRE_MSG("Another component with the same name already exists in the graph: " - "%![other-comp-]+c, name=\"%s\"", - other_comp, name); - exists = true; - goto end; - } - } - -end: - return exists; -} - -static -enum bt_graph_status add_component_with_init_method_data( - struct bt_graph *graph, - struct bt_component_class *comp_cls, - comp_init_method_t init_method, - const char *name, const struct bt_value *params, - void *init_method_data, struct bt_component **user_component) -{ - enum bt_graph_status graph_status = BT_GRAPH_STATUS_OK; - enum bt_self_component_status comp_status; - struct bt_component *component = NULL; - int ret; - bool init_can_consume; - struct bt_value *new_params = NULL; - - BT_ASSERT(comp_cls); - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); - BT_ASSERT_PRE( - graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not in the \"configuring\" state: %!+g", graph); - BT_ASSERT_PRE(!component_name_exists(graph, name), - "Duplicate component name: %!+g, name=\"%s\"", graph, name); - BT_ASSERT_PRE(!params || bt_value_is_map(params), - "Parameter value is not a map value: %!+v", params); - init_can_consume = graph->can_consume; - bt_graph_set_can_consume(graph, false); - BT_LIB_LOGD("Adding component to graph: " - "%![graph-]+g, %![cc-]+C, name=\"%s\", %![params-]+v, " - "init-method-data-addr=%p", - graph, comp_cls, name, params, init_method_data); - - if (!params) { - new_params = bt_value_map_create(); - if (!new_params) { - BT_LOGE_STR("Cannot create map value object."); - graph_status = BT_GRAPH_STATUS_NOMEM; - goto end; - } - - params = new_params; - } - - ret = bt_component_create(comp_cls, name, &component); - if (ret) { - BT_LOGE("Cannot create empty component object: ret=%d", - ret); - graph_status = BT_GRAPH_STATUS_NOMEM; - goto end; - } - - /* - * The user's initialization method needs to see that this - * component is part of the graph. If the user method fails, we - * immediately remove the component from the graph's components. - */ - g_ptr_array_add(graph->components, component); - bt_component_set_graph(component, graph); - bt_value_freeze(params); - - if (init_method) { - BT_LOGD_STR("Calling user's initialization method."); - comp_status = init_method(component, params, init_method_data); - BT_LOGD("User method returned: status=%s", - bt_self_component_status_string(comp_status)); - if (comp_status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGW_STR("Initialization method failed."); - graph_status = (int) comp_status; - bt_component_set_graph(component, NULL); - g_ptr_array_remove_fast(graph->components, component); - goto end; - } - } - - /* - * Mark the component as initialized so that its finalization - * method is called when it is destroyed. - */ - component->initialized = true; - - /* - * If it's a sink component, it needs to be part of the graph's - * sink queue to be consumed by bt_graph_consume(). - */ - if (bt_component_is_sink(component)) { - graph->has_sink = true; - g_queue_push_tail(graph->sinks_to_consume, component); - } - - /* - * Freeze the component class now that it's instantiated at - * least once. - */ - BT_LOGD_STR("Freezing component class."); - bt_component_class_freeze(comp_cls); - BT_LIB_LOGD("Added component to graph: " - "%![graph-]+g, %![cc-]+C, name=\"%s\", %![params-]+v, " - "init-method-data-addr=%p, %![comp-]+c", - graph, comp_cls, name, params, init_method_data, component); - - if (user_component) { - /* Move reference to user */ - *user_component = component; - component = NULL; - } - -end: - if (graph_status != BT_GRAPH_STATUS_OK) { - bt_graph_make_faulty(graph); - } - - bt_object_put_ref(component); - bt_object_put_ref(new_params); - (void) init_can_consume; - bt_graph_set_can_consume(graph, init_can_consume); - return graph_status; -} - -enum bt_graph_status -bt_graph_add_source_component_with_init_method_data( - struct bt_graph *graph, - const struct bt_component_class_source *comp_cls, - const char *name, const struct bt_value *params, - void *init_method_data, - const struct bt_component_source **component) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return add_component_with_init_method_data(graph, - (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, - name, params, init_method_data, (void *) component); -} - -enum bt_graph_status bt_graph_add_source_component( - struct bt_graph *graph, - const struct bt_component_class_source *comp_cls, - const char *name, const struct bt_value *params, - const struct bt_component_source **component) -{ - return bt_graph_add_source_component_with_init_method_data( - graph, comp_cls, name, params, NULL, component); -} - -enum bt_graph_status -bt_graph_add_filter_component_with_init_method_data( - struct bt_graph *graph, - const struct bt_component_class_filter *comp_cls, - const char *name, const struct bt_value *params, - void *init_method_data, - const struct bt_component_filter **component) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return add_component_with_init_method_data(graph, - (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, - name, params, init_method_data, (void *) component); -} - -enum bt_graph_status bt_graph_add_filter_component( - struct bt_graph *graph, - const struct bt_component_class_filter *comp_cls, - const char *name, const struct bt_value *params, - const struct bt_component_filter **component) -{ - return bt_graph_add_filter_component_with_init_method_data( - graph, comp_cls, name, params, NULL, component); -} - -enum bt_graph_status -bt_graph_add_sink_component_with_init_method_data( - struct bt_graph *graph, - const struct bt_component_class_sink *comp_cls, - const char *name, const struct bt_value *params, - void *init_method_data, - const struct bt_component_sink **component) -{ - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - return add_component_with_init_method_data(graph, - (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, - name, params, init_method_data, (void *) component); -} - -enum bt_graph_status bt_graph_add_sink_component( - struct bt_graph *graph, - const struct bt_component_class_sink *comp_cls, - const char *name, const struct bt_value *params, - const struct bt_component_sink **component) -{ - return bt_graph_add_sink_component_with_init_method_data( - graph, comp_cls, name, params, NULL, component); -} - -BT_HIDDEN -int bt_graph_remove_unconnected_component(struct bt_graph *graph, - struct bt_component *component) -{ - bool init_can_consume; - uint64_t count; - uint64_t i; - int ret = 0; - - BT_ASSERT(graph); - BT_ASSERT(component); - BT_ASSERT(component->base.ref_count == 0); - BT_ASSERT(bt_component_borrow_graph(component) == graph); - - init_can_consume = graph->can_consume; - count = bt_component_get_input_port_count(component); - - for (i = 0; i < count; i++) { - struct bt_port *port = (void *) - bt_component_borrow_input_port_by_index(component, i); - - BT_ASSERT(port); - - if (bt_port_is_connected(port)) { - BT_LIB_LOGW("Cannot remove component from graph: " - "an input port is connected: " - "%![graph-]+g, %![comp-]+c, %![port-]+p", - graph, component, port); - goto error; - } - } - - count = bt_component_get_output_port_count(component); - - for (i = 0; i < count; i++) { - struct bt_port *port = (void *) - bt_component_borrow_output_port_by_index(component, i); - - BT_ASSERT(port); - - if (bt_port_is_connected(port)) { - BT_LIB_LOGW("Cannot remove component from graph: " - "an output port is connected: " - "%![graph-]+g, %![comp-]+c, %![port-]+p", - graph, component, port); - goto error; - } - } - - bt_graph_set_can_consume(graph, false); - - /* Possibly remove from sinks to consume */ - (void) g_queue_remove(graph->sinks_to_consume, component); - - if (graph->sinks_to_consume->length == 0) { - graph->has_sink = false; - } - - /* - * This calls bt_object_try_spec_release() on the component, and - * since its reference count is 0, its destructor is called. Its - * destructor calls the user's finalization method (if set). - */ - g_ptr_array_remove(graph->components, component); - goto end; - -error: - ret = -1; - -end: - (void) init_can_consume; - bt_graph_set_can_consume(graph, init_can_consume); - return ret; -} - -BT_HIDDEN -void bt_graph_add_message(struct bt_graph *graph, - struct bt_message *msg) -{ - BT_ASSERT(graph); - BT_ASSERT(msg); - - /* - * It's okay not to take a reference because, when a - * message's reference count drops to 0, either: - * - * * It is recycled back to one of this graph's pool. - * * It is destroyed because it doesn't have any link to any - * graph, which means the original graph is already destroyed. - */ - g_ptr_array_add(graph->messages, msg); -} - -void bt_graph_get_ref(const struct bt_graph *graph) -{ - bt_object_get_ref(graph); -} - -void bt_graph_put_ref(const struct bt_graph *graph) -{ - bt_object_put_ref(graph); -} diff --git a/lib/graph/iterator.c b/lib/graph/iterator.c deleted file mode 100644 index aec91f11..00000000 --- a/lib/graph/iterator.c +++ /dev/null @@ -1,1472 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-ITER" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * TODO: Use graph's state (number of active iterators, etc.) and - * possibly system specifications to make a better guess than this. - */ -#define MSG_BATCH_SIZE 15 - -#define BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(_iter) \ - BT_ASSERT_PRE((_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE || \ - (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED || \ - (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN || \ - (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR, \ - "Message iterator is in the wrong state: %!+i", _iter) - -static inline -void set_self_comp_port_input_msg_iterator_state( - struct bt_self_component_port_input_message_iterator *iterator, - enum bt_self_component_port_input_message_iterator_state state) -{ - BT_ASSERT(iterator); - BT_LIB_LOGD("Updating message iterator's state: new-state=%s", - bt_self_component_port_input_message_iterator_state_string(state)); - iterator->state = state; -} - -static -void destroy_base_message_iterator(struct bt_object *obj) -{ - struct bt_message_iterator *iterator = (void *) obj; - - BT_ASSERT(iterator); - - if (iterator->msgs) { - g_ptr_array_free(iterator->msgs, TRUE); - iterator->msgs = NULL; - } - - g_free(iterator); -} - -static -void bt_self_component_port_input_message_iterator_destroy(struct bt_object *obj) -{ - struct bt_self_component_port_input_message_iterator *iterator; - - BT_ASSERT(obj); - - /* - * The message iterator's reference count is 0 if we're - * here. Increment it to avoid a double-destroy (possibly - * infinitely recursive). This could happen for example if the - * message iterator's finalization function does - * bt_object_get_ref() (or anything that causes - * bt_object_get_ref() to be called) on itself (ref. count goes - * from 0 to 1), and then bt_object_put_ref(): the reference - * count would go from 1 to 0 again and this function would be - * called again. - */ - obj->ref_count++; - iterator = (void *) obj; - BT_LIB_LOGD("Destroying self component input port message iterator object: " - "%!+i", iterator); - bt_self_component_port_input_message_iterator_try_finalize(iterator); - - if (iterator->connection) { - /* - * Remove ourself from the originating connection so - * that it does not try to finalize a dangling pointer - * later. - */ - bt_connection_remove_iterator(iterator->connection, iterator); - iterator->connection = NULL; - } - - if (iterator->auto_seek_msgs) { - while (!g_queue_is_empty(iterator->auto_seek_msgs)) { - bt_object_put_no_null_check( - g_queue_pop_tail(iterator->auto_seek_msgs)); - } - - g_queue_free(iterator->auto_seek_msgs); - iterator->auto_seek_msgs = NULL; - } - - destroy_base_message_iterator(obj); -} - -BT_HIDDEN -void bt_self_component_port_input_message_iterator_try_finalize( - struct bt_self_component_port_input_message_iterator *iterator) -{ - typedef void (*method_t)(void *); - - struct bt_component_class *comp_class = NULL; - method_t method = NULL; - - BT_ASSERT(iterator); - - switch (iterator->state) { - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED: - /* Skip user finalization if user initialization failed */ - BT_LIB_LOGD("Not finalizing non-initialized message iterator: " - "%!+i", iterator); - goto end; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED: - /* Already finalized */ - BT_LIB_LOGD("Not finalizing message iterator: already finalized: " - "%!+i", iterator); - goto end; - case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING: - /* Already finalized */ - BT_LIB_LOGF("Message iterator is already being finalized: " - "%!+i", iterator); - abort(); - default: - break; - } - - BT_LIB_LOGD("Finalizing message iterator: %!+i", iterator); - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING); - BT_ASSERT(iterator->upstream_component); - comp_class = iterator->upstream_component->class; - - /* Call user-defined destroy method */ - switch (comp_class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_comp_cls = - (void *) comp_class; - - method = (method_t) src_comp_cls->methods.msg_iter_finalize; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_comp_cls = - (void *) comp_class; - - method = (method_t) flt_comp_cls->methods.msg_iter_finalize; - break; - } - default: - /* Unreachable */ - abort(); - } - - if (method) { - BT_LIB_LOGD("Calling user's finalization method: %!+i", - iterator); - method(iterator); - } - - iterator->upstream_component = NULL; - iterator->upstream_port = NULL; - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED); - BT_LIB_LOGD("Finalized message iterator: %!+i", iterator); - -end: - return; -} - -BT_HIDDEN -void bt_self_component_port_input_message_iterator_set_connection( - struct bt_self_component_port_input_message_iterator *iterator, - struct bt_connection *connection) -{ - BT_ASSERT(iterator); - iterator->connection = connection; - BT_LIB_LOGV("Set message iterator's connection: " - "%![iter-]+i, %![conn-]+x", iterator, connection); -} - -static -int init_message_iterator(struct bt_message_iterator *iterator, - enum bt_message_iterator_type type, - bt_object_release_func destroy) -{ - int ret = 0; - - bt_object_init_shared(&iterator->base, destroy); - iterator->type = type; - iterator->msgs = g_ptr_array_new(); - if (!iterator->msgs) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - ret = -1; - goto end; - } - - g_ptr_array_set_size(iterator->msgs, MSG_BATCH_SIZE); - -end: - return ret; -} - -static -bt_bool can_seek_ns_from_origin_true( - struct bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin) -{ - return BT_TRUE; -} - -static -bt_bool can_seek_beginning_true( - struct bt_self_component_port_input_message_iterator *iterator) -{ - return BT_TRUE; -} - -static -struct bt_self_component_port_input_message_iterator * -bt_self_component_port_input_message_iterator_create_initial( - struct bt_component *upstream_comp, - struct bt_port *upstream_port) -{ - int ret; - struct bt_self_component_port_input_message_iterator *iterator = NULL; - - BT_ASSERT(upstream_comp); - BT_ASSERT(upstream_port); - BT_ASSERT(bt_port_is_connected(upstream_port)); - BT_LIB_LOGD("Creating initial message iterator on self component input port: " - "%![up-comp-]+c, %![up-port-]+p", upstream_comp, upstream_port); - BT_ASSERT(bt_component_get_class_type(upstream_comp) == - BT_COMPONENT_CLASS_TYPE_SOURCE || - bt_component_get_class_type(upstream_comp) == - BT_COMPONENT_CLASS_TYPE_FILTER); - iterator = g_new0( - struct bt_self_component_port_input_message_iterator, 1); - if (!iterator) { - BT_LOGE_STR("Failed to allocate one self component input port " - "message iterator."); - goto end; - } - - ret = init_message_iterator((void *) iterator, - BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT, - bt_self_component_port_input_message_iterator_destroy); - if (ret) { - /* init_message_iterator() logs errors */ - BT_OBJECT_PUT_REF_AND_RESET(iterator); - goto end; - } - - iterator->auto_seek_msgs = g_queue_new(); - if (!iterator->auto_seek_msgs) { - BT_LOGE_STR("Failed to allocate a GQueue."); - ret = -1; - goto end; - } - - iterator->upstream_component = upstream_comp; - iterator->upstream_port = upstream_port; - iterator->connection = iterator->upstream_port->connection; - iterator->graph = bt_component_borrow_graph(upstream_comp); - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED); - - switch (iterator->upstream_component->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_comp_cls = - (void *) iterator->upstream_component->class; - - iterator->methods.next = - (bt_self_component_port_input_message_iterator_next_method) - src_comp_cls->methods.msg_iter_next; - iterator->methods.seek_ns_from_origin = - (bt_self_component_port_input_message_iterator_seek_ns_from_origin_method) - src_comp_cls->methods.msg_iter_seek_ns_from_origin; - iterator->methods.seek_beginning = - (bt_self_component_port_input_message_iterator_seek_beginning_method) - src_comp_cls->methods.msg_iter_seek_beginning; - iterator->methods.can_seek_ns_from_origin = - (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) - src_comp_cls->methods.msg_iter_can_seek_ns_from_origin; - iterator->methods.can_seek_beginning = - (bt_self_component_port_input_message_iterator_can_seek_beginning_method) - src_comp_cls->methods.msg_iter_can_seek_beginning; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_comp_cls = - (void *) iterator->upstream_component->class; - - iterator->methods.next = - (bt_self_component_port_input_message_iterator_next_method) - flt_comp_cls->methods.msg_iter_next; - iterator->methods.seek_ns_from_origin = - (bt_self_component_port_input_message_iterator_seek_ns_from_origin_method) - flt_comp_cls->methods.msg_iter_seek_ns_from_origin; - iterator->methods.seek_beginning = - (bt_self_component_port_input_message_iterator_seek_beginning_method) - flt_comp_cls->methods.msg_iter_seek_beginning; - iterator->methods.can_seek_ns_from_origin = - (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) - flt_comp_cls->methods.msg_iter_can_seek_ns_from_origin; - iterator->methods.can_seek_beginning = - (bt_self_component_port_input_message_iterator_can_seek_beginning_method) - flt_comp_cls->methods.msg_iter_can_seek_beginning; - break; - } - default: - abort(); - } - - if (iterator->methods.seek_ns_from_origin && - !iterator->methods.can_seek_ns_from_origin) { - iterator->methods.can_seek_ns_from_origin = - (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) - can_seek_ns_from_origin_true; - } - - if (iterator->methods.seek_beginning && - !iterator->methods.can_seek_beginning) { - iterator->methods.can_seek_beginning = - (bt_self_component_port_input_message_iterator_seek_beginning_method) - can_seek_beginning_true; - } - - BT_LIB_LOGD("Created initial message iterator on self component input port: " - "%![up-port-]+p, %![up-comp-]+c, %![iter-]+i", - upstream_port, upstream_comp, iterator); - -end: - return iterator; -} - -struct bt_self_component_port_input_message_iterator * -bt_self_component_port_input_message_iterator_create( - struct bt_self_component_port_input *self_port) -{ - typedef enum bt_self_message_iterator_status (*init_method_t)( - void *, void *, void *); - - init_method_t init_method = NULL; - struct bt_self_component_port_input_message_iterator *iterator = - NULL; - struct bt_port *port = (void *) self_port; - struct bt_port *upstream_port; - struct bt_component *comp; - struct bt_component *upstream_comp; - struct bt_component_class *upstream_comp_cls; - - BT_ASSERT_PRE_NON_NULL(port, "Port"); - comp = bt_port_borrow_component_inline(port); - BT_ASSERT_PRE(bt_port_is_connected(port), - "Port is not connected: %![port-]+p", port); - BT_ASSERT_PRE(comp, "Port is not part of a component: %![port-]+p", - port); - BT_ASSERT_PRE(!bt_component_graph_is_canceled(comp), - "Port's component's graph is canceled: " - "%![port-]+p, %![comp-]+c", port, comp); - BT_ASSERT(port->connection); - upstream_port = port->connection->upstream_port; - BT_ASSERT(upstream_port); - upstream_comp = bt_port_borrow_component_inline(upstream_port); - BT_ASSERT(upstream_comp); - BT_ASSERT_PRE( - bt_component_borrow_graph(upstream_comp)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(upstream_comp)); - upstream_comp_cls = upstream_comp->class; - BT_ASSERT(upstream_comp->class->type == - BT_COMPONENT_CLASS_TYPE_SOURCE || - upstream_comp->class->type == - BT_COMPONENT_CLASS_TYPE_FILTER); - iterator = bt_self_component_port_input_message_iterator_create_initial( - upstream_comp, upstream_port); - if (!iterator) { - BT_LOGW_STR("Cannot create self component input port " - "message iterator."); - goto end; - } - - switch (upstream_comp_cls->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_comp_cls = - (void *) upstream_comp_cls; - - init_method = - (init_method_t) src_comp_cls->methods.msg_iter_init; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_comp_cls = - (void *) upstream_comp_cls; - - init_method = - (init_method_t) flt_comp_cls->methods.msg_iter_init; - break; - } - default: - /* Unreachable */ - abort(); - } - - if (init_method) { - int iter_status; - - BT_LIB_LOGD("Calling user's initialization method: %!+i", iterator); - iter_status = init_method(iterator, upstream_comp, - upstream_port); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(iter_status)); - if (iter_status != BT_MESSAGE_ITERATOR_STATUS_OK) { - BT_LOGW_STR("Initialization method failed."); - BT_OBJECT_PUT_REF_AND_RESET(iterator); - goto end; - } - } - - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); - g_ptr_array_add(port->connection->iterators, iterator); - BT_LIB_LOGD("Created message iterator on self component input port: " - "%![up-port-]+p, %![up-comp-]+c, %![iter-]+i", - upstream_port, upstream_comp, iterator); - -end: - return iterator; -} - -void *bt_self_message_iterator_get_data( - const struct bt_self_message_iterator *self_iterator) -{ - struct bt_self_component_port_input_message_iterator *iterator = - (void *) self_iterator; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return iterator->user_data; -} - -void bt_self_message_iterator_set_data( - struct bt_self_message_iterator *self_iterator, void *data) -{ - struct bt_self_component_port_input_message_iterator *iterator = - (void *) self_iterator; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - iterator->user_data = data; - BT_LIB_LOGV("Set message iterator's user data: " - "%!+i, user-data-addr=%p", iterator, data); -} - -enum bt_message_iterator_status -bt_self_component_port_input_message_iterator_next( - struct bt_self_component_port_input_message_iterator *iterator, - bt_message_array_const *msgs, uint64_t *user_count) -{ - int status = BT_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(msgs, "Message array (output)"); - BT_ASSERT_PRE_NON_NULL(user_count, "Message count (output)"); - BT_ASSERT_PRE(iterator->state == - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE, - "Message iterator's \"next\" called, but " - "message iterator is in the wrong state: %!+i", iterator); - BT_ASSERT(iterator->upstream_component); - BT_ASSERT(iterator->upstream_component->class); - BT_ASSERT_PRE( - bt_component_borrow_graph(iterator->upstream_component)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(iterator->upstream_component)); - BT_LIB_LOGD("Getting next self component input port " - "message iterator's messages: %!+i", iterator); - - /* - * Call the user's "next" method to get the next messages - * and status. - */ - BT_ASSERT(iterator->methods.next); - BT_LOGD_STR("Calling user's \"next\" method."); - status = iterator->methods.next(iterator, - (void *) iterator->base.msgs->pdata, MSG_BATCH_SIZE, - user_count); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(status)); - if (status < 0) { - BT_LOGW_STR("User method failed."); - goto end; - } - - /* - * There is no way that this iterator could have been finalized - * during its "next" method, as the only way to do this is to - * put the last iterator's reference, and this can only be done - * by its downstream owner. - * - * For the same reason, there is no way that this iterator could - * have seeked (cannot seek a self message iterator). - */ - BT_ASSERT(iterator->state == - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); - - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - BT_ASSERT_PRE(*user_count <= MSG_BATCH_SIZE, - "Invalid returned message count: greater than " - "batch size: count=%" PRIu64 ", batch-size=%u", - *user_count, MSG_BATCH_SIZE); - *msgs = (void *) iterator->base.msgs->pdata; - break; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - goto end; - case BT_MESSAGE_ITERATOR_STATUS_END: - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED); - goto end; - default: - /* Unknown non-error status */ - abort(); - } - -end: - return status; -} - -enum bt_message_iterator_status bt_port_output_message_iterator_next( - struct bt_port_output_message_iterator *iterator, - bt_message_array_const *msgs_to_user, - uint64_t *count_to_user) -{ - enum bt_message_iterator_status status; - enum bt_graph_status graph_status; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(msgs_to_user, "Message array (output)"); - BT_ASSERT_PRE_NON_NULL(count_to_user, "Message count (output)"); - BT_LIB_LOGD("Getting next output port message iterator's messages: " - "%!+i", iterator); - graph_status = bt_graph_consume_sink_no_check(iterator->graph, - iterator->colander); - switch (graph_status) { - case BT_GRAPH_STATUS_CANCELED: - case BT_GRAPH_STATUS_AGAIN: - case BT_GRAPH_STATUS_END: - case BT_GRAPH_STATUS_NOMEM: - status = (int) graph_status; - break; - case BT_GRAPH_STATUS_OK: - status = BT_MESSAGE_ITERATOR_STATUS_OK; - - /* - * On success, the colander sink moves the messages - * to this iterator's array and sets this iterator's - * message count: move them to the user. - */ - *msgs_to_user = (void *) iterator->base.msgs->pdata; - *count_to_user = iterator->count; - break; - default: - /* Other errors */ - status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - } - - return status; -} - -struct bt_component * -bt_self_component_port_input_message_iterator_borrow_component( - struct bt_self_component_port_input_message_iterator *iterator) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return iterator->upstream_component; -} - -const struct bt_component * -bt_self_component_port_input_message_iterator_borrow_component_const( - const struct bt_self_component_port_input_message_iterator *iterator) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return iterator->upstream_component; -} - -struct bt_self_component *bt_self_message_iterator_borrow_component( - struct bt_self_message_iterator *self_iterator) -{ - struct bt_self_component_port_input_message_iterator *iterator = - (void *) self_iterator; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return (void *) iterator->upstream_component; -} - -struct bt_self_port_output *bt_self_message_iterator_borrow_port( - struct bt_self_message_iterator *self_iterator) -{ - struct bt_self_component_port_input_message_iterator *iterator = - (void *) self_iterator; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return (void *) iterator->upstream_port; -} - -static -void bt_port_output_message_iterator_destroy(struct bt_object *obj) -{ - struct bt_port_output_message_iterator *iterator = (void *) obj; - - BT_LIB_LOGD("Destroying output port message iterator object: %!+i", - iterator); - BT_LOGD_STR("Putting graph."); - BT_OBJECT_PUT_REF_AND_RESET(iterator->graph); - BT_LOGD_STR("Putting colander sink component."); - BT_OBJECT_PUT_REF_AND_RESET(iterator->colander); - destroy_base_message_iterator(obj); -} - -struct bt_port_output_message_iterator * -bt_port_output_message_iterator_create(struct bt_graph *graph, - const struct bt_port_output *output_port) -{ - struct bt_port_output_message_iterator *iterator = NULL; - struct bt_component_class_sink *colander_comp_cls = NULL; - struct bt_component *output_port_comp = NULL; - struct bt_component_sink *colander_comp; - enum bt_graph_status graph_status; - struct bt_port_input *colander_in_port = NULL; - struct bt_component_class_sink_colander_data colander_data; - int ret; - - BT_ASSERT_PRE_NON_NULL(graph, "Graph"); - BT_ASSERT_PRE_NON_NULL(output_port, "Output port"); - output_port_comp = bt_port_borrow_component_inline( - (const void *) output_port); - BT_ASSERT_PRE(output_port_comp, - "Output port has no component: %!+p", output_port); - BT_ASSERT_PRE(bt_component_borrow_graph(output_port_comp) == - (void *) graph, - "Output port is not part of graph: %![graph-]+g, %![port-]+p", - graph, output_port); - BT_ASSERT_PRE(!graph->has_sink, - "Graph already has a sink component: %![graph-]+g"); - - /* Create message iterator */ - BT_LIB_LOGD("Creating message iterator on output port: " - "%![port-]+p, %![comp-]+c", output_port, output_port_comp); - iterator = g_new0(struct bt_port_output_message_iterator, 1); - if (!iterator) { - BT_LOGE_STR("Failed to allocate one output port message iterator."); - goto error; - } - - ret = init_message_iterator((void *) iterator, - BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT, - bt_port_output_message_iterator_destroy); - if (ret) { - /* init_message_iterator() logs errors */ - BT_OBJECT_PUT_REF_AND_RESET(iterator); - goto end; - } - - /* Create colander component */ - colander_comp_cls = bt_component_class_sink_colander_get(); - if (!colander_comp_cls) { - BT_LOGW("Cannot get colander sink component class."); - goto error; - } - - iterator->graph = graph; - bt_object_get_no_null_check(iterator->graph); - colander_data.msgs = (void *) iterator->base.msgs->pdata; - colander_data.count_addr = &iterator->count; - - /* Hope that nobody uses this very unique name */ - graph_status = - bt_graph_add_sink_component_with_init_method_data( - (void *) graph, colander_comp_cls, - "colander-36ac3409-b1a8-4d60-ab1f-4fdf341a8fb1", - NULL, &colander_data, (void *) &iterator->colander); - if (graph_status != BT_GRAPH_STATUS_OK) { - BT_LIB_LOGW("Cannot add colander sink component to graph: " - "%1[graph-]+g, status=%s", graph, - bt_graph_status_string(graph_status)); - goto error; - } - - /* - * Connect provided output port to the colander component's - * input port. - */ - colander_in_port = - (void *) bt_component_sink_borrow_input_port_by_index_const( - (void *) iterator->colander, 0); - BT_ASSERT(colander_in_port); - graph_status = bt_graph_connect_ports(graph, - output_port, colander_in_port, NULL); - if (graph_status != BT_GRAPH_STATUS_OK) { - BT_LIB_LOGW("Cannot add colander sink component to graph: " - "%![graph-]+g, %![comp-]+c, status=%s", graph, - iterator->colander, - bt_graph_status_string(graph_status)); - goto error; - } - - /* - * At this point everything went fine. Make the graph - * nonconsumable forever so that only this message iterator - * can consume (thanks to bt_graph_consume_sink_no_check()). - * This avoids leaking the message created by the colander - * sink and moved to the message iterator's message - * member. - */ - bt_graph_set_can_consume(iterator->graph, false); - - /* Also set the graph as being configured. */ - graph_status = bt_graph_configure(graph); - if (graph_status != BT_GRAPH_STATUS_OK) { - BT_LIB_LOGW("Cannot configure graph after having added colander: " - "%![graph-]+g, status=%s", graph, - bt_graph_status_string(graph_status)); - goto error; - } - goto end; - -error: - if (iterator && iterator->graph && iterator->colander) { - int ret; - - /* Remove created colander component from graph if any */ - colander_comp = iterator->colander; - BT_OBJECT_PUT_REF_AND_RESET(iterator->colander); - - /* - * At this point the colander component's reference - * count is 0 because iterator->colander was the only - * owner. We also know that it is not connected because - * this is the last operation before this function - * succeeds. - * - * Since we honor the preconditions here, - * bt_graph_remove_unconnected_component() always - * succeeds. - */ - ret = bt_graph_remove_unconnected_component(iterator->graph, - (void *) colander_comp); - BT_ASSERT(ret == 0); - } - - BT_OBJECT_PUT_REF_AND_RESET(iterator); - -end: - bt_object_put_ref(colander_comp_cls); - return (void *) iterator; -} - -bt_bool bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( - struct bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin) -{ - bt_bool can = BT_FALSE; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); - BT_ASSERT_PRE( - bt_component_borrow_graph(iterator->upstream_component)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(iterator->upstream_component)); - - if (iterator->methods.can_seek_ns_from_origin) { - can = iterator->methods.can_seek_ns_from_origin(iterator, - ns_from_origin); - goto end; - } - - /* - * Automatic seeking fall back: if we can seek to the beginning, - * then we can automatically seek to any message. - */ - if (iterator->methods.can_seek_beginning) { - can = iterator->methods.can_seek_beginning(iterator); - } - -end: - return can; -} - -bt_bool bt_self_component_port_input_message_iterator_can_seek_beginning( - struct bt_self_component_port_input_message_iterator *iterator) -{ - bt_bool can = BT_FALSE; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); - BT_ASSERT_PRE( - bt_component_borrow_graph(iterator->upstream_component)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(iterator->upstream_component)); - - if (iterator->methods.can_seek_beginning) { - can = iterator->methods.can_seek_beginning(iterator); - } - - return can; -} - -static inline -void set_iterator_state_after_seeking( - struct bt_self_component_port_input_message_iterator *iterator, - enum bt_message_iterator_status status) -{ - enum bt_self_component_port_input_message_iterator_state new_state = 0; - - /* Set iterator's state depending on seeking status */ - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE; - break; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN; - break; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR; - break; - case BT_MESSAGE_ITERATOR_STATUS_END: - new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED; - break; - default: - abort(); - } - - set_self_comp_port_input_msg_iterator_state(iterator, new_state); -} - -enum bt_message_iterator_status -bt_self_component_port_input_message_iterator_seek_beginning( - struct bt_self_component_port_input_message_iterator *iterator) -{ - int status; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); - BT_ASSERT_PRE( - bt_component_borrow_graph(iterator->upstream_component)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(iterator->upstream_component)); - BT_ASSERT_PRE( - bt_self_component_port_input_message_iterator_can_seek_beginning( - iterator), - "Message iterator cannot seek beginning: %!+i", iterator); - BT_LIB_LOGD("Calling user's \"seek beginning\" method: %!+i", iterator); - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING); - status = iterator->methods.seek_beginning(iterator); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(status)); - BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || - status == BT_MESSAGE_ITERATOR_STATUS_ERROR || - status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || - status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, - "Unexpected status: %![iter-]+i, status=%s", - iterator, bt_common_self_message_iterator_status_string(status)); - set_iterator_state_after_seeking(iterator, status); - return status; -} - -static inline -enum bt_message_iterator_status auto_seek_handle_message( - struct bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin, const struct bt_message *msg, - bool *got_first) -{ - enum bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; - int64_t msg_ns_from_origin; - const struct bt_clock_snapshot *clk_snapshot = NULL; - int ret; - - BT_ASSERT(msg); - BT_ASSERT(got_first); - - switch (msg->type) { - case BT_MESSAGE_TYPE_EVENT: - { - const struct bt_message_event *event_msg = - (const void *) msg; - - clk_snapshot = event_msg->default_cs; - BT_ASSERT_PRE(clk_snapshot, - "Event message has no default clock snapshot: %!+n", - event_msg); - break; - } - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - { - const struct bt_message_message_iterator_inactivity *inactivity_msg = - (const void *) msg; - - clk_snapshot = inactivity_msg->default_cs; - BT_ASSERT(clk_snapshot); - break; - } - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - case BT_MESSAGE_TYPE_PACKET_END: - { - const struct bt_message_packet *packet_msg = - (const void *) msg; - - clk_snapshot = packet_msg->default_cs; - BT_ASSERT_PRE(clk_snapshot, - "Packet message has no default clock snapshot: %!+n", - packet_msg); - break; - } - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - { - struct bt_message_discarded_items *msg_disc_items = - (void *) msg; - - BT_ASSERT_PRE(msg_disc_items->default_begin_cs && - msg_disc_items->default_end_cs, - "Discarded events/packets message has no default clock snapshots: %!+n", - msg_disc_items); - ret = bt_clock_snapshot_get_ns_from_origin( - msg_disc_items->default_begin_cs, - &msg_ns_from_origin); - if (ret) { - status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (msg_ns_from_origin >= ns_from_origin) { - *got_first = true; - goto push_msg; - } - - ret = bt_clock_snapshot_get_ns_from_origin( - msg_disc_items->default_end_cs, - &msg_ns_from_origin); - if (ret) { - status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (msg_ns_from_origin >= ns_from_origin) { - /* - * The discarded items message's beginning time - * is before the requested seeking time, but its - * end time is after. Modify the message so as - * to set its beginning time to the requested - * seeking time, and make its item count unknown - * as we don't know if items were really - * discarded within the new time range. - */ - uint64_t new_begin_raw_value; - - ret = bt_clock_class_clock_value_from_ns_from_origin( - msg_disc_items->default_end_cs->clock_class, - ns_from_origin, &new_begin_raw_value); - if (ret) { - status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - bt_clock_snapshot_set_raw_value( - msg_disc_items->default_begin_cs, - new_begin_raw_value); - msg_disc_items->count.base.avail = - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE; - - /* - * It is safe to push it because its beginning - * time is exactly the requested seeking time. - */ - goto push_msg; - } else { - goto skip_msg; - } - } - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - { - const struct bt_message_stream_activity *stream_act_msg = - (const void *) msg; - - switch (stream_act_msg->default_cs_state) { - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: - /* - * -inf is always less than any requested time, - * and we can't assume any specific time for an - * unknown clock snapshot, so skip this. - */ - goto skip_msg; - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: - clk_snapshot = stream_act_msg->default_cs; - BT_ASSERT(clk_snapshot); - break; - default: - abort(); - } - - break; - } - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - { - const struct bt_message_stream_activity *stream_act_msg = - (const void *) msg; - - switch (stream_act_msg->default_cs_state) { - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: - /* - * We can't assume any specific time for an - * unknown clock snapshot, so skip this. - */ - goto skip_msg; - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: - /* - * +inf is always greater than any requested - * time. - */ - *got_first = true; - goto push_msg; - case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: - clk_snapshot = stream_act_msg->default_cs; - BT_ASSERT(clk_snapshot); - break; - default: - abort(); - } - - break; - } - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - case BT_MESSAGE_TYPE_STREAM_END: - /* Ignore */ - goto skip_msg; - default: - abort(); - } - - BT_ASSERT(clk_snapshot); - ret = bt_clock_snapshot_get_ns_from_origin(clk_snapshot, - &msg_ns_from_origin); - if (ret) { - status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (msg_ns_from_origin >= ns_from_origin) { - *got_first = true; - goto push_msg; - } - -skip_msg: - bt_object_put_no_null_check(msg); - msg = NULL; - goto end; - -push_msg: - g_queue_push_head(iterator->auto_seek_msgs, (void *) msg); - msg = NULL; - -end: - BT_ASSERT(!msg || status != BT_MESSAGE_ITERATOR_STATUS_OK); - return status; -} - -static -enum bt_message_iterator_status find_message_ge_ns_from_origin( - struct bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin) -{ - int status; - enum bt_self_component_port_input_message_iterator_state init_state = - iterator->state; - const struct bt_message *messages[MSG_BATCH_SIZE]; - uint64_t user_count = 0; - uint64_t i; - bool got_first = false; - - BT_ASSERT(iterator); - memset(&messages[0], 0, sizeof(messages[0]) * MSG_BATCH_SIZE); - - /* - * Make this iterator temporarily active (not seeking) to call - * the "next" method. - */ - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); - - BT_ASSERT(iterator->methods.next); - - while (!got_first) { - /* - * Call the user's "next" method to get the next - * messages and status. - */ - BT_LOGD_STR("Calling user's \"next\" method."); - status = iterator->methods.next(iterator, - &messages[0], MSG_BATCH_SIZE, &user_count); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(status)); - - /* - * The user's "next" method must not do any action which - * would change the iterator's state. - */ - BT_ASSERT(iterator->state == - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); - - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - BT_ASSERT_PRE(user_count <= MSG_BATCH_SIZE, - "Invalid returned message count: greater than " - "batch size: count=%" PRIu64 ", batch-size=%u", - user_count, MSG_BATCH_SIZE); - break; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - case BT_MESSAGE_ITERATOR_STATUS_END: - goto end; - default: - abort(); - } - - for (i = 0; i < user_count; i++) { - if (got_first) { - g_queue_push_head(iterator->auto_seek_msgs, - (void *) messages[i]); - messages[i] = NULL; - continue; - } - - status = auto_seek_handle_message(iterator, - ns_from_origin, messages[i], &got_first); - if (status == BT_MESSAGE_ITERATOR_STATUS_OK) { - /* Message was either pushed or moved */ - messages[i] = NULL; - } else { - goto end; - } - } - } - -end: - for (i = 0; i < user_count; i++) { - if (messages[i]) { - bt_object_put_no_null_check(messages[i]); - } - } - - set_self_comp_port_input_msg_iterator_state(iterator, init_state); - return status; -} - -static -enum bt_self_message_iterator_status post_auto_seek_next( - struct bt_self_component_port_input_message_iterator *iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - BT_ASSERT(!g_queue_is_empty(iterator->auto_seek_msgs)); - *count = 0; - - /* - * Move auto-seek messages to the output array (which is this - * iterator's base message array). - */ - while (capacity > 0 && !g_queue_is_empty(iterator->auto_seek_msgs)) { - msgs[*count] = g_queue_pop_tail(iterator->auto_seek_msgs); - capacity--; - (*count)++; - } - - BT_ASSERT(*count > 0); - - if (g_queue_is_empty(iterator->auto_seek_msgs)) { - /* No more auto-seek messages */ - switch (iterator->upstream_component->class->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_comp_cls = - (void *) iterator->upstream_component->class; - - iterator->methods.next = - (bt_self_component_port_input_message_iterator_next_method) - src_comp_cls->methods.msg_iter_next; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_comp_cls = - (void *) iterator->upstream_component->class; - - iterator->methods.next = - (bt_self_component_port_input_message_iterator_next_method) - flt_comp_cls->methods.msg_iter_next; - break; - } - default: - abort(); - } - } - - return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; -} - -enum bt_message_iterator_status -bt_self_component_port_input_message_iterator_seek_ns_from_origin( - struct bt_self_component_port_input_message_iterator *iterator, - int64_t ns_from_origin) -{ - int status; - - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); - BT_ASSERT_PRE( - bt_component_borrow_graph(iterator->upstream_component)->config_state != - BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, - "Graph is not configured: %!+g", - bt_component_borrow_graph(iterator->upstream_component)); - BT_ASSERT_PRE( - bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( - iterator, ns_from_origin), - "Message iterator cannot seek nanoseconds from origin: %!+i, " - "ns-from-origin=%" PRId64, iterator, ns_from_origin); - set_self_comp_port_input_msg_iterator_state(iterator, - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING); - - if (iterator->methods.seek_ns_from_origin) { - BT_LIB_LOGD("Calling user's \"seek nanoseconds from origin\" method: " - "%![iter-]+i, ns=%" PRId64, iterator, ns_from_origin); - status = iterator->methods.seek_ns_from_origin(iterator, - ns_from_origin); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(status)); - BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || - status == BT_MESSAGE_ITERATOR_STATUS_ERROR || - status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || - status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, - "Unexpected status: %![iter-]+i, status=%s", - iterator, - bt_common_self_message_iterator_status_string(status)); - } else { - /* Start automatic seeking: seek beginning first */ - BT_ASSERT(iterator->methods.can_seek_beginning(iterator)); - BT_ASSERT(iterator->methods.seek_beginning); - BT_LIB_LOGD("Calling user's \"seek beginning\" method: %!+i", - iterator); - status = iterator->methods.seek_beginning(iterator); - BT_LOGD("User method returned: status=%s", - bt_message_iterator_status_string(status)); - BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || - status == BT_MESSAGE_ITERATOR_STATUS_ERROR || - status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || - status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, - "Unexpected status: %![iter-]+i, status=%s", - iterator, - bt_common_self_message_iterator_status_string(status)); - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - break; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - goto end; - default: - abort(); - } - - /* - * Find the first message which has a default clock - * snapshot greater than or equal to the requested - * seeking time, and move the received messages from - * this point in the batch to this iterator's auto-seek - * message queue. - */ - while (!g_queue_is_empty(iterator->auto_seek_msgs)) { - bt_object_put_no_null_check( - g_queue_pop_tail(iterator->auto_seek_msgs)); - } - - status = find_message_ge_ns_from_origin(iterator, - ns_from_origin); - switch (status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - case BT_MESSAGE_ITERATOR_STATUS_END: - /* - * If there are messages in the auto-seek - * message queue, replace the user's "next" - * method with a custom, temporary "next" method - * which returns them. - */ - if (!g_queue_is_empty(iterator->auto_seek_msgs)) { - iterator->methods.next = - (bt_self_component_port_input_message_iterator_next_method) - post_auto_seek_next; - } - - /* - * `BT_MESSAGE_ITERATOR_STATUS_END` becomes - * `BT_MESSAGE_ITERATOR_STATUS_OK`: the next - * time this iterator's "next" method is called, - * it will return - * `BT_MESSAGE_ITERATOR_STATUS_END`. - */ - status = BT_MESSAGE_ITERATOR_STATUS_OK; - break; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - goto end; - default: - abort(); - } - } - -end: - set_iterator_state_after_seeking(iterator, status); - return status; -} - -static inline -bt_self_component_port_input_message_iterator * -borrow_output_port_message_iterator_upstream_iterator( - struct bt_port_output_message_iterator *iterator) -{ - struct bt_component_class_sink_colander_priv_data *colander_data; - - BT_ASSERT(iterator); - colander_data = (void *) iterator->colander->parent.user_data; - BT_ASSERT(colander_data); - BT_ASSERT(colander_data->msg_iter); - return colander_data->msg_iter; -} - -bt_bool bt_port_output_message_iterator_can_seek_ns_from_origin( - struct bt_port_output_message_iterator *iterator, - int64_t ns_from_origin) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( - borrow_output_port_message_iterator_upstream_iterator( - iterator), ns_from_origin); -} - -bt_bool bt_port_output_message_iterator_can_seek_beginning( - struct bt_port_output_message_iterator *iterator) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return bt_self_component_port_input_message_iterator_can_seek_beginning( - borrow_output_port_message_iterator_upstream_iterator( - iterator)); -} - -enum bt_message_iterator_status bt_port_output_message_iterator_seek_ns_from_origin( - struct bt_port_output_message_iterator *iterator, - int64_t ns_from_origin) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return bt_self_component_port_input_message_iterator_seek_ns_from_origin( - borrow_output_port_message_iterator_upstream_iterator(iterator), - ns_from_origin); -} - -enum bt_message_iterator_status bt_port_output_message_iterator_seek_beginning( - struct bt_port_output_message_iterator *iterator) -{ - BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); - return bt_self_component_port_input_message_iterator_seek_beginning( - borrow_output_port_message_iterator_upstream_iterator( - iterator)); -} - -void bt_port_output_message_iterator_get_ref( - const struct bt_port_output_message_iterator *iterator) -{ - bt_object_get_ref(iterator); -} - -void bt_port_output_message_iterator_put_ref( - const struct bt_port_output_message_iterator *iterator) -{ - bt_object_put_ref(iterator); -} - -void bt_self_component_port_input_message_iterator_get_ref( - const struct bt_self_component_port_input_message_iterator *iterator) -{ - bt_object_get_ref(iterator); -} - -void bt_self_component_port_input_message_iterator_put_ref( - const struct bt_self_component_port_input_message_iterator *iterator) -{ - bt_object_put_ref(iterator); -} diff --git a/lib/graph/message/Makefile.am b/lib/graph/message/Makefile.am deleted file mode 100644 index 9c69099f..00000000 --- a/lib/graph/message/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -noinst_LTLIBRARIES = libgraph-message.la - -libgraph_message_la_SOURCES = \ - message.c \ - packet.c \ - event.c \ - stream.c \ - message-iterator-inactivity.c \ - stream-activity.c \ - discarded-items.c diff --git a/lib/graph/message/discarded-items.c b/lib/graph/message/discarded-items.c deleted file mode 100644 index 36bdc368..00000000 --- a/lib/graph/message/discarded-items.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-DISCARDED-ITEMS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_discarded_items_message(struct bt_object *obj) -{ - struct bt_message_discarded_items *message = (void *) obj; - - BT_LIB_LOGD("Destroying discarded items message: %!+n", message); - BT_LIB_LOGD("Putting stream: %!+s", message->stream); - BT_OBJECT_PUT_REF_AND_RESET(message->stream); - - if (message->default_begin_cs) { - bt_clock_snapshot_recycle(message->default_begin_cs); - message->default_begin_cs = NULL; - } - - if (message->default_end_cs) { - bt_clock_snapshot_recycle(message->default_end_cs); - message->default_end_cs = NULL; - } - - g_free(message); -} - -static inline -struct bt_message *create_discarded_items_message( - struct bt_self_message_iterator *self_msg_iter, - enum bt_message_type type, struct bt_stream *stream, - bool with_cs, - uint64_t beginning_raw_value, uint64_t end_raw_value) -{ - struct bt_message_discarded_items *message; - struct bt_stream_class *stream_class; - bool has_support; - bool need_cs; - - BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - stream_class = bt_stream_borrow_class(stream); - BT_ASSERT(stream_class); - - if (type == BT_MESSAGE_TYPE_DISCARDED_EVENTS) { - has_support = stream_class->supports_discarded_events; - need_cs = stream_class->discarded_events_have_default_clock_snapshots; - } else { - has_support = stream_class->supports_discarded_packets; - need_cs = stream_class->discarded_packets_have_default_clock_snapshots; - } - - BT_ASSERT_PRE(has_support, - "Stream class does not support discarded events or packets: " - "type=%s, %![stream-]+s, %![sc-]+S", - bt_message_type_string(type), stream, stream_class); - BT_ASSERT_PRE(need_cs ? with_cs : true, - "Unexpected stream class configuration when creating " - "a discarded events or discarded packets message: " - "default clock snapshots are needed, but none was provided: " - "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " - "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, - bt_message_type_string(type), stream, stream_class, - with_cs, beginning_raw_value, end_raw_value); - BT_ASSERT_PRE(!need_cs ? !with_cs : true, - "Unexpected stream class configuration when creating " - "a discarded events or discarded packets message: " - "no default clock snapshots are needed, but two were provided: " - "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " - "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, - bt_message_type_string(type), stream, stream_class, - with_cs, beginning_raw_value, end_raw_value); - BT_LIB_LOGD("Creating discarded items message object: " - "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " - "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, - bt_message_type_string(type), stream, stream_class, - with_cs, beginning_raw_value, end_raw_value); - message = g_new0(struct bt_message_discarded_items, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one discarded items message."); - goto error; - } - - bt_message_init(&message->parent, type, - destroy_discarded_items_message, NULL); - message->stream = stream; - bt_object_get_no_null_check(message->stream); - - if (with_cs) { - BT_ASSERT(stream_class->default_clock_class); - message->default_begin_cs = bt_clock_snapshot_create( - stream_class->default_clock_class); - if (!message->default_begin_cs) { - goto error; - } - - bt_clock_snapshot_set_raw_value(message->default_begin_cs, - beginning_raw_value); - - message->default_end_cs = bt_clock_snapshot_create( - stream_class->default_clock_class); - if (!message->default_end_cs) { - goto error; - } - - bt_clock_snapshot_set_raw_value(message->default_end_cs, - end_raw_value); - } - - bt_property_uint_init(&message->count, - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, 0); - BT_LIB_LOGD("Created discarded items message object: " - "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, - stream, stream_class); - - return (void *) &message->parent; - -error: - return NULL; -} - -static inline -struct bt_stream *borrow_discarded_items_message_stream( - struct bt_message *message) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) message; - - BT_ASSERT(message); - return disc_items_msg->stream; -} - -static inline -void set_discarded_items_message_count(struct bt_message *message, - uint64_t count) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) message; - - BT_ASSERT(message); - BT_ASSERT_PRE_HOT(message, "Message", ": %!+n", message); - bt_property_uint_set(&disc_items_msg->count, count); -} - -static inline -enum bt_property_availability get_discarded_items_message_count( - const struct bt_message *message, uint64_t *count) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) message; - - BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); - BT_ASSERT(message); - *count = disc_items_msg->count.value; - return disc_items_msg->count.base.avail; -} - -static inline -const struct bt_clock_snapshot * -borrow_discarded_items_message_beginning_default_clock_snapshot_const( - const struct bt_message *message) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) message; - - BT_ASSERT(message); - BT_ASSERT_PRE(disc_items_msg->stream->class->default_clock_class, - "Message's stream's class has no default clock class: " - "%![msg-]+n, %![sc-]+S", - message, disc_items_msg->stream->class); - return disc_items_msg->default_begin_cs; -} - -static inline -const struct bt_clock_snapshot * -borrow_discarded_items_message_end_default_clock_snapshot_const( - const struct bt_message *message) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) message; - - BT_ASSERT(message); - BT_ASSERT_PRE(disc_items_msg->stream->class->default_clock_class, - "Message's stream's class has no default clock class: " - "%![msg-]+n, %![sc-]+S", - message, disc_items_msg->stream->class); - return disc_items_msg->default_end_cs; -} - -struct bt_message *bt_message_discarded_events_create( - struct bt_self_message_iterator *message_iterator, - const struct bt_stream *stream) -{ - return create_discarded_items_message(message_iterator, - BT_MESSAGE_TYPE_DISCARDED_EVENTS, (void *) stream, - false, 0, 0); -} - -struct bt_message *bt_message_discarded_events_create_with_default_clock_snapshots( - struct bt_self_message_iterator *message_iterator, - const struct bt_stream *stream, uint64_t beginning_raw_value, - uint64_t end_raw_value) -{ - return create_discarded_items_message(message_iterator, - BT_MESSAGE_TYPE_DISCARDED_EVENTS, (void *) stream, - true, beginning_raw_value, end_raw_value); -} - -struct bt_stream *bt_message_discarded_events_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - return borrow_discarded_items_message_stream(message); -} - -void bt_message_discarded_events_set_count(struct bt_message *message, - uint64_t count) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - set_discarded_items_message_count(message, count); -} - -const struct bt_clock_snapshot * -bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - return borrow_discarded_items_message_beginning_default_clock_snapshot_const( - msg); -} - -const struct bt_clock_snapshot * -bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - return borrow_discarded_items_message_end_default_clock_snapshot_const( - msg); -} - -const struct bt_stream * -bt_message_discarded_events_borrow_stream_const(const struct bt_message *message) -{ - return (void *) bt_message_discarded_events_borrow_stream( - (void *) message); -} - -enum bt_property_availability bt_message_discarded_events_get_count( - const struct bt_message *message, uint64_t *count) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - return get_discarded_items_message_count(message, count); -} - -struct bt_message *bt_message_discarded_packets_create( - struct bt_self_message_iterator *message_iterator, - const struct bt_stream *stream) -{ - return create_discarded_items_message(message_iterator, - BT_MESSAGE_TYPE_DISCARDED_PACKETS, (void *) stream, - false, 0, 0); -} - -struct bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots( - struct bt_self_message_iterator *message_iterator, - const struct bt_stream *stream, uint64_t beginning_raw_value, - uint64_t end_raw_value) -{ - return create_discarded_items_message(message_iterator, - BT_MESSAGE_TYPE_DISCARDED_PACKETS, (void *) stream, - true, beginning_raw_value, end_raw_value); -} - -struct bt_stream *bt_message_discarded_packets_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - return borrow_discarded_items_message_stream(message); -} - -void bt_message_discarded_packets_set_count(struct bt_message *message, - uint64_t count) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - set_discarded_items_message_count(message, count); -} - -const struct bt_clock_snapshot * -bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - return borrow_discarded_items_message_beginning_default_clock_snapshot_const( - msg); -} - -const struct bt_clock_snapshot * -bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - return borrow_discarded_items_message_end_default_clock_snapshot_const( - msg); -} - -const struct bt_stream * -bt_message_discarded_packets_borrow_stream_const(const struct bt_message *message) -{ - return (void *) bt_message_discarded_packets_borrow_stream( - (void *) message); -} - -enum bt_property_availability bt_message_discarded_packets_get_count( - const struct bt_message *message, uint64_t *count) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - return get_discarded_items_message_count(message, count); -} - -static inline -const struct bt_clock_class * -borrow_discarded_items_message_stream_class_default_clock_class( - const struct bt_message *msg) -{ - struct bt_message_discarded_items *disc_items_msg = (void *) msg; - - BT_ASSERT(msg); - return disc_items_msg->stream->class->default_clock_class; -} - -const struct bt_clock_class * -bt_message_discarded_events_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); - return borrow_discarded_items_message_stream_class_default_clock_class( - msg); -} - -const struct bt_clock_class * -bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); - return borrow_discarded_items_message_stream_class_default_clock_class( - msg); -} diff --git a/lib/graph/message/event.c b/lib/graph/message/event.c deleted file mode 100644 index 7544e33d..00000000 --- a/lib/graph/message/event.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-EVENT" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_ASSERT_PRE_FUNC -static inline bool event_class_has_trace(struct bt_event_class *event_class) -{ - struct bt_stream_class *stream_class; - - stream_class = bt_event_class_borrow_stream_class_inline(event_class); - BT_ASSERT(stream_class); - return bt_stream_class_borrow_trace_class(stream_class) != NULL; -} - -BT_HIDDEN -struct bt_message *bt_message_event_new( - struct bt_graph *graph) -{ - struct bt_message_event *message = NULL; - - message = g_new0(struct bt_message_event, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one event message."); - goto error; - } - - bt_message_init(&message->parent, BT_MESSAGE_TYPE_EVENT, - (bt_object_release_func) bt_message_event_recycle, graph); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(message); - -end: - return (void *) message; -} - -static inline -struct bt_message *create_event_message( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_event_class *c_event_class, - const struct bt_packet *c_packet, bool with_cs, - uint64_t raw_value) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - struct bt_message_event *message = NULL; - struct bt_event_class *event_class = (void *) c_event_class; - struct bt_stream_class *stream_class; - struct bt_packet *packet = (void *) c_packet; - struct bt_event *event; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - BT_ASSERT_PRE(event_class_has_trace(event_class), - "Event class is not part of a trace: %!+E", event_class); - stream_class = bt_event_class_borrow_stream_class_inline(event_class); - BT_ASSERT(stream_class); - BT_ASSERT_PRE((with_cs && stream_class->default_clock_class) || - (!with_cs && !stream_class->default_clock_class), - "Creating an event message with a default clock snapshot, but without " - "a default clock class, or without a default clock snapshot, " - "but with a default clock class: ", - "%![ec-]+E, %![sc-]+S, with-cs=%d, " - "cs-val=%" PRIu64, - event_class, stream_class, with_cs, raw_value); - BT_LIB_LOGD("Creating event message object: %![ec-]+E", event_class); - event = bt_event_create(event_class, packet); - if (unlikely(!event)) { - BT_LIB_LOGE("Cannot create event from event class: " - "%![ec-]+E", event_class); - goto error; - } - - /* - * Create message from pool _after_ we have everything - * (in this case, a valid event object) so that we never have an - * error condition with a non-NULL message object. - * Otherwise: - * - * * We cannot recycle the message on error because - * bt_message_event_recycle() expects a complete - * message (and the event or clock class priority map - * object could be unset). - * - * * We cannot destroy the message because we would need - * to notify the graph (pool owner) so that it removes the - * message from its message array. - */ - message = (void *) bt_message_create_from_pool( - &msg_iter->graph->event_msg_pool, msg_iter->graph); - if (unlikely(!message)) { - /* bt_message_create_from_pool() logs errors */ - goto error; - } - - if (with_cs) { - BT_ASSERT(stream_class->default_clock_class); - message->default_cs = bt_clock_snapshot_create( - stream_class->default_clock_class); - if (!message->default_cs) { - goto error; - } - - bt_clock_snapshot_set_raw_value(message->default_cs, raw_value); - } - - BT_ASSERT(!message->event); - message->event = event; - bt_packet_set_is_frozen(packet, true); - bt_event_class_freeze(event_class); - BT_LIB_LOGD("Created event message object: " - "%![msg-]+n, %![event-]+e", message, event); - goto end; - -error: - BT_ASSERT(!message); - bt_event_destroy(event); - -end: - return (void *) message; -} - -struct bt_message *bt_message_event_create( - struct bt_self_message_iterator *msg_iter, - const struct bt_event_class *event_class, - const struct bt_packet *packet) -{ - return create_event_message(msg_iter, event_class, packet, false, 0); -} - -struct bt_message *bt_message_event_create_with_default_clock_snapshot( - struct bt_self_message_iterator *msg_iter, - const struct bt_event_class *event_class, - const struct bt_packet *packet, - uint64_t raw_value) -{ - return create_event_message(msg_iter, event_class, packet, - true, raw_value); -} - -BT_HIDDEN -void bt_message_event_destroy(struct bt_message *msg) -{ - struct bt_message_event *event_msg = (void *) msg; - - BT_LIB_LOGD("Destroying event message: %!+n", msg); - - if (event_msg->event) { - BT_LIB_LOGD("Recycling event: %!+e", event_msg->event); - bt_event_recycle(event_msg->event); - event_msg->event = NULL; - } - - if (event_msg->default_cs) { - bt_clock_snapshot_recycle(event_msg->default_cs); - event_msg->default_cs = NULL; - } - - g_free(msg); -} - -BT_HIDDEN -void bt_message_event_recycle(struct bt_message *msg) -{ - struct bt_message_event *event_msg = (void *) msg; - struct bt_graph *graph; - - BT_ASSERT(event_msg); - - if (unlikely(!msg->graph)) { - bt_message_event_destroy(msg); - return; - } - - BT_LIB_LOGD("Recycling event message: %![msg-]+n, %![event-]+e", - msg, event_msg->event); - bt_message_reset(msg); - BT_ASSERT(event_msg->event); - bt_event_recycle(event_msg->event); - event_msg->event = NULL; - - if (event_msg->default_cs) { - bt_clock_snapshot_recycle(event_msg->default_cs); - event_msg->default_cs = NULL; - } - - graph = msg->graph; - msg->graph = NULL; - bt_object_pool_recycle_object(&graph->event_msg_pool, msg); -} - -static inline -struct bt_event *borrow_event(struct bt_message *message) -{ - struct bt_message_event *event_message; - - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_EVENT); - event_message = container_of(message, - struct bt_message_event, parent); - return event_message->event; -} - -struct bt_event *bt_message_event_borrow_event( - struct bt_message *message) -{ - return borrow_event(message); -} - -const struct bt_event *bt_message_event_borrow_event_const( - const struct bt_message *message) -{ - return borrow_event((void *) message); -} - -const struct bt_clock_snapshot * -bt_message_event_borrow_default_clock_snapshot_const( - const struct bt_message *msg) -{ - struct bt_message_event *event_msg = (void *) msg; - struct bt_stream_class *stream_class; - - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_EVENT); - stream_class = bt_event_class_borrow_stream_class_inline( - event_msg->event->class); - BT_ASSERT(stream_class); - BT_ASSERT_PRE(stream_class->default_clock_class, - "Message's stream's class has no default clock class: " - "%![msg-]+n, %![sc-]+S", msg, stream_class); - return event_msg->default_cs; -} - -const bt_clock_class * -bt_message_event_borrow_stream_class_default_clock_class_const( - const bt_message *msg) -{ - struct bt_message_event *event_msg = (void *) msg; - struct bt_stream_class *stream_class; - - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_EVENT); - stream_class = bt_event_class_borrow_stream_class_inline( - event_msg->event->class); - BT_ASSERT(stream_class); - return stream_class->default_clock_class; -} diff --git a/lib/graph/message/message-iterator-inactivity.c b/lib/graph/message/message-iterator-inactivity.c deleted file mode 100644 index 4cb0a8e0..00000000 --- a/lib/graph/message/message-iterator-inactivity.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-MESSAGE-ITERATOR-INACTIVITY" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void bt_message_message_iterator_inactivity_destroy(struct bt_object *obj) -{ - struct bt_message_message_iterator_inactivity *message = - (struct bt_message_message_iterator_inactivity *) obj; - - BT_LIB_LOGD("Destroying message iterator inactivity message: %!+n", - message); - - if (message->default_cs) { - bt_clock_snapshot_recycle(message->default_cs); - message->default_cs = NULL; - } - - g_free(message); -} - -struct bt_message *bt_message_message_iterator_inactivity_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_clock_class *default_clock_class, - uint64_t value_cycles) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - struct bt_message_message_iterator_inactivity *message; - struct bt_message *ret_msg = NULL; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(default_clock_class, "Default clock class"); - BT_LIB_LOGD("Creating message iterator inactivity message object: " - "%![iter-]+i, %![default-cc-]+K, value=%" PRIu64, msg_iter, - default_clock_class, value_cycles); - message = g_new0(struct bt_message_message_iterator_inactivity, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one message iterator " - "inactivity message."); - goto error; - } - bt_message_init(&message->parent, - BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY, - bt_message_message_iterator_inactivity_destroy, NULL); - ret_msg = &message->parent; - message->default_cs = bt_clock_snapshot_create( - (void *) default_clock_class); - if (!message->default_cs) { - goto error; - } - bt_clock_snapshot_set_raw_value(message->default_cs, value_cycles); - - BT_LIB_LOGD("Created message iterator inactivity message object: %!+n", - ret_msg); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(ret_msg); - -end: - return (void *) ret_msg; -} - -extern const struct bt_clock_snapshot * -bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - const bt_message *msg) -{ - struct bt_message_message_iterator_inactivity *inactivity = (void *) msg; - - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY); - return inactivity->default_cs; -} diff --git a/lib/graph/message/message.c b/lib/graph/message/message.c deleted file mode 100644 index 7cdd3d37..00000000 --- a/lib/graph/message/message.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG" -#include - -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_message_init(struct bt_message *message, - enum bt_message_type type, - bt_object_release_func release, - struct bt_graph *graph) -{ - BT_ASSERT(type >= 0 && type <= BT_MESSAGE_TYPE_DISCARDED_PACKETS); - message->type = type; - bt_object_init_shared(&message->base, release); - message->graph = graph; - - if (graph) { - bt_graph_add_message(graph, message); - } -} - -enum bt_message_type bt_message_get_type( - const struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - return message->type; -} - -BT_HIDDEN -void bt_message_unlink_graph(struct bt_message *msg) -{ - BT_ASSERT(msg); - msg->graph = NULL; -} - -void bt_message_get_ref(const struct bt_message *message) -{ - bt_object_get_ref(message); -} - -void bt_message_put_ref(const struct bt_message *message) -{ - bt_object_put_ref(message); -} diff --git a/lib/graph/message/packet.c b/lib/graph/message/packet.c deleted file mode 100644 index 9ed9a0c4..00000000 --- a/lib/graph/message/packet.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-PACKET" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static inline -struct bt_message *new_packet_message(struct bt_graph *graph, - enum bt_message_type type, bt_object_release_func recycle_func) -{ - struct bt_message_packet *message; - - message = g_new0(struct bt_message_packet, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one packet message."); - goto error; - } - - bt_message_init(&message->parent, type, recycle_func, graph); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(message); - -end: - return (void *) message; -} - -BT_HIDDEN -struct bt_message *bt_message_packet_beginning_new(struct bt_graph *graph) -{ - return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_BEGINNING, - (bt_object_release_func) bt_message_packet_beginning_recycle); -} - -BT_HIDDEN -struct bt_message *bt_message_packet_end_new(struct bt_graph *graph) -{ - return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_END, - (bt_object_release_func) bt_message_packet_end_recycle); -} - -static inline -struct bt_message *create_packet_message( - struct bt_self_component_port_input_message_iterator *msg_iter, - struct bt_packet *packet, struct bt_object_pool *pool, - bool with_cs, uint64_t raw_value) -{ - struct bt_message_packet *message = NULL; - struct bt_stream *stream; - struct bt_stream_class *stream_class; - bool need_cs; - - BT_ASSERT(msg_iter); - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - stream = bt_packet_borrow_stream(packet); - BT_ASSERT(stream); - stream_class = bt_stream_borrow_class(stream); - BT_ASSERT(stream_class); - - if (pool == &msg_iter->graph->packet_begin_msg_pool) { - need_cs = stream_class->packets_have_beginning_default_clock_snapshot; - } else { - need_cs = stream_class->packets_have_end_default_clock_snapshot; - } - - /* - * `packet_has_default_clock_snapshot` implies that the stream - * class has a default clock class (precondition). - */ - BT_ASSERT_PRE(need_cs ? with_cs : true, - "Unexpected stream class configuration when creating " - "a packet beginning or end message: " - "a default clock snapshot is needed, but none was provided: " - "%![stream-]+s, %![sc-]+S, with-cs=%d, " - "cs-val=%" PRIu64, - stream, stream_class, with_cs, raw_value); - BT_ASSERT_PRE(!need_cs ? !with_cs : true, - "Unexpected stream class configuration when creating " - "a packet beginning or end message: " - "no default clock snapshot is needed, but one was provided: " - "%![stream-]+s, %![sc-]+S, with-cs=%d, " - "cs-val=%" PRIu64, - stream, stream_class, with_cs, raw_value); - BT_LIB_LOGD("Creating packet message object: " - "%![packet-]+a, %![stream-]+s, %![sc-]+S", - packet, stream, stream_class); - message = (void *) bt_message_create_from_pool(pool, msg_iter->graph); - if (!message) { - /* bt_message_create_from_pool() logs errors */ - goto end; - } - - if (with_cs) { - BT_ASSERT(stream_class->default_clock_class); - message->default_cs = bt_clock_snapshot_create( - stream_class->default_clock_class); - if (!message->default_cs) { - bt_object_put_no_null_check(message); - message = NULL; - goto end; - } - - bt_clock_snapshot_set_raw_value(message->default_cs, raw_value); - } - - BT_ASSERT(!message->packet); - message->packet = packet; - bt_object_get_no_null_check_no_parent_check( - &message->packet->base); - bt_packet_set_is_frozen(packet, true); - BT_LIB_LOGD("Created packet message object: " - "%![msg-]+n, %![packet-]+a, %![stream-]+s, %![sc-]+S", - message, packet, stream, stream_class); - goto end; - -end: - return (void *) message; -} - -struct bt_message *bt_message_packet_beginning_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_packet *packet) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - return create_packet_message(msg_iter, (void *) packet, - &msg_iter->graph->packet_begin_msg_pool, false, 0); -} - -struct bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_packet *packet, uint64_t raw_value) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - return create_packet_message(msg_iter, (void *) packet, - &msg_iter->graph->packet_begin_msg_pool, true, raw_value); -} - -struct bt_message *bt_message_packet_end_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_packet *packet) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - return create_packet_message(msg_iter, (void *) packet, - &msg_iter->graph->packet_end_msg_pool, false, 0); -} - -struct bt_message *bt_message_packet_end_create_with_default_clock_snapshot( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_packet *packet, uint64_t raw_value) -{ - struct bt_self_component_port_input_message_iterator *msg_iter = - (void *) self_msg_iter; - - BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); - return create_packet_message(msg_iter, (void *) packet, - &msg_iter->graph->packet_end_msg_pool, true, raw_value); -} - -BT_HIDDEN -void bt_message_packet_destroy(struct bt_message *msg) -{ - struct bt_message_packet *packet_msg = (void *) msg; - - BT_LIB_LOGD("Destroying packet message: %!+n", msg); - BT_LIB_LOGD("Putting packet: %!+a", packet_msg->packet); - BT_OBJECT_PUT_REF_AND_RESET(packet_msg->packet); - - if (packet_msg->default_cs) { - bt_clock_snapshot_recycle(packet_msg->default_cs); - packet_msg->default_cs = NULL; - } - - g_free(msg); -} - -static inline -void recycle_packet_message(struct bt_message *msg, struct bt_object_pool *pool) -{ - struct bt_message_packet *packet_msg = (void *) msg; - - BT_LIB_LOGD("Recycling packet message: %!+n", msg); - bt_message_reset(msg); - bt_object_put_no_null_check(&packet_msg->packet->base); - - if (packet_msg->default_cs) { - bt_clock_snapshot_recycle(packet_msg->default_cs); - packet_msg->default_cs = NULL; - } - - packet_msg->packet = NULL; - msg->graph = NULL; - bt_object_pool_recycle_object(pool, msg); -} - -BT_HIDDEN -void bt_message_packet_beginning_recycle(struct bt_message *msg) -{ - BT_ASSERT(msg); - - if (unlikely(!msg->graph)) { - bt_message_packet_destroy(msg); - return; - } - - recycle_packet_message(msg, &msg->graph->packet_begin_msg_pool); -} - -BT_HIDDEN -void bt_message_packet_end_recycle(struct bt_message *msg) -{ - BT_ASSERT(msg); - - if (unlikely(!msg->graph)) { - bt_message_packet_destroy(msg); - return; - } - - recycle_packet_message(msg, &msg->graph->packet_end_msg_pool); -} - -struct bt_packet *bt_message_packet_beginning_borrow_packet( - struct bt_message *message) -{ - struct bt_message_packet *packet_msg = (void *) message; - - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, - BT_MESSAGE_TYPE_PACKET_BEGINNING); - return packet_msg->packet; -} - -const struct bt_packet *bt_message_packet_beginning_borrow_packet_const( - const struct bt_message *message) -{ - return bt_message_packet_beginning_borrow_packet( - (void *) message); -} - -struct bt_packet *bt_message_packet_end_borrow_packet( - struct bt_message *message) -{ - struct bt_message_packet *packet_msg = (void *) message; - - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, - BT_MESSAGE_TYPE_PACKET_END); - return packet_msg->packet; -} - -const struct bt_packet *bt_message_packet_end_borrow_packet_const( - const struct bt_message *message) -{ - return bt_message_packet_end_borrow_packet( - (void *) message); -} - -static inline -const struct bt_clock_snapshot * -borrow_packet_message_default_clock_snapshot_const( - const struct bt_message *message) -{ - struct bt_message_packet *packet_msg = (void *) message; - - BT_ASSERT(message); - BT_ASSERT_PRE(packet_msg->packet->stream->class->default_clock_class, - "Message's stream's class has no default clock class: " - "%![msg-]+n, %![sc-]+S", - message, packet_msg->packet->stream->class); - return packet_msg->default_cs; -} - -const struct bt_clock_snapshot * -bt_message_packet_beginning_borrow_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_BEGINNING); - return borrow_packet_message_default_clock_snapshot_const(msg); -} - -const struct bt_clock_snapshot * -bt_message_packet_end_borrow_default_clock_snapshot_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_END); - return borrow_packet_message_default_clock_snapshot_const(msg); -} - -static inline -const struct bt_clock_class * -borrow_packet_message_stream_class_default_clock_class( - const struct bt_message *msg) -{ - struct bt_message_packet *packet_msg = (void *) msg; - - BT_ASSERT(msg); - return packet_msg->packet->stream->class->default_clock_class; -} - -const struct bt_clock_class * -bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_BEGINNING); - return borrow_packet_message_stream_class_default_clock_class(msg); -} - -const struct bt_clock_class * -bt_message_packet_end_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_END); - return borrow_packet_message_stream_class_default_clock_class(msg); -} diff --git a/lib/graph/message/stream-activity.c b/lib/graph/message/stream-activity.c deleted file mode 100644 index 7ecbd461..00000000 --- a/lib/graph/message/stream-activity.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-STREAM-ACTIVITY" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_stream_activity_message(struct bt_object *obj) -{ - struct bt_message_stream_activity *message = (void *) obj; - - BT_LIB_LOGD("Destroying stream activity message: %!+n", message); - BT_LIB_LOGD("Putting stream: %!+s", message->stream); - BT_OBJECT_PUT_REF_AND_RESET(message->stream); - - if (message->default_cs) { - bt_clock_snapshot_recycle(message->default_cs); - message->default_cs = NULL; - } - - g_free(message); -} - -static inline -struct bt_message *create_stream_activity_message( - struct bt_self_message_iterator *self_msg_iter, - struct bt_stream *stream, enum bt_message_type type) -{ - struct bt_message_stream_activity *message; - struct bt_stream_class *stream_class; - - BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - stream_class = bt_stream_borrow_class(stream); - BT_ASSERT(stream_class); - BT_LIB_LOGD("Creating stream activity message object: " - "type=%s, %![stream-]+s, %![sc-]+S", - bt_message_type_string(type), stream, stream_class); - message = g_new0(struct bt_message_stream_activity, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one stream activity message."); - goto error; - } - - bt_message_init(&message->parent, type, - destroy_stream_activity_message, NULL); - message->stream = stream; - bt_object_get_no_null_check(message->stream); - - if (stream_class->default_clock_class) { - message->default_cs = bt_clock_snapshot_create( - stream_class->default_clock_class); - if (!message->default_cs) { - goto error; - } - } - - message->default_cs_state = - BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN; - BT_LIB_LOGD("Created stream activity message object: " - "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, - stream, stream_class); - - return (void *) &message->parent; - -error: - return NULL; -} - -struct bt_message *bt_message_stream_activity_beginning_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_stream *stream) -{ - return create_stream_activity_message(self_msg_iter, (void *) stream, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); -} - -struct bt_message *bt_message_stream_activity_end_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_stream *stream) -{ - return create_stream_activity_message(self_msg_iter, (void *) stream, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); -} - -static inline -struct bt_stream *borrow_stream_activity_message_stream( - struct bt_message *message) -{ - struct bt_message_stream_activity *stream_act_msg = (void *) message; - - BT_ASSERT(message); - return stream_act_msg->stream; -} - -struct bt_stream *bt_message_stream_activity_beginning_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); - return borrow_stream_activity_message_stream(message); -} - -struct bt_stream *bt_message_stream_activity_end_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); - return borrow_stream_activity_message_stream(message); -} - -const struct bt_stream *bt_message_stream_activity_beginning_borrow_stream_const( - const struct bt_message *message) -{ - return bt_message_stream_activity_beginning_borrow_stream( - (void *) message); -} - -const struct bt_stream *bt_message_stream_activity_end_borrow_stream_const( - const struct bt_message *message) -{ - return bt_message_stream_activity_end_borrow_stream((void *) message); -} - -static inline -void set_stream_activity_message_default_clock_snapshot( - struct bt_message *msg, uint64_t value_cycles) -{ - struct bt_message_stream_activity *stream_act_msg = (void *) msg; - struct bt_stream_class *sc; - - BT_ASSERT(msg); - BT_ASSERT_PRE_HOT(msg, "Message", ": %!+n", msg); - sc = stream_act_msg->stream->class; - BT_ASSERT(sc); - BT_ASSERT_PRE(sc->default_clock_class, - "Message's stream's class has no default clock class: " - "%![msg-]+n, %![sc-]+S", msg, sc); - bt_clock_snapshot_set_raw_value(stream_act_msg->default_cs, - value_cycles); - stream_act_msg->default_cs_state = - BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN; - BT_LIB_LOGV("Set stream activity message's default clock snapshot: " - "%![msg-]+n, value=%" PRIu64, msg, value_cycles); -} - -void bt_message_stream_activity_beginning_set_default_clock_snapshot( - struct bt_message *msg, uint64_t raw_value) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); - set_stream_activity_message_default_clock_snapshot(msg, raw_value); -} - -void bt_message_stream_activity_end_set_default_clock_snapshot( - struct bt_message *msg, uint64_t raw_value) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); - set_stream_activity_message_default_clock_snapshot(msg, raw_value); -} - -static inline -enum bt_message_stream_activity_clock_snapshot_state -borrow_stream_activity_message_default_clock_snapshot_const( - const bt_message *msg, const bt_clock_snapshot **snapshot) -{ - const struct bt_message_stream_activity *stream_act_msg = - (const void *) msg; - - BT_ASSERT_PRE_NON_NULL(snapshot, "Clock snapshot (output)"); - *snapshot = stream_act_msg->default_cs; - return stream_act_msg->default_cs_state; -} - -enum bt_message_stream_activity_clock_snapshot_state -bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - const bt_message *msg, const bt_clock_snapshot **snapshot) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); - return borrow_stream_activity_message_default_clock_snapshot_const(msg, - snapshot); -} - -enum bt_message_stream_activity_clock_snapshot_state -bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - const bt_message *msg, const bt_clock_snapshot **snapshot) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); - return borrow_stream_activity_message_default_clock_snapshot_const(msg, - snapshot); -} - -static inline -void set_stream_activity_message_default_clock_snapshot_state( - struct bt_message *msg, - enum bt_message_stream_activity_clock_snapshot_state state) -{ - struct bt_message_stream_activity *stream_act_msg = (void *) msg; - - BT_ASSERT(msg); - BT_ASSERT_PRE_HOT(msg, "Message", ": %!+n", msg); - BT_ASSERT_PRE(state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN, - "Invalid clock snapshot state: %![msg-]+n, state=%s", - msg, - bt_message_stream_activity_clock_snapshot_state_string(state)); - stream_act_msg->default_cs_state = state; - BT_LIB_LOGV("Set stream activity message's default clock snapshot state: " - "%![msg-]+n, state=%s", msg, - bt_message_stream_activity_clock_snapshot_state_string(state)); -} - -void bt_message_stream_activity_beginning_set_default_clock_snapshot_state( - struct bt_message *msg, - enum bt_message_stream_activity_clock_snapshot_state state) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); - set_stream_activity_message_default_clock_snapshot_state(msg, state); -} - -void bt_message_stream_activity_end_set_default_clock_snapshot_state( - struct bt_message *msg, - enum bt_message_stream_activity_clock_snapshot_state state) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); - set_stream_activity_message_default_clock_snapshot_state(msg, state); -} - -static inline -const struct bt_clock_class * -borrow_stream_activity_message_stream_class_default_clock_class( - const struct bt_message *msg) -{ - struct bt_message_stream_activity *stream_act_msg = (void *) msg; - - BT_ASSERT(msg); - return stream_act_msg->stream->class->default_clock_class; -} - -const struct bt_clock_class * -bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, - BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); - return borrow_stream_activity_message_stream_class_default_clock_class( - msg); -} - -const struct bt_clock_class * -bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( - const struct bt_message *msg) -{ - BT_ASSERT_PRE_NON_NULL(msg, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); - return borrow_stream_activity_message_stream_class_default_clock_class( - msg); -} diff --git a/lib/graph/message/stream.c b/lib/graph/message/stream.c deleted file mode 100644 index 935942a7..00000000 --- a/lib/graph/message/stream.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "MSG-STREAM" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_stream_message(struct bt_object *obj) -{ - struct bt_message_stream *message = (void *) obj; - - BT_LIB_LOGD("Destroying stream message: %!+n", message); - BT_LIB_LOGD("Putting stream: %!+s", message->stream); - BT_OBJECT_PUT_REF_AND_RESET(message->stream); - g_free(message); -} - -static inline -struct bt_message *create_stream_message( - struct bt_self_message_iterator *self_msg_iter, - struct bt_stream *stream, enum bt_message_type type) -{ - struct bt_message_stream *message; - struct bt_stream_class *stream_class; - - BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - stream_class = bt_stream_borrow_class(stream); - BT_ASSERT(stream_class); - BT_LIB_LOGD("Creating stream message object: " - "type=%s, %![stream-]+s, %![sc-]+S", - bt_message_type_string(type), stream, stream_class); - message = g_new0(struct bt_message_stream, 1); - if (!message) { - BT_LOGE_STR("Failed to allocate one stream message."); - goto error; - } - - bt_message_init(&message->parent, type, - destroy_stream_message, NULL); - message->stream = stream; - bt_object_get_no_null_check(message->stream); - BT_LIB_LOGD("Created stream message object: " - "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, - stream, stream_class); - - return (void *) &message->parent; - -error: - return NULL; -} - -struct bt_message *bt_message_stream_beginning_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_stream *stream) -{ - return create_stream_message(self_msg_iter, (void *) stream, - BT_MESSAGE_TYPE_STREAM_BEGINNING); -} - -struct bt_message *bt_message_stream_end_create( - struct bt_self_message_iterator *self_msg_iter, - const struct bt_stream *stream) -{ - return create_stream_message(self_msg_iter, (void *) stream, - BT_MESSAGE_TYPE_STREAM_END); -} - -static inline -struct bt_stream *borrow_stream_message_stream(struct bt_message *message) -{ - struct bt_message_stream *stream_msg; - - BT_ASSERT(message); - stream_msg = (void *) message; - return stream_msg->stream; -} - -struct bt_stream *bt_message_stream_beginning_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_STREAM_BEGINNING); - return borrow_stream_message_stream(message); -} - -struct bt_stream *bt_message_stream_end_borrow_stream( - struct bt_message *message) -{ - BT_ASSERT_PRE_NON_NULL(message, "Message"); - BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_STREAM_END); - return borrow_stream_message_stream(message); -} - -const struct bt_stream *bt_message_stream_beginning_borrow_stream_const( - const struct bt_message *message) -{ - return bt_message_stream_beginning_borrow_stream( - (void *) message); -} - -const struct bt_stream *bt_message_stream_end_borrow_stream_const( - const struct bt_message *message) -{ - return bt_message_stream_end_borrow_stream( - (void *) message); -} diff --git a/lib/graph/port.c b/lib/graph/port.c deleted file mode 100644 index f119d60b..00000000 --- a/lib/graph/port.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PORT" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_port(struct bt_object *obj) -{ - struct bt_port *port = (void *) obj; - - BT_LIB_LOGD("Destroying port: %!+p", port); - - if (port->name) { - g_string_free(port->name, TRUE); - port->name = NULL; - } - - g_free(port); -} - -BT_HIDDEN -struct bt_port *bt_port_create(struct bt_component *parent_component, - enum bt_port_type type, const char *name, void *user_data) -{ - struct bt_port *port = NULL; - - BT_ASSERT(name); - BT_ASSERT(parent_component); - BT_ASSERT(type == BT_PORT_TYPE_INPUT || type == BT_PORT_TYPE_OUTPUT); - BT_ASSERT(strlen(name) > 0); - port = g_new0(struct bt_port, 1); - if (!port) { - BT_LOGE_STR("Failed to allocate one port."); - goto end; - } - - BT_LIB_LOGD("Creating port for component: %![comp-]+c, port-type=%s, " - "port-name=\"%s\"", parent_component, bt_port_type_string(type), - name); - bt_object_init_shared_with_parent(&port->base, destroy_port); - port->name = g_string_new(name); - if (!port->name) { - BT_LOGE_STR("Failed to allocate one GString."); - BT_OBJECT_PUT_REF_AND_RESET(port); - goto end; - } - - port->type = type; - port->user_data = user_data; - bt_object_set_parent(&port->base, &parent_component->base); - BT_LIB_LOGD("Created port for component: " - "%![comp-]+c, %![port-]+p", parent_component, port); - -end: - return port; -} - -const char *bt_port_get_name(const struct bt_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return port->name->str; -} - -enum bt_port_type bt_port_get_type(const struct bt_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return port->type; -} - -const struct bt_connection *bt_port_borrow_connection_const( - const struct bt_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return port->connection; -} - -const struct bt_component *bt_port_borrow_component_const( - const struct bt_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return bt_port_borrow_component_inline(port); -} - -struct bt_self_component *bt_self_component_port_borrow_component( - struct bt_self_component_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return (void *) bt_object_borrow_parent((void *) port); -} - -BT_HIDDEN -void bt_port_set_connection(struct bt_port *port, - struct bt_connection *connection) -{ - /* - * Don't take a reference on connection as its existence is - * guaranteed by the existence of the graph in which the - * connection exists. - */ - port->connection = connection; - BT_LIB_LOGV("Set port's connection: %![port-]+p, %![conn-]+x", port, - connection); -} - -bt_bool bt_port_is_connected(const struct bt_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return port->connection ? BT_TRUE : BT_FALSE; -} - -void *bt_self_component_port_get_data(const struct bt_self_component_port *port) -{ - BT_ASSERT_PRE_NON_NULL(port, "Port"); - return ((struct bt_port *) port)->user_data; -} - -void bt_port_get_ref(const struct bt_port *port) -{ - bt_object_get_ref(port); -} - -void bt_port_put_ref(const struct bt_port *port) -{ - bt_object_put_ref(port); -} - -void bt_port_input_get_ref(const struct bt_port_input *port_input) -{ - bt_object_get_ref(port_input); -} - -void bt_port_input_put_ref(const struct bt_port_input *port_input) -{ - bt_object_put_ref(port_input); -} - -void bt_port_output_get_ref(const struct bt_port_output *port_output) -{ - bt_object_get_ref(port_output); -} - -void bt_port_output_put_ref(const struct bt_port_output *port_output) -{ - bt_object_put_ref(port_output); -} diff --git a/lib/graph/query-executor.c b/lib/graph/query-executor.c deleted file mode 100644 index 5c8e33e1..00000000 --- a/lib/graph/query-executor.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "QUERY-EXECUTOR" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void bt_query_executor_destroy(struct bt_object *obj) -{ - struct bt_query_executor *query_exec = - container_of(obj, struct bt_query_executor, base); - - BT_LOGD("Destroying query executor: addr=%p", query_exec); - g_free(query_exec); -} - -struct bt_query_executor *bt_query_executor_create(void) -{ - struct bt_query_executor *query_exec; - - BT_LOGD_STR("Creating query executor."); - query_exec = g_new0(struct bt_query_executor, 1); - if (!query_exec) { - BT_LOGE_STR("Failed to allocate one query executor."); - goto end; - } - - bt_object_init_shared(&query_exec->base, - bt_query_executor_destroy); - BT_LOGD("Created query executor: addr=%p", query_exec); - -end: - return (void *) query_exec; -} - -enum bt_query_executor_status bt_query_executor_query( - struct bt_query_executor *query_exec, - const struct bt_component_class *comp_cls, - const char *object, const struct bt_value *params, - const struct bt_value **user_result) -{ - typedef enum bt_query_status (*method_t)(void *, const void *, - const void *, const void *, const void *); - - enum bt_query_status status; - enum bt_query_executor_status exec_status; - method_t method = NULL; - - BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); - BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); - BT_ASSERT_PRE_NON_NULL(object, "Object"); - BT_ASSERT_PRE_NON_NULL(user_result, "Result (output)"); - BT_ASSERT_PRE(!query_exec->canceled, "Query executor is canceled."); - - if (!params) { - params = bt_value_null; - } - - switch (comp_cls->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - { - struct bt_component_class_source *src_cc = (void *) comp_cls; - - method = (method_t) src_cc->methods.query; - break; - } - case BT_COMPONENT_CLASS_TYPE_FILTER: - { - struct bt_component_class_filter *flt_cc = (void *) comp_cls; - - method = (method_t) flt_cc->methods.query; - break; - } - case BT_COMPONENT_CLASS_TYPE_SINK: - { - struct bt_component_class_sink *sink_cc = (void *) comp_cls; - - method = (method_t) sink_cc->methods.query; - break; - } - default: - abort(); - } - - if (!method) { - /* Not an error: nothing to query */ - BT_LIB_LOGD("Component class has no registered query method: " - "%!+C", comp_cls); - exec_status = BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED; - goto end; - } - - BT_LIB_LOGD("Calling user's query method: " - "query-exec-addr=%p, %![cc-]+C, object=\"%s\", %![params-]+v", - query_exec, comp_cls, object, params); - *user_result = NULL; - status = method((void *) comp_cls, query_exec, object, params, - user_result); - BT_LIB_LOGD("User method returned: status=%s, %![res-]+v", - bt_query_status_string(status), *user_result); - BT_ASSERT_PRE(status != BT_QUERY_STATUS_OK || *user_result, - "User method returned `BT_QUERY_STATUS_OK` without a result."); - exec_status = (int) status; - if (query_exec->canceled) { - BT_OBJECT_PUT_REF_AND_RESET(*user_result); - exec_status = BT_QUERY_EXECUTOR_STATUS_CANCELED; - goto end; - } - -end: - return exec_status; -} - -enum bt_query_executor_status bt_query_executor_cancel( - struct bt_query_executor *query_exec) -{ - BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); - query_exec->canceled = BT_TRUE; - BT_LOGV("Canceled query executor: addr=%p", query_exec); - return BT_QUERY_EXECUTOR_STATUS_OK; -} - -bt_bool bt_query_executor_is_canceled(const struct bt_query_executor *query_exec) -{ - BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); - return query_exec->canceled; -} - -void bt_query_executor_get_ref(const struct bt_query_executor *query_executor) -{ - bt_object_get_ref(query_executor); -} - -void bt_query_executor_put_ref(const struct bt_query_executor *query_executor) -{ - bt_object_put_ref(query_executor); -} diff --git a/lib/lib-logging.c b/lib/lib-logging.c deleted file mode 100644 index 8b16a011..00000000 --- a/lib/lib-logging.c +++ /dev/null @@ -1,1420 +0,0 @@ -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "LIB-LOGGING" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LIB_LOGGING_BUF_SIZE (4096 * 4) - -static __thread char lib_logging_buf[LIB_LOGGING_BUF_SIZE]; - -#define BUF_APPEND(_fmt, ...) \ - do { \ - int _count; \ - size_t _size = LIB_LOGGING_BUF_SIZE - \ - (size_t) (*buf_ch - lib_logging_buf); \ - _count = snprintf(*buf_ch, _size, (_fmt), __VA_ARGS__); \ - BT_ASSERT(_count >= 0); \ - *buf_ch += MIN(_count, _size); \ - if (*buf_ch >= lib_logging_buf + LIB_LOGGING_BUF_SIZE - 1) { \ - return; \ - } \ - } while (0) - -#define BUF_APPEND_UUID(_uuid) \ - do { \ - BUF_APPEND(", %suuid=", prefix); \ - format_uuid(buf_ch, (_uuid)); \ - } while (0) - -#define PRFIELD(_expr) prefix, (_expr) - -#define PRFIELD_GSTRING(_expr) PRFIELD((_expr) ? (_expr)->str : NULL) - -#define TMP_PREFIX_LEN 64 -#define SET_TMP_PREFIX(_prefix2) \ - do { \ - snprintf(tmp_prefix, TMP_PREFIX_LEN - 1, "%s%s", \ - prefix, (_prefix2)); \ - tmp_prefix[TMP_PREFIX_LEN - 1] = '\0'; \ - } while (0) - -static inline void format_component(char **buf_ch, bool extended, - const char *prefix, const struct bt_component *component); - -static inline void format_port(char **buf_ch, bool extended, - const char *prefix, const struct bt_port *port); - -static inline void format_connection(char **buf_ch, bool extended, - const char *prefix, const struct bt_connection *connection); - -static inline void format_clock_snapshot(char **buf_ch, bool extended, - const char *prefix, const struct bt_clock_snapshot *clock_snapshot); - -static inline void format_field_path(char **buf_ch, bool extended, - const char *prefix, const struct bt_field_path *field_path); - -static inline void format_object(char **buf_ch, bool extended, - const char *prefix, const struct bt_object *obj) -{ - BUF_APPEND(", %sref-count=%llu", prefix, obj->ref_count); -} - -static inline void format_uuid(char **buf_ch, bt_uuid uuid) -{ - BUF_APPEND("\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - (unsigned int) uuid[0], - (unsigned int) uuid[1], - (unsigned int) uuid[2], - (unsigned int) uuid[3], - (unsigned int) uuid[4], - (unsigned int) uuid[5], - (unsigned int) uuid[6], - (unsigned int) uuid[7], - (unsigned int) uuid[8], - (unsigned int) uuid[9], - (unsigned int) uuid[10], - (unsigned int) uuid[11], - (unsigned int) uuid[12], - (unsigned int) uuid[13], - (unsigned int) uuid[14], - (unsigned int) uuid[15]); -} - -static inline void format_object_pool(char **buf_ch, bool extended, - const char *prefix, const struct bt_object_pool *pool) -{ - BUF_APPEND(", %ssize=%zu", PRFIELD(pool->size)); - - if (pool->objects) { - BUF_APPEND(", %scap=%u", PRFIELD(pool->objects->len)); - } -} - -static inline void format_integer_field_class(char **buf_ch, - bool extended, const char *prefix, - const struct bt_field_class *field_class) -{ - const struct bt_field_class_integer *int_fc = - (const void *) field_class; - - BUF_APPEND(", %srange-size=%" PRIu64 ", %sbase=%s", - PRFIELD(int_fc->range), - PRFIELD(bt_common_field_class_integer_preferred_display_base_string(int_fc->base))); -} - -static inline void format_array_field_class(char **buf_ch, - bool extended, const char *prefix, - const struct bt_field_class *field_class) -{ - const struct bt_field_class_array *array_fc = - (const void *) field_class; - - BUF_APPEND(", %selement-fc-addr=%p, %selement-fc-type=%s", - PRFIELD(array_fc->element_fc), - PRFIELD(bt_common_field_class_type_string(array_fc->element_fc->type))); -} - -static inline void format_field_class(char **buf_ch, bool extended, - const char *prefix, const struct bt_field_class *field_class) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %stype=%s", - PRFIELD(bt_common_field_class_type_string(field_class->type))); - - if (extended) { - BUF_APPEND(", %sis-frozen=%d", PRFIELD(field_class->frozen)); - BUF_APPEND(", %sis-part-of-trace-class=%d", - PRFIELD(field_class->part_of_trace_class)); - } else { - return; - } - - switch (field_class->type) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - { - format_integer_field_class(buf_ch, extended, prefix, field_class); - break; - } - case BT_FIELD_CLASS_TYPE_REAL: - { - const struct bt_field_class_real *real_fc = (void *) field_class; - - BUF_APPEND(", %sis-single-precision=%d", - PRFIELD(real_fc->is_single_precision)); - break; - } - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - { - const struct bt_field_class_enumeration *enum_fc = - (const void *) field_class; - - format_integer_field_class(buf_ch, extended, prefix, field_class); - BUF_APPEND(", %smapping-count=%u", - PRFIELD(enum_fc->mappings->len)); - break; - } - case BT_FIELD_CLASS_TYPE_STRUCTURE: - { - const struct bt_field_class_structure *struct_fc = - (const void *) field_class; - - if (struct_fc->common.named_fcs) { - BUF_APPEND(", %smember-count=%u", - PRFIELD(struct_fc->common.named_fcs->len)); - } - - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - { - const struct bt_field_class_static_array *array_fc = - (const void *) field_class; - - format_array_field_class(buf_ch, extended, prefix, field_class); - BUF_APPEND(", %slength=%" PRIu64, PRFIELD(array_fc->length)); - break; - } - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - const struct bt_field_class_dynamic_array *array_fc = - (const void *) field_class; - - format_array_field_class(buf_ch, extended, prefix, field_class); - - if (array_fc->length_fc) { - SET_TMP_PREFIX("length-fc-"); - format_field_class(buf_ch, extended, tmp_prefix, - array_fc->length_fc); - } - - if (array_fc->length_field_path) { - SET_TMP_PREFIX("length-field-path-"); - format_field_path(buf_ch, extended, tmp_prefix, - array_fc->length_field_path); - } - - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - { - const struct bt_field_class_variant *var_fc = - (const void *) field_class; - - if (var_fc->common.named_fcs) { - BUF_APPEND(", %soption-count=%u", - PRFIELD(var_fc->common.named_fcs->len)); - } - - if (var_fc->selector_fc) { - SET_TMP_PREFIX("selector-fc-"); - format_field_class(buf_ch, extended, tmp_prefix, - var_fc->selector_fc); - } - - if (var_fc->selector_field_path) { - SET_TMP_PREFIX("selector-field-path-"); - format_field_path(buf_ch, extended, tmp_prefix, - var_fc->selector_field_path); - } - - break; - } - default: - break; - } -} - -static inline void format_field_integer_extended(char **buf_ch, - const char *prefix, const struct bt_field *field) -{ - const struct bt_field_integer *integer = (void *) field; - const struct bt_field_class_integer *field_class = - (void *) field->class; - const char *fmt = NULL; - - BT_ASSERT(field_class); - - if (field_class->base == BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL) { - fmt = ", %svalue=%" PRIo64; - } else if (field_class->base == BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL) { - fmt = ", %svalue=%" PRIx64; - } - - if (field_class->common.type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || - field_class->common.type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { - if (!fmt) { - fmt = ", %svalue=%" PRId64; - } - - BUF_APPEND(fmt, PRFIELD(integer->value.i)); - } else { - if (!fmt) { - fmt = ", %svalue=%" PRIu64; - } - - BUF_APPEND(fmt, PRFIELD(integer->value.u)); - } -} - -static inline void format_field(char **buf_ch, bool extended, - const char *prefix, const struct bt_field *field) -{ - BUF_APPEND(", %sis-set=%d", PRFIELD(field->is_set)); - - if (extended) { - BUF_APPEND(", %sis-frozen=%d", PRFIELD(field->frozen)); - } - - BUF_APPEND(", %sclass-addr=%p", PRFIELD(field->class)); - - if (!field->class) { - return; - } - - BUF_APPEND(", %sclass-type=%s", - PRFIELD(bt_common_field_class_type_string(field->class->type))); - - if (!extended || !field->is_set) { - return; - } - - switch (field->class->type) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - { - format_field_integer_extended(buf_ch, prefix, field); - break; - } - case BT_FIELD_CLASS_TYPE_REAL: - { - const struct bt_field_real *real_field = (const void *) field; - - BUF_APPEND(", %svalue=%f", PRFIELD(real_field->value)); - break; - } - case BT_FIELD_CLASS_TYPE_STRING: - { - const struct bt_field_string *str = (const void *) field; - - if (str->buf) { - BT_ASSERT(str->buf->data); - BUF_APPEND(", %spartial-value=\"%.32s\"", - PRFIELD(str->buf->data)); - } - - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - const struct bt_field_array *array_field = (const void *) field; - - BUF_APPEND(", %slength=%" PRIu64, PRFIELD(array_field->length)); - - if (array_field->fields) { - BUF_APPEND(", %sallocated-length=%u", - PRFIELD(array_field->fields->len)); - } - - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - { - const struct bt_field_variant *var_field = (const void *) field; - - BUF_APPEND(", %sselected-field-index=%" PRIu64, - PRFIELD(var_field->selected_index)); - break; - } - default: - break; - } -} - -static inline void format_field_path(char **buf_ch, bool extended, - const char *prefix, const struct bt_field_path *field_path) -{ - uint64_t i; - - if (field_path->items) { - BT_ASSERT(field_path->items); - BUF_APPEND(", %sitem-count=%u", - PRFIELD(field_path->items->len)); - } - - if (!extended || !field_path->items) { - return; - } - - BUF_APPEND(", %spath=[%s", - PRFIELD(bt_common_scope_string(field_path->root))); - - for (i = 0; i < bt_field_path_get_item_count(field_path); i++) { - const struct bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_const(field_path, i); - - switch (bt_field_path_item_get_type(fp_item)) { - case BT_FIELD_PATH_ITEM_TYPE_INDEX: - BUF_APPEND(", %" PRIu64, - bt_field_path_item_index_get_index(fp_item)); - break; - case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: - BUF_APPEND("%s", ", "); - break; - default: - abort(); - } - } - - BUF_APPEND("%s", "]"); -} - -static inline void format_trace_class(char **buf_ch, bool extended, - const char *prefix, const struct bt_trace_class *trace_class) -{ - if (trace_class->name.value) { - BUF_APPEND(", %sname=\"%s\"", - PRFIELD(trace_class->name.value)); - } - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d", PRFIELD(trace_class->frozen)); - - if (trace_class->uuid.value) { - BUF_APPEND_UUID(trace_class->uuid.value); - } - - if (trace_class->stream_classes) { - BUF_APPEND(", %sstream-class-count=%u", - PRFIELD(trace_class->stream_classes->len)); - } - - BUF_APPEND(", %sassigns-auto-sc-id=%d", - PRFIELD(trace_class->assigns_automatic_stream_class_id)); -} - -static inline void format_trace(char **buf_ch, bool extended, - const char *prefix, const struct bt_trace *trace) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - if (trace->name.value) { - BUF_APPEND(", %sname=\"%s\"", PRFIELD(trace->name.value)); - } - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d", PRFIELD(trace->frozen)); - - if (trace->streams) { - BUF_APPEND(", %sstream-count=%u", - PRFIELD(trace->streams->len)); - } - - if (!trace->class) { - return; - } - - BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace->class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, trace->class); -} - -static inline void format_stream_class(char **buf_ch, bool extended, - const char *prefix, - const struct bt_stream_class *stream_class) -{ - const struct bt_trace_class *trace_class; - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %sid=%" PRIu64, PRFIELD(stream_class->id)); - - if (stream_class->name.value) { - BUF_APPEND(", %sname=\"%s\"", - PRFIELD(stream_class->name.value)); - } - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d", PRFIELD(stream_class->frozen)); - - if (stream_class->event_classes) { - BUF_APPEND(", %sevent-class-count=%u", - PRFIELD(stream_class->event_classes->len)); - } - - BUF_APPEND(", %spacket-context-fc-addr=%p, " - "%sevent-common-context-fc-addr=%p", - PRFIELD(stream_class->packet_context_fc), - PRFIELD(stream_class->event_common_context_fc)); - trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); - if (!trace_class) { - return; - } - - BUF_APPEND(", %sassigns-auto-ec-id=%d, %sassigns-auto-stream-id=%d, " - "%spackets-have-default-beginning-cs=%d, " - "%spackets-have-default-end-cs=%d, " - "%ssupports-discarded-events=%d, " - "%sdiscarded-events-have-default-cs=%d, " - "%ssupports-discarded-packets=%d, " - "%sdiscarded-packets-have-default-cs=%d", - PRFIELD(stream_class->assigns_automatic_event_class_id), - PRFIELD(stream_class->assigns_automatic_stream_id), - PRFIELD(stream_class->packets_have_beginning_default_clock_snapshot), - PRFIELD(stream_class->packets_have_end_default_clock_snapshot), - PRFIELD(stream_class->supports_discarded_events), - PRFIELD(stream_class->discarded_events_have_default_clock_snapshots), - PRFIELD(stream_class->supports_discarded_packets), - PRFIELD(stream_class->discarded_packets_have_default_clock_snapshots)); - BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, trace_class); - SET_TMP_PREFIX("pcf-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &stream_class->packet_context_field_pool); -} - -static inline void format_event_class(char **buf_ch, bool extended, - const char *prefix, const struct bt_event_class *event_class) -{ - const struct bt_stream_class *stream_class; - const struct bt_trace_class *trace_class; - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %sid=%" PRIu64, PRFIELD(event_class->id)); - - if (event_class->name.value) { - BUF_APPEND(", %sname=\"%s\"", - PRFIELD(event_class->name.value)); - } - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d", PRFIELD(event_class->frozen)); - - if (event_class->log_level.base.avail) { - BUF_APPEND(", %slog-level=%s", - PRFIELD(bt_common_event_class_log_level_string( - (int) event_class->log_level.value))); - } - - if (event_class->emf_uri.value) { - BUF_APPEND(", %semf-uri=\"%s\"", - PRFIELD(event_class->emf_uri.value)); - } - - BUF_APPEND(", %sspecific-context-fc-addr=%p, %spayload-fc-addr=%p", - PRFIELD(event_class->specific_context_fc), - PRFIELD(event_class->payload_fc)); - - stream_class = bt_event_class_borrow_stream_class_const(event_class); - if (!stream_class) { - return; - } - - BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); - SET_TMP_PREFIX("stream-class-"); - format_stream_class(buf_ch, false, tmp_prefix, stream_class); - trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); - if (!trace_class) { - return; - } - - BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, trace_class); - SET_TMP_PREFIX("event-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &event_class->event_pool); -} - -static inline void format_stream(char **buf_ch, bool extended, - const char *prefix, const struct bt_stream *stream) -{ - const struct bt_stream_class *stream_class; - const struct bt_trace_class *trace_class = NULL; - const struct bt_trace *trace = NULL; - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %sid=%" PRIu64, PRFIELD(stream->id)); - - if (stream->name.value) { - BUF_APPEND(", %sname=\"%s\"", PRFIELD(stream->name.value)); - } - - if (!extended) { - return; - } - - stream_class = bt_stream_borrow_class_const(stream); - if (stream_class) { - BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); - SET_TMP_PREFIX("stream-class-"); - format_stream_class(buf_ch, false, tmp_prefix, stream_class); - trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); - } - - if (trace_class) { - BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, trace_class); - } - - trace = bt_stream_borrow_trace_inline(stream); - if (trace) { - BUF_APPEND(", %strace-addr=%p", PRFIELD(trace)); - SET_TMP_PREFIX("trace-"); - format_trace(buf_ch, false, tmp_prefix, trace); - } - - SET_TMP_PREFIX("packet-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, &stream->packet_pool); -} - -static inline void format_packet(char **buf_ch, bool extended, - const char *prefix, const struct bt_packet *packet) -{ - const struct bt_stream *stream; - const struct bt_trace_class *trace_class; - char tmp_prefix[TMP_PREFIX_LEN]; - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d, %scontext-field-addr=%p", - PRFIELD(packet->frozen), - PRFIELD(packet->context_field ? packet->context_field->field : NULL)); - stream = bt_packet_borrow_stream_const(packet); - if (!stream) { - return; - } - - BUF_APPEND(", %sstream-addr=%p", PRFIELD(stream)); - SET_TMP_PREFIX("stream-"); - format_stream(buf_ch, false, tmp_prefix, stream); - trace_class = (const struct bt_trace_class *) bt_object_borrow_parent(&stream->base); - if (!trace_class) { - return; - } - - BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, trace_class); -} - -static inline void format_event(char **buf_ch, bool extended, - const char *prefix, const struct bt_event *event) -{ - const struct bt_packet *packet; - const struct bt_stream *stream; - const struct bt_trace_class *trace_class; - const struct bt_stream_class *stream_class; - char tmp_prefix[TMP_PREFIX_LEN]; - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d, " - "%scommon-context-field-addr=%p, " - "%sspecific-context-field-addr=%p, " - "%spayload-field-addr=%p, ", - PRFIELD(event->frozen), - PRFIELD(event->common_context_field), - PRFIELD(event->specific_context_field), - PRFIELD(event->payload_field)); - BUF_APPEND(", %sevent-class-addr=%p", PRFIELD(event->class)); - - if (!event->class) { - return; - } - - SET_TMP_PREFIX("event-class-"); - format_event_class(buf_ch, false, tmp_prefix, event->class); - stream_class = bt_event_class_borrow_stream_class(event->class); - if (stream_class) { - BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); - SET_TMP_PREFIX("stream-class-"); - format_stream_class(buf_ch, false, tmp_prefix, - stream_class); - - trace_class = bt_stream_class_borrow_trace_class_inline( - stream_class); - if (trace_class) { - BUF_APPEND(", %strace-class-addr=%p", - PRFIELD(trace_class)); - SET_TMP_PREFIX("trace-class-"); - format_trace_class(buf_ch, false, tmp_prefix, - trace_class); - } - } - - packet = bt_event_borrow_packet_const(event); - if (!packet) { - return; - } - - BUF_APPEND(", %spacket-addr=%p", PRFIELD(packet)); - SET_TMP_PREFIX("packet-"); - format_packet(buf_ch, false, tmp_prefix, packet); - stream = bt_packet_borrow_stream_const(packet); - if (!stream) { - return; - } - - BUF_APPEND(", %sstream-addr=%p", PRFIELD(stream)); - SET_TMP_PREFIX("stream-"); - format_stream(buf_ch, false, tmp_prefix, stream); -} - -static inline void format_clock_class(char **buf_ch, bool extended, - const char *prefix, const struct bt_clock_class *clock_class) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - if (clock_class->name.value) { - BUF_APPEND(", %sname=\"%s\"", PRFIELD(clock_class->name.value)); - } - - BUF_APPEND(", %sfreq=%" PRIu64, PRFIELD(clock_class->frequency)); - - if (!extended) { - return; - } - - if (clock_class->description.value) { - BUF_APPEND(", %spartial-descr=\"%.32s\"", - PRFIELD(clock_class->description.value)); - } - - if (clock_class->uuid.value) { - BUF_APPEND_UUID(clock_class->uuid.value); - } - - BUF_APPEND(", %sis-frozen=%d, %sprecision=%" PRIu64 ", " - "%soffset-s=%" PRId64 ", " - "%soffset-cycles=%" PRIu64 ", %sorigin-is-unix-epoch=%d, " - "%sbase-offset-ns=%" PRId64, - PRFIELD(clock_class->frozen), PRFIELD(clock_class->precision), - PRFIELD(clock_class->offset_seconds), - PRFIELD(clock_class->offset_cycles), - PRFIELD(clock_class->origin_is_unix_epoch), - PRFIELD(clock_class->base_offset.value_ns)); - - SET_TMP_PREFIX("cs-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &clock_class->cs_pool); -} - -static inline void format_clock_snapshot(char **buf_ch, bool extended, - const char *prefix, const struct bt_clock_snapshot *clock_snapshot) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - BUF_APPEND(", %svalue=%" PRIu64 ", %sns-from-origin=%" PRId64, - PRFIELD(clock_snapshot->value_cycles), - PRFIELD(clock_snapshot->ns_from_origin)); - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-set=%d", PRFIELD(clock_snapshot->is_set)); - - if (clock_snapshot->clock_class) { - BUF_APPEND(", %sclock-class-addr=%p", - PRFIELD(clock_snapshot->clock_class)); - SET_TMP_PREFIX("clock-class-"); - format_clock_class(buf_ch, false, tmp_prefix, - clock_snapshot->clock_class); - } -} - -static inline void format_value(char **buf_ch, bool extended, - const char *prefix, const struct bt_value *value) -{ - BUF_APPEND(", %stype=%s", - PRFIELD(bt_common_value_type_string(bt_value_get_type(value)))); - - if (!extended) { - return; - } - - switch (bt_value_get_type(value)) { - case BT_VALUE_TYPE_BOOL: - { - bt_bool val = bt_value_bool_get(value); - - BUF_APPEND(", %svalue=%d", PRFIELD(val)); - break; - } - case BT_VALUE_TYPE_UNSIGNED_INTEGER: - { - BUF_APPEND(", %svalue=%" PRIu64, - PRFIELD(bt_value_unsigned_integer_get(value))); - break; - } - case BT_VALUE_TYPE_SIGNED_INTEGER: - { - BUF_APPEND(", %svalue=%" PRId64, - PRFIELD(bt_value_signed_integer_get(value))); - break; - } - case BT_VALUE_TYPE_REAL: - { - double val = bt_value_real_get(value); - - BUF_APPEND(", %svalue=%f", PRFIELD(val)); - break; - } - case BT_VALUE_TYPE_STRING: - { - const char *val = bt_value_string_get(value); - - BUF_APPEND(", %spartial-value=\"%.32s\"", PRFIELD(val)); - break; - } - case BT_VALUE_TYPE_ARRAY: - { - int64_t count = bt_value_array_get_size(value); - - BT_ASSERT(count >= 0); - BUF_APPEND(", %selement-count=%" PRId64, PRFIELD(count)); - break; - } - case BT_VALUE_TYPE_MAP: - { - int64_t count = bt_value_map_get_size(value); - - BT_ASSERT(count >= 0); - BUF_APPEND(", %selement-count=%" PRId64, PRFIELD(count)); - break; - } - default: - break; - } -} - -static inline void format_message(char **buf_ch, bool extended, - const char *prefix, const struct bt_message *msg) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %stype=%s", - PRFIELD(bt_message_type_string(msg->type))); - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d, %sgraph-addr=%p", - PRFIELD(msg->frozen), PRFIELD(msg->graph)); - - switch (msg->type) { - case BT_MESSAGE_TYPE_EVENT: - { - const struct bt_message_event *msg_event = - (const void *) msg; - - if (msg_event->event) { - SET_TMP_PREFIX("event-"); - format_event(buf_ch, true, tmp_prefix, - msg_event->event); - } - - if (msg_event->default_cs) { - SET_TMP_PREFIX("default-cs-"); - format_clock_snapshot(buf_ch, true, tmp_prefix, - msg_event->default_cs); - } - - break; - } - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - case BT_MESSAGE_TYPE_STREAM_END: - { - const struct bt_message_stream *msg_stream = (const void *) msg; - - if (msg_stream->stream) { - SET_TMP_PREFIX("stream-"); - format_stream(buf_ch, true, tmp_prefix, - msg_stream->stream); - } - - break; - } - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - { - const struct bt_message_stream_activity *msg_stream_activity = - (const void *) msg; - - if (msg_stream_activity->stream) { - SET_TMP_PREFIX("stream-"); - format_stream(buf_ch, true, tmp_prefix, - msg_stream_activity->stream); - } - - BUF_APPEND(", %sdefault-cs-state=%s", - PRFIELD(bt_message_stream_activity_clock_snapshot_state_string( - msg_stream_activity->default_cs_state))); - - if (msg_stream_activity->default_cs) { - SET_TMP_PREFIX("default-cs-"); - format_clock_snapshot(buf_ch, true, tmp_prefix, - msg_stream_activity->default_cs); - } - - break; - } - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - case BT_MESSAGE_TYPE_PACKET_END: - { - const struct bt_message_packet *msg_packet = (const void *) msg; - - if (msg_packet->packet) { - SET_TMP_PREFIX("packet-"); - format_packet(buf_ch, true, tmp_prefix, - msg_packet->packet); - } - - if (msg_packet->default_cs) { - SET_TMP_PREFIX("default-cs-"); - format_clock_snapshot(buf_ch, true, tmp_prefix, - msg_packet->default_cs); - } - - break; - } - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - { - const struct bt_message_discarded_items *msg_disc_items = - (const void *) msg; - - if (msg_disc_items->stream) { - SET_TMP_PREFIX("stream-"); - format_stream(buf_ch, true, tmp_prefix, - msg_disc_items->stream); - } - - if (msg_disc_items->default_begin_cs) { - SET_TMP_PREFIX("default-begin-cs-"); - format_clock_snapshot(buf_ch, true, tmp_prefix, - msg_disc_items->default_begin_cs); - } - - if (msg_disc_items->default_end_cs) { - SET_TMP_PREFIX("default-end-cs-"); - format_clock_snapshot(buf_ch, true, tmp_prefix, - msg_disc_items->default_end_cs); - } - - if (msg_disc_items->count.base.avail) { - BUF_APPEND(", %scount=%" PRIu64, - PRFIELD(msg_disc_items->count.value)); - } - - break; - } - default: - break; - } -} - -static inline void format_plugin_so_shared_lib_handle(char **buf_ch, - const char *prefix, - const struct bt_plugin_so_shared_lib_handle *handle) -{ - BUF_APPEND(", %saddr=%p", PRFIELD(handle)); - - if (handle->path) { - BUF_APPEND(", %spath=\"%s\"", PRFIELD_GSTRING(handle->path)); - } -} - -static inline void format_component_class(char **buf_ch, bool extended, - const char *prefix, - const struct bt_component_class *comp_class) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %stype=%s, %sname=\"%s\"", - PRFIELD(bt_component_class_type_string(comp_class->type)), - PRFIELD_GSTRING(comp_class->name)); - - if (comp_class->description) { - BUF_APPEND(", %spartial-descr=\"%.32s\"", - PRFIELD_GSTRING(comp_class->description)); - } - - if (!extended) { - return; - } - - BUF_APPEND(", %sis-frozen=%d", PRFIELD(comp_class->frozen)); - - if (comp_class->so_handle) { - SET_TMP_PREFIX("so-handle-"); - format_plugin_so_shared_lib_handle(buf_ch, tmp_prefix, - comp_class->so_handle); - } -} - -static inline void format_component(char **buf_ch, bool extended, - const char *prefix, const struct bt_component *component) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %sname=\"%s\"", - PRFIELD_GSTRING(component->name)); - - if (component->class) { - SET_TMP_PREFIX("class-"); - format_component_class(buf_ch, extended, tmp_prefix, - component->class); - } - - if (!extended) { - return; - } - - if (component->input_ports) { - BUF_APPEND(", %sinput-port-count=%u", - PRFIELD(component->input_ports->len)); - } - - if (component->output_ports) { - BUF_APPEND(", %soutput-port-count=%u", - PRFIELD(component->output_ports->len)); - } -} - -static inline void format_port(char **buf_ch, bool extended, - const char *prefix, const struct bt_port *port) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %stype=%s, %sname=\"%s\"", - PRFIELD(bt_port_type_string(port->type)), - PRFIELD_GSTRING(port->name)); - - if (!extended) { - return; - } - - if (port->connection) { - SET_TMP_PREFIX("conn-"); - format_connection(buf_ch, false, tmp_prefix, port->connection); - } -} - -static inline void format_connection(char **buf_ch, bool extended, - const char *prefix, const struct bt_connection *connection) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - if (!extended) { - return; - } - - if (connection->upstream_port) { - SET_TMP_PREFIX("upstream-port-"); - format_port(buf_ch, false, tmp_prefix, - connection->upstream_port); - } - - if (connection->downstream_port) { - SET_TMP_PREFIX("downstream-port-"); - format_port(buf_ch, false, tmp_prefix, - connection->downstream_port); - } -} - -static inline void format_graph(char **buf_ch, bool extended, - const char *prefix, const struct bt_graph *graph) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %sis-canceled=%d, %scan-consume=%d, " - "%sconfig-state=%s", - PRFIELD(graph->canceled), - PRFIELD(graph->can_consume), - PRFIELD(bt_graph_configuration_state_string(graph->config_state))); - - if (!extended) { - return; - } - - if (graph->components) { - BUF_APPEND(", %scomp-count=%u", - PRFIELD(graph->components->len)); - } - - if (graph->connections) { - BUF_APPEND(", %sconn-count=%u", - PRFIELD(graph->connections->len)); - } - - SET_TMP_PREFIX("en-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &graph->event_msg_pool); - SET_TMP_PREFIX("pbn-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &graph->packet_begin_msg_pool); - SET_TMP_PREFIX("pen-pool-"); - format_object_pool(buf_ch, extended, tmp_prefix, - &graph->packet_end_msg_pool); -} - -static inline void format_message_iterator(char **buf_ch, - bool extended, const char *prefix, - const struct bt_message_iterator *iterator) -{ - const char *type; - char tmp_prefix[TMP_PREFIX_LEN]; - - if (iterator->type == BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT) { - type = "BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT"; - } else if (iterator->type == BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT) { - type = "BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT"; - } else { - type = "(unknown)"; - } - - BUF_APPEND(", %stype=%s", PRFIELD(type)); - - switch (iterator->type) { - case BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT: - { - const struct bt_self_component_port_input_message_iterator * - port_in_iter = (const void *) iterator; - - if (port_in_iter->upstream_component) { - SET_TMP_PREFIX("upstream-comp-"); - format_component(buf_ch, false, tmp_prefix, - port_in_iter->upstream_component); - } - - if (port_in_iter->upstream_port) { - SET_TMP_PREFIX("upstream-port-"); - format_port(buf_ch, false, tmp_prefix, - port_in_iter->upstream_port); - } - - if (port_in_iter->connection) { - SET_TMP_PREFIX("upstream-conn-"); - format_connection(buf_ch, false, tmp_prefix, - port_in_iter->connection); - } - break; - } - case BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT: - { - const struct bt_port_output_message_iterator *port_out_iter = - (const void *) iterator; - - if (port_out_iter->graph) { - SET_TMP_PREFIX("graph-"); - format_graph(buf_ch, false, tmp_prefix, - port_out_iter->graph); - } - - if (port_out_iter->colander) { - SET_TMP_PREFIX("colander-comp-"); - format_component(buf_ch, false, tmp_prefix, - (void *) port_out_iter->colander); - } - - break; - } - default: - break; - } -} - -static inline void format_plugin(char **buf_ch, bool extended, - const char *prefix, const struct bt_plugin *plugin) -{ - char tmp_prefix[TMP_PREFIX_LEN]; - - BUF_APPEND(", %stype=%s", PRFIELD(bt_plugin_type_string(plugin->type))); - - if (plugin->info.path_set) { - BUF_APPEND(", %spath=\"%s\"", - PRFIELD_GSTRING(plugin->info.path)); - } - - if (plugin->info.name_set) { - BUF_APPEND(", %sname=\"%s\"", - PRFIELD_GSTRING(plugin->info.name)); - } - - if (!extended) { - return; - } - - if (plugin->info.author_set) { - BUF_APPEND(", %sauthor=\"%s\"", - PRFIELD_GSTRING(plugin->info.author)); - } - - if (plugin->info.license_set) { - BUF_APPEND(", %slicense=\"%s\"", - PRFIELD_GSTRING(plugin->info.license)); - } - - if (plugin->info.version_set) { - BUF_APPEND(", %sversion=%u.%u.%u%s", - PRFIELD(plugin->info.version.major), - plugin->info.version.minor, - plugin->info.version.patch, - plugin->info.version.extra ? - plugin->info.version.extra->str : ""); - } - - BUF_APPEND(", %ssrc-comp-class-count=%u, %sflt-comp-class-count=%u, " - "%ssink-comp-class-count=%u", - PRFIELD(plugin->src_comp_classes->len), - PRFIELD(plugin->flt_comp_classes->len), - PRFIELD(plugin->sink_comp_classes->len)); - - if (plugin->spec_data) { - const struct bt_plugin_so_spec_data *spec_data = - (const void *) plugin->spec_data; - - if (spec_data->shared_lib_handle) { - SET_TMP_PREFIX("so-handle-"); - format_plugin_so_shared_lib_handle(buf_ch, tmp_prefix, - spec_data->shared_lib_handle); - } - } -} - -static inline void handle_conversion_specifier_bt(void *priv_data, - char **buf_ch, size_t avail_size, - const char **out_fmt_ch, va_list *args) -{ - const char *fmt_ch = *out_fmt_ch; - bool extended = false; - char prefix[64]; - char *prefix_ch = prefix; - const void *obj; - - /* skip "%!" */ - fmt_ch += 2; - - if (*fmt_ch == 'u') { - /* UUID */ - obj = va_arg(*args, void *); - format_uuid(buf_ch, obj); - goto update_fmt; - } - - if (*fmt_ch == '[') { - /* local prefix */ - fmt_ch++; - - while (true) { - if (*fmt_ch == ']') { - *prefix_ch = '\0'; - fmt_ch++; - break; - } - - *prefix_ch = *fmt_ch; - prefix_ch++; - fmt_ch++; - } - } - - *prefix_ch = '\0'; - - if (*fmt_ch == '+') { - extended = true; - fmt_ch++; - } - - obj = va_arg(*args, void *); - BUF_APPEND("%saddr=%p", prefix, obj); - - if (!obj) { - goto update_fmt; - } - - switch (*fmt_ch) { - case 'F': - format_field_class(buf_ch, extended, prefix, obj); - break; - case 'f': - format_field(buf_ch, extended, prefix, obj); - break; - case 'P': - format_field_path(buf_ch, extended, prefix, obj); - break; - case 'E': - format_event_class(buf_ch, extended, prefix, obj); - break; - case 'e': - format_event(buf_ch, extended, prefix, obj); - break; - case 'S': - format_stream_class(buf_ch, extended, prefix, obj); - break; - case 's': - format_stream(buf_ch, extended, prefix, obj); - break; - case 'a': - format_packet(buf_ch, extended, prefix, obj); - break; - case 't': - format_trace(buf_ch, extended, prefix, obj); - break; - case 'T': - format_trace_class(buf_ch, extended, prefix, obj); - break; - case 'K': - format_clock_class(buf_ch, extended, prefix, obj); - break; - case 'k': - format_clock_snapshot(buf_ch, extended, prefix, obj); - break; - case 'v': - format_value(buf_ch, extended, prefix, obj); - break; - case 'n': - format_message(buf_ch, extended, prefix, obj); - break; - case 'i': - format_message_iterator(buf_ch, extended, prefix, obj); - break; - case 'C': - format_component_class(buf_ch, extended, prefix, obj); - break; - case 'c': - format_component(buf_ch, extended, prefix, obj); - break; - case 'p': - format_port(buf_ch, extended, prefix, obj); - break; - case 'x': - format_connection(buf_ch, extended, prefix, obj); - break; - case 'l': - format_plugin(buf_ch, extended, prefix, obj); - break; - case 'g': - format_graph(buf_ch, extended, prefix, obj); - break; - case 'o': - format_object_pool(buf_ch, extended, prefix, obj); - break; - case 'O': - format_object(buf_ch, extended, prefix, obj); - break; - default: - abort(); - } - -update_fmt: - fmt_ch++; - *out_fmt_ch = fmt_ch; -} - -void bt_lib_log(const char *func, const char *file, unsigned line, - int lvl, const char *tag, const char *fmt, ...) -{ - va_list args; - - BT_ASSERT(fmt); - va_start(args, fmt); - bt_common_custom_vsnprintf(lib_logging_buf, LIB_LOGGING_BUF_SIZE, '!', - handle_conversion_specifier_bt, NULL, fmt, &args); - va_end(args); - _bt_log_write_d(func, file, line, lvl, tag, "%s", lib_logging_buf); -} diff --git a/lib/logging.c b/lib/logging.c deleted file mode 100644 index 7ebb52c0..00000000 --- a/lib/logging.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#define BT_LOG_TAG "LIB" -#include - -#ifdef BT_DEV_MODE -/* - * Default log level is FATAL in developer mode because fatal logging is - * our way to communicate an unsatisfied precondition and the details. - */ -# define DEFAULT_LOG_LEVEL BT_LOG_FATAL -#else -/* - * In non-developer mode, use NONE by default: we don't to print logging - * statements for any executable which links with the library. The - * executable should call bt_logging_set_global_level() or the - * executable's user should set the BABELTRACE_LOGGING_GLOBAL_LEVEL - * environment variable. - */ -# define DEFAULT_LOG_LEVEL BT_LOG_NONE -#endif /* BT_DEV_MODE */ - -int bt_lib_log_level = DEFAULT_LOG_LEVEL; - -enum bt_logging_level bt_logging_get_minimal_level(void) -{ - return BT_LOG_LEVEL; -} - -enum bt_logging_level bt_logging_get_global_level(void) -{ - return bt_lib_log_level; -} - -void bt_logging_set_global_level(enum bt_logging_level log_level) -{ -#ifdef BT_DEV_MODE - /* - * Do not allow the library's log level to fall to NONE when in - * developer mode because fatal logging is our way to - * communicate an unsatisfied precondition and the details. - */ - if (log_level == BT_LOG_NONE) { - log_level = BT_LOG_FATAL; - } -#endif - - bt_lib_log_level = log_level; -} - -static -void __attribute__((constructor)) bt_logging_ctor(void) -{ - const char *v_extra = bt_version_get_extra() ? bt_version_get_extra() : - ""; - - bt_logging_set_global_level( - bt_log_get_level_from_env("BABELTRACE_LOGGING_GLOBAL_LEVEL")); - BT_LOGI("Babeltrace %d.%d.%d%s library loaded: " - "major=%d, minor=%d, patch=%d, extra=\"%s\"", - bt_version_get_major(), bt_version_get_minor(), - bt_version_get_patch(), v_extra, - bt_version_get_major(), bt_version_get_minor(), - bt_version_get_patch(), v_extra); -} diff --git a/lib/object-pool.c b/lib/object-pool.c deleted file mode 100644 index 8cdf98c7..00000000 --- a/lib/object-pool.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "OBJECT-POOL" -#include - -#include -#include -#include -#include - -int bt_object_pool_initialize(struct bt_object_pool *pool, - bt_object_pool_new_object_func new_object_func, - bt_object_pool_destroy_object_func destroy_object_func, - void *data) -{ - int ret = 0; - - BT_ASSERT(new_object_func); - BT_ASSERT(destroy_object_func); - BT_LOGD("Initializing object pool: addr=%p, data-addr=%p", - pool, data); - pool->objects = g_ptr_array_new(); - if (!pool->objects) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - pool->funcs.new_object = new_object_func; - pool->funcs.destroy_object = destroy_object_func; - pool->data = data; - pool->size = 0; - BT_LIB_LOGD("Initialized object pool: %!+o", pool); - goto end; - -error: - if (pool) { - bt_object_pool_finalize(pool); - } - - ret = -1; - -end: - return ret; -} - -void bt_object_pool_finalize(struct bt_object_pool *pool) -{ - uint64_t i; - - BT_ASSERT(pool); - BT_LIB_LOGD("Finalizing object pool: %!+o", pool); - - if (pool->objects) { - for (i = 0; i < pool->size; i++) { - void *obj = pool->objects->pdata[i]; - - if (obj) { - pool->funcs.destroy_object(obj, pool->data); - } - } - - g_ptr_array_free(pool->objects, TRUE); - pool->objects = NULL; - } -} diff --git a/lib/plugin/Makefile.am b/lib/plugin/Makefile.am deleted file mode 100644 index bf8afa21..00000000 --- a/lib/plugin/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LTLIBRARIES = libplugin.la - -# Plug-in system library -libplugin_la_SOURCES = \ - plugin.c \ - plugin-so.c diff --git a/lib/plugin/plugin-so.c b/lib/plugin/plugin-so.c deleted file mode 100644 index ec69646f..00000000 --- a/lib/plugin/plugin-so.c +++ /dev/null @@ -1,1620 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-SO" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NATIVE_PLUGIN_SUFFIX "." G_MODULE_SUFFIX -#define NATIVE_PLUGIN_SUFFIX_LEN sizeof(NATIVE_PLUGIN_SUFFIX) -#define LIBTOOL_PLUGIN_SUFFIX ".la" -#define LIBTOOL_PLUGIN_SUFFIX_LEN sizeof(LIBTOOL_PLUGIN_SUFFIX) - -#define PLUGIN_SUFFIX_LEN max_t(size_t, sizeof(NATIVE_PLUGIN_SUFFIX), \ - sizeof(LIBTOOL_PLUGIN_SUFFIX)) - -BT_PLUGIN_MODULE(); - -/* - * This list, global to the library, keeps all component classes that - * have a reference to their shared library handles. It allows iteration - * on all component classes still present when the destructor executes - * to release the shared library handle references they might still have. - * - * The list items are the component classes created with - * bt_plugin_add_component_class(). They keep the shared library handle - * object created by their plugin alive so that the plugin's code is - * not discarded when it could still be in use by living components - * created from those component classes: - * - * [component] --ref-> [component class]-> [shlib handle] - * - * It allows this use-case: - * - * my_plugins = bt_plugin_find_all_from_file("/path/to/my-plugin.so"); - * // instantiate components from a plugin's component classes - * // put plugins and free my_plugins here - * // user code of instantiated components still exists - * - * An entry is removed from this list when a component class is - * destroyed thanks to a custom destroy listener. When the entry is - * removed, the entry is removed from the list, and we release the - * reference on the shlib handle. Assuming the original plugin object - * which contained some component classes is put first, when the last - * component class is removed from this list, the shared library handle - * object's reference count falls to zero and the shared library is - * finally closed. - */ - -static -BT_LIST_HEAD(component_class_list); - -__attribute__((destructor)) static -void fini_comp_class_list(void) -{ - struct bt_component_class *comp_class, *tmp; - - bt_list_for_each_entry_safe(comp_class, tmp, &component_class_list, node) { - bt_list_del(&comp_class->node); - BT_OBJECT_PUT_REF_AND_RESET(comp_class->so_handle); - } - - BT_LOGD_STR("Released references from all component classes to shared library handles."); -} - -static inline -const char *bt_self_plugin_status_string(enum bt_self_plugin_status status) -{ - switch (status) { - case BT_SELF_PLUGIN_STATUS_OK: - return "BT_SELF_PLUGIN_STATUS_OK"; - case BT_SELF_PLUGIN_STATUS_ERROR: - return "BT_SELF_PLUGIN_STATUS_ERROR"; - case BT_SELF_PLUGIN_STATUS_NOMEM: - return "BT_SELF_PLUGIN_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -static -void bt_plugin_so_shared_lib_handle_destroy(struct bt_object *obj) -{ - struct bt_plugin_so_shared_lib_handle *shared_lib_handle; - - BT_ASSERT(obj); - shared_lib_handle = container_of(obj, - struct bt_plugin_so_shared_lib_handle, base); - const char *path = shared_lib_handle->path ? - shared_lib_handle->path->str : NULL; - - BT_LOGD("Destroying shared library handle: addr=%p, path=\"%s\"", - shared_lib_handle, path); - - if (shared_lib_handle->init_called && shared_lib_handle->exit) { - BT_LOGD_STR("Calling user's plugin exit function."); - shared_lib_handle->exit(); - BT_LOGD_STR("User function returned."); - } - - if (shared_lib_handle->module) { -#ifndef NDEBUG - /* - * Valgrind shows incomplete stack traces when - * dynamically loaded libraries are closed before it - * finishes. Use the BABELTRACE_NO_DLCLOSE in a debug - * build to avoid this. - */ - const char *var = getenv("BABELTRACE_NO_DLCLOSE"); - - if (!var || strcmp(var, "1") != 0) { -#endif - BT_LOGD("Closing GModule: path=\"%s\"", path); - - if (!g_module_close(shared_lib_handle->module)) { - BT_LOGE("Cannot close GModule: %s: path=\"%s\"", - g_module_error(), path); - } - - shared_lib_handle->module = NULL; -#ifndef NDEBUG - } else { - BT_LOGD("Not closing GModule because `BABELTRACE_NO_DLCLOSE=1`: " - "path=\"%s\"", path); - } -#endif - } - - if (shared_lib_handle->path) { - g_string_free(shared_lib_handle->path, TRUE); - shared_lib_handle->path = NULL; - } - - g_free(shared_lib_handle); -} - -static -struct bt_plugin_so_shared_lib_handle *bt_plugin_so_shared_lib_handle_create( - const char *path) -{ - struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL; - - BT_LOGD("Creating shared library handle: path=\"%s\"", path); - shared_lib_handle = g_new0(struct bt_plugin_so_shared_lib_handle, 1); - if (!shared_lib_handle) { - BT_LOGE_STR("Failed to allocate one shared library handle."); - goto error; - } - - bt_object_init_shared(&shared_lib_handle->base, - bt_plugin_so_shared_lib_handle_destroy); - - if (!path) { - goto end; - } - - shared_lib_handle->path = g_string_new(path); - if (!shared_lib_handle->path) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - shared_lib_handle->module = g_module_open(path, G_MODULE_BIND_LOCAL); - if (!shared_lib_handle->module) { - /* - * DEBUG-level logging because we're only _trying_ to - * open this file as a Babeltrace plugin: if it's not, - * it's not an error. And because this can be tried - * during bt_plugin_find_all_from_dir(), it's not even - * a warning. - */ - BT_LOGD("Cannot open GModule: %s: path=\"%s\"", - g_module_error(), path); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); - -end: - if (shared_lib_handle) { - BT_LOGD("Created shared library handle: path=\"%s\", addr=%p", - path, shared_lib_handle); - } - - return shared_lib_handle; -} - -static -void bt_plugin_so_destroy_spec_data(struct bt_plugin *plugin) -{ - struct bt_plugin_so_spec_data *spec = plugin->spec_data; - - if (!plugin->spec_data) { - return; - } - - BT_ASSERT(plugin->type == BT_PLUGIN_TYPE_SO); - BT_ASSERT(spec); - BT_OBJECT_PUT_REF_AND_RESET(spec->shared_lib_handle); - g_free(plugin->spec_data); - plugin->spec_data = NULL; -} - -/* - * This function does the following: - * - * 1. Iterate on the plugin descriptor attributes section and set the - * plugin's attributes depending on the attribute types. This - * includes the name of the plugin, its description, and its - * initialization function, for example. - * - * 2. Iterate on the component class descriptors section and create one - * "full descriptor" (temporary structure) for each one that is found - * and attached to our plugin descriptor. - * - * 3. Iterate on the component class descriptor attributes section and - * set the corresponding full descriptor's attributes depending on - * the attribute types. This includes the description of the - * component class, as well as its initialization and destroy - * methods. - * - * 4. Call the user's plugin initialization function, if any is - * defined. - * - * 5. For each full component class descriptor, create a component class - * object, set its optional attributes, and add it to the plugin - * object. - * - * 6. Freeze the plugin object. - */ -static -enum bt_plugin_status bt_plugin_so_init( - struct bt_plugin *plugin, - const struct __bt_plugin_descriptor *descriptor, - struct __bt_plugin_descriptor_attribute const * const *attrs_begin, - struct __bt_plugin_descriptor_attribute const * const *attrs_end, - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin, - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end, - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin, - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end) -{ - /* - * This structure's members point to the plugin's memory - * (do NOT free). - */ - struct comp_class_full_descriptor { - const struct __bt_plugin_component_class_descriptor *descriptor; - const char *description; - const char *help; - - union { - struct { - bt_component_class_source_init_method init; - bt_component_class_source_finalize_method finalize; - bt_component_class_source_query_method query; - bt_component_class_source_accept_output_port_connection_method accept_output_port_connection; - bt_component_class_source_output_port_connected_method output_port_connected; - bt_component_class_source_message_iterator_init_method msg_iter_init; - bt_component_class_source_message_iterator_finalize_method msg_iter_finalize; - bt_component_class_source_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; - bt_component_class_source_message_iterator_seek_beginning_method msg_iter_seek_beginning; - bt_component_class_source_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; - bt_component_class_source_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; - } source; - - struct { - bt_component_class_filter_init_method init; - bt_component_class_filter_finalize_method finalize; - bt_component_class_filter_query_method query; - bt_component_class_filter_accept_input_port_connection_method accept_input_port_connection; - bt_component_class_filter_accept_output_port_connection_method accept_output_port_connection; - bt_component_class_filter_input_port_connected_method input_port_connected; - bt_component_class_filter_output_port_connected_method output_port_connected; - bt_component_class_filter_message_iterator_init_method msg_iter_init; - bt_component_class_filter_message_iterator_finalize_method msg_iter_finalize; - bt_component_class_filter_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; - bt_component_class_filter_message_iterator_seek_beginning_method msg_iter_seek_beginning; - bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; - bt_component_class_filter_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; - } filter; - - struct { - bt_component_class_sink_init_method init; - bt_component_class_sink_finalize_method finalize; - bt_component_class_sink_query_method query; - bt_component_class_sink_accept_input_port_connection_method accept_input_port_connection; - bt_component_class_sink_input_port_connected_method input_port_connected; - bt_component_class_sink_graph_is_configured_method graph_is_configured; - } sink; - } methods; - }; - - enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; - struct __bt_plugin_descriptor_attribute const * const *cur_attr_ptr; - struct __bt_plugin_component_class_descriptor const * const *cur_cc_descr_ptr; - struct __bt_plugin_component_class_descriptor_attribute const * const *cur_cc_descr_attr_ptr; - struct bt_plugin_so_spec_data *spec = plugin->spec_data; - GArray *comp_class_full_descriptors; - size_t i; - int ret; - - BT_LOGD("Initializing plugin object from descriptors found in sections: " - "plugin-addr=%p, plugin-path=\"%s\", " - "attrs-begin-addr=%p, attrs-end-addr=%p, " - "cc-descr-begin-addr=%p, cc-descr-end-addr=%p, " - "cc-descr-attrs-begin-addr=%p, cc-descr-attrs-end-addr=%p", - plugin, - spec->shared_lib_handle->path ? - spec->shared_lib_handle->path->str : NULL, - attrs_begin, attrs_end, - cc_descriptors_begin, cc_descriptors_end, - cc_descr_attrs_begin, cc_descr_attrs_end); - comp_class_full_descriptors = g_array_new(FALSE, TRUE, - sizeof(struct comp_class_full_descriptor)); - if (!comp_class_full_descriptors) { - BT_LOGE_STR("Failed to allocate a GArray."); - status = BT_PLUGIN_STATUS_ERROR; - goto end; - } - - /* Set mandatory attributes */ - spec->descriptor = descriptor; - bt_plugin_set_name(plugin, descriptor->name); - - /* - * Find and set optional attributes attached to this plugin - * descriptor. - */ - for (cur_attr_ptr = attrs_begin; cur_attr_ptr != attrs_end; cur_attr_ptr++) { - const struct __bt_plugin_descriptor_attribute *cur_attr = - *cur_attr_ptr; - - if (cur_attr == NULL) { - continue; - } - - if (cur_attr->plugin_descriptor != descriptor) { - continue; - } - - switch (cur_attr->type) { - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_INIT: - spec->init = cur_attr->value.init; - break; - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_EXIT: - spec->shared_lib_handle->exit = cur_attr->value.exit; - break; - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_AUTHOR: - bt_plugin_set_author(plugin, cur_attr->value.author); - break; - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_LICENSE: - bt_plugin_set_license(plugin, cur_attr->value.license); - break; - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_DESCRIPTION: - bt_plugin_set_description(plugin, cur_attr->value.description); - break; - case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_VERSION: - bt_plugin_set_version(plugin, - (unsigned int) cur_attr->value.version.major, - (unsigned int) cur_attr->value.version.minor, - (unsigned int) cur_attr->value.version.patch, - cur_attr->value.version.extra); - break; - default: - /* - * WARN-level logging because this should not - * happen with the appropriate ABI version. If - * we're here, we know that for the reported - * version of the ABI, this attribute is - * unknown. - */ - BT_LOGW("Ignoring unknown plugin descriptor attribute: " - "plugin-path=\"%s\", plugin-name=\"%s\", " - "attr-type-name=\"%s\", attr-type-id=%d", - spec->shared_lib_handle->path ? - spec->shared_lib_handle->path->str : - NULL, - descriptor->name, cur_attr->type_name, - cur_attr->type); - break; - } - } - - /* - * Find component class descriptors attached to this plugin - * descriptor and initialize corresponding full component class - * descriptors in the array. - */ - for (cur_cc_descr_ptr = cc_descriptors_begin; cur_cc_descr_ptr != cc_descriptors_end; cur_cc_descr_ptr++) { - const struct __bt_plugin_component_class_descriptor *cur_cc_descr = - *cur_cc_descr_ptr; - struct comp_class_full_descriptor full_descriptor = {0}; - - if (cur_cc_descr == NULL) { - continue; - } - - if (cur_cc_descr->plugin_descriptor != descriptor) { - continue; - } - - full_descriptor.descriptor = cur_cc_descr; - g_array_append_val(comp_class_full_descriptors, - full_descriptor); - } - - /* - * Find component class descriptor attributes attached to this - * plugin descriptor and update corresponding full component - * class descriptors in the array. - */ - for (cur_cc_descr_attr_ptr = cc_descr_attrs_begin; cur_cc_descr_attr_ptr != cc_descr_attrs_end; cur_cc_descr_attr_ptr++) { - const struct __bt_plugin_component_class_descriptor_attribute *cur_cc_descr_attr = - *cur_cc_descr_attr_ptr; - enum bt_component_class_type cc_type; - - if (cur_cc_descr_attr == NULL) { - continue; - } - - if (cur_cc_descr_attr->comp_class_descriptor->plugin_descriptor != - descriptor) { - continue; - } - - cc_type = cur_cc_descr_attr->comp_class_descriptor->type; - - /* Find the corresponding component class descriptor entry */ - for (i = 0; i < comp_class_full_descriptors->len; i++) { - struct comp_class_full_descriptor *cc_full_descr = - &g_array_index(comp_class_full_descriptors, - struct comp_class_full_descriptor, i); - - if (cur_cc_descr_attr->comp_class_descriptor != - cc_full_descr->descriptor) { - continue; - } - - switch (cur_cc_descr_attr->type) { - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_DESCRIPTION: - cc_full_descr->description = - cur_cc_descr_attr->value.description; - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_HELP: - cc_full_descr->help = - cur_cc_descr_attr->value.help; - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_INIT_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.init = - cur_cc_descr_attr->value.source_init_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.init = - cur_cc_descr_attr->value.filter_init_method; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.init = - cur_cc_descr_attr->value.sink_init_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_FINALIZE_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.finalize = - cur_cc_descr_attr->value.source_finalize_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.finalize = - cur_cc_descr_attr->value.filter_finalize_method; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.finalize = - cur_cc_descr_attr->value.sink_finalize_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_QUERY_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.query = - cur_cc_descr_attr->value.source_query_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.query = - cur_cc_descr_attr->value.filter_query_method; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.query = - cur_cc_descr_attr->value.sink_query_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_ACCEPT_INPUT_PORT_CONNECTION_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.accept_input_port_connection = - cur_cc_descr_attr->value.filter_accept_input_port_connection_method; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.accept_input_port_connection = - cur_cc_descr_attr->value.sink_accept_input_port_connection_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_ACCEPT_OUTPUT_PORT_CONNECTION_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.accept_output_port_connection = - cur_cc_descr_attr->value.source_accept_output_port_connection_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.accept_output_port_connection = - cur_cc_descr_attr->value.filter_accept_output_port_connection_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_INPUT_PORT_CONNECTED_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.input_port_connected = - cur_cc_descr_attr->value.filter_input_port_connected_method; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.input_port_connected = - cur_cc_descr_attr->value.sink_input_port_connected_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_OUTPUT_PORT_CONNECTED_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.output_port_connected = - cur_cc_descr_attr->value.source_output_port_connected_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.output_port_connected = - cur_cc_descr_attr->value.filter_output_port_connected_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_GRAPH_IS_CONFIGURED_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SINK: - cc_full_descr->methods.sink.graph_is_configured = - cur_cc_descr_attr->value.sink_graph_is_configured_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_INIT_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_init = - cur_cc_descr_attr->value.source_msg_iter_init_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_init = - cur_cc_descr_attr->value.filter_msg_iter_init_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_FINALIZE_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_finalize = - cur_cc_descr_attr->value.source_msg_iter_finalize_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_finalize = - cur_cc_descr_attr->value.filter_msg_iter_finalize_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_SEEK_NS_FROM_ORIGIN_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_seek_ns_from_origin = - cur_cc_descr_attr->value.source_msg_iter_seek_ns_from_origin_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin = - cur_cc_descr_attr->value.filter_msg_iter_seek_ns_from_origin_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_SEEK_BEGINNING_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_seek_beginning = - cur_cc_descr_attr->value.source_msg_iter_seek_beginning_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_seek_beginning = - cur_cc_descr_attr->value.filter_msg_iter_seek_beginning_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_CAN_SEEK_NS_FROM_ORIGIN_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin = - cur_cc_descr_attr->value.source_msg_iter_can_seek_ns_from_origin_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin = - cur_cc_descr_attr->value.filter_msg_iter_can_seek_ns_from_origin_method; - break; - default: - abort(); - } - break; - case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_CAN_SEEK_BEGINNING_METHOD: - switch (cc_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cc_full_descr->methods.source.msg_iter_can_seek_beginning = - cur_cc_descr_attr->value.source_msg_iter_can_seek_beginning_method; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cc_full_descr->methods.filter.msg_iter_can_seek_beginning = - cur_cc_descr_attr->value.filter_msg_iter_can_seek_beginning_method; - break; - default: - abort(); - } - break; - default: - /* - * WARN-level logging because this - * should not happen with the - * appropriate ABI version. If we're - * here, we know that for the reported - * version of the ABI, this attribute is - * unknown. - */ - BT_LOGW("Ignoring unknown component class descriptor attribute: " - "plugin-path=\"%s\", " - "plugin-name=\"%s\", " - "comp-class-name=\"%s\", " - "comp-class-type=%s, " - "attr-type-name=\"%s\", " - "attr-type-id=%d", - spec->shared_lib_handle->path ? - spec->shared_lib_handle->path->str : - NULL, - descriptor->name, - cur_cc_descr_attr->comp_class_descriptor->name, - bt_component_class_type_string( - cur_cc_descr_attr->comp_class_descriptor->type), - cur_cc_descr_attr->type_name, - cur_cc_descr_attr->type); - break; - } - } - } - - /* Initialize plugin */ - if (spec->init) { - enum bt_self_plugin_status init_status; - - BT_LOGD_STR("Calling user's plugin initialization function."); - init_status = spec->init((void *) plugin); - BT_LOGD("User function returned: %s", - bt_self_plugin_status_string(init_status)); - - if (init_status < 0) { - BT_LOGW_STR("User's plugin initialization function failed."); - status = BT_PLUGIN_STATUS_ERROR; - goto end; - } - } - - spec->shared_lib_handle->init_called = BT_TRUE; - - /* Add described component classes to plugin */ - for (i = 0; i < comp_class_full_descriptors->len; i++) { - struct comp_class_full_descriptor *cc_full_descr = - &g_array_index(comp_class_full_descriptors, - struct comp_class_full_descriptor, i); - struct bt_component_class *comp_class = NULL; - struct bt_component_class_source *src_comp_class = NULL; - struct bt_component_class_filter *flt_comp_class = NULL; - struct bt_component_class_sink *sink_comp_class = NULL; - - BT_LOGD("Creating and setting properties of plugin's component class: " - "plugin-path=\"%s\", plugin-name=\"%s\", " - "comp-class-name=\"%s\", comp-class-type=%s", - spec->shared_lib_handle->path ? - spec->shared_lib_handle->path->str : - NULL, - descriptor->name, - cc_full_descr->descriptor->name, - bt_component_class_type_string( - cc_full_descr->descriptor->type)); - - switch (cc_full_descr->descriptor->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - src_comp_class = bt_component_class_source_create( - cc_full_descr->descriptor->name, - cc_full_descr->descriptor->methods.source.msg_iter_next); - comp_class = bt_component_class_source_as_component_class( - src_comp_class); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - flt_comp_class = bt_component_class_filter_create( - cc_full_descr->descriptor->name, - cc_full_descr->descriptor->methods.source.msg_iter_next); - comp_class = bt_component_class_filter_as_component_class( - flt_comp_class); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - sink_comp_class = bt_component_class_sink_create( - cc_full_descr->descriptor->name, - cc_full_descr->descriptor->methods.sink.consume); - comp_class = bt_component_class_sink_as_component_class( - sink_comp_class); - break; - default: - /* - * WARN-level logging because this should not - * happen with the appropriate ABI version. If - * we're here, we know that for the reported - * version of the ABI, this component class type - * is unknown. - */ - BT_LOGW("Ignoring unknown component class type: " - "plugin-path=\"%s\", plugin-name=\"%s\", " - "comp-class-name=\"%s\", comp-class-type=%d", - spec->shared_lib_handle->path->str ? - spec->shared_lib_handle->path->str : - NULL, - descriptor->name, - cc_full_descr->descriptor->name, - cc_full_descr->descriptor->type); - continue; - } - - if (!comp_class) { - BT_LOGE_STR("Cannot create component class."); - status = BT_PLUGIN_STATUS_ERROR; - goto end; - } - - if (cc_full_descr->description) { - ret = bt_component_class_set_description( - comp_class, cc_full_descr->description); - if (ret) { - BT_LOGE_STR("Cannot set component class's description."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(comp_class); - goto end; - } - } - - if (cc_full_descr->help) { - ret = bt_component_class_set_help(comp_class, - cc_full_descr->help); - if (ret) { - BT_LOGE_STR("Cannot set component class's help string."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(comp_class); - goto end; - } - } - - switch (cc_full_descr->descriptor->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - if (cc_full_descr->methods.source.init) { - ret = bt_component_class_source_set_init_method( - src_comp_class, - cc_full_descr->methods.source.init); - if (ret) { - BT_LOGE_STR("Cannot set source component class's initialization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.finalize) { - ret = bt_component_class_source_set_finalize_method( - src_comp_class, - cc_full_descr->methods.source.finalize); - if (ret) { - BT_LOGE_STR("Cannot set source component class's finalization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.query) { - ret = bt_component_class_source_set_query_method( - src_comp_class, - cc_full_descr->methods.source.query); - if (ret) { - BT_LOGE_STR("Cannot set source component class's query method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.accept_output_port_connection) { - ret = bt_component_class_source_set_accept_output_port_connection_method( - src_comp_class, - cc_full_descr->methods.source.accept_output_port_connection); - if (ret) { - BT_LOGE_STR("Cannot set source component class's \"accept input output connection\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.output_port_connected) { - ret = bt_component_class_source_set_output_port_connected_method( - src_comp_class, - cc_full_descr->methods.source.output_port_connected); - if (ret) { - BT_LOGE_STR("Cannot set source component class's \"output port connected\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_init) { - ret = bt_component_class_source_set_message_iterator_init_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_init); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator initialization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_finalize) { - ret = bt_component_class_source_set_message_iterator_finalize_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_finalize); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator finalization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_seek_ns_from_origin) { - ret = bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_seek_ns_from_origin); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator \"seek nanoseconds from origin\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_seek_beginning) { - ret = bt_component_class_source_set_message_iterator_seek_beginning_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_seek_beginning); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator \"seek beginning\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin) { - ret = bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator \"can seek nanoseconds from origin\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.source.msg_iter_can_seek_beginning) { - ret = bt_component_class_source_set_message_iterator_can_seek_beginning_method( - src_comp_class, - cc_full_descr->methods.source.msg_iter_can_seek_beginning); - if (ret) { - BT_LOGE_STR("Cannot set source component class's message iterator \"can seek beginning\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); - goto end; - } - } - - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - if (cc_full_descr->methods.filter.init) { - ret = bt_component_class_filter_set_init_method( - flt_comp_class, - cc_full_descr->methods.filter.init); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's initialization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.finalize) { - ret = bt_component_class_filter_set_finalize_method( - flt_comp_class, - cc_full_descr->methods.filter.finalize); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's finalization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.query) { - ret = bt_component_class_filter_set_query_method( - flt_comp_class, - cc_full_descr->methods.filter.query); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's query method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.accept_input_port_connection) { - ret = bt_component_class_filter_set_accept_input_port_connection_method( - flt_comp_class, - cc_full_descr->methods.filter.accept_input_port_connection); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's \"accept input port connection\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.accept_output_port_connection) { - ret = bt_component_class_filter_set_accept_output_port_connection_method( - flt_comp_class, - cc_full_descr->methods.filter.accept_output_port_connection); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's \"accept input output connection\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.input_port_connected) { - ret = bt_component_class_filter_set_input_port_connected_method( - flt_comp_class, - cc_full_descr->methods.filter.input_port_connected); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's \"input port connected\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.output_port_connected) { - ret = bt_component_class_filter_set_output_port_connected_method( - flt_comp_class, - cc_full_descr->methods.filter.output_port_connected); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's \"output port connected\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_init) { - ret = bt_component_class_filter_set_message_iterator_init_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_init); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator initialization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_finalize) { - ret = bt_component_class_filter_set_message_iterator_finalize_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_finalize); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator finalization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin) { - ret = bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator \"seek nanoseconds from origin\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_seek_beginning) { - ret = bt_component_class_filter_set_message_iterator_seek_beginning_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_seek_beginning); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator \"seek beginning\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin) { - ret = bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek nanoseconds from origin\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.filter.msg_iter_can_seek_beginning) { - ret = bt_component_class_filter_set_message_iterator_can_seek_beginning_method( - flt_comp_class, - cc_full_descr->methods.filter.msg_iter_can_seek_beginning); - if (ret) { - BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek beginning\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); - goto end; - } - } - - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - if (cc_full_descr->methods.sink.init) { - ret = bt_component_class_sink_set_init_method( - sink_comp_class, - cc_full_descr->methods.sink.init); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's initialization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.sink.finalize) { - ret = bt_component_class_sink_set_finalize_method( - sink_comp_class, - cc_full_descr->methods.sink.finalize); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's finalization method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.sink.query) { - ret = bt_component_class_sink_set_query_method( - sink_comp_class, - cc_full_descr->methods.sink.query); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's query method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.sink.accept_input_port_connection) { - ret = bt_component_class_sink_set_accept_input_port_connection_method( - sink_comp_class, - cc_full_descr->methods.sink.accept_input_port_connection); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's \"accept input port connection\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.sink.input_port_connected) { - ret = bt_component_class_sink_set_input_port_connected_method( - sink_comp_class, - cc_full_descr->methods.sink.input_port_connected); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's \"input port connected\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - if (cc_full_descr->methods.sink.graph_is_configured) { - ret = bt_component_class_sink_set_graph_is_configured_method( - sink_comp_class, - cc_full_descr->methods.sink.graph_is_configured); - if (ret) { - BT_LOGE_STR("Cannot set sink component class's \"graph is configured\" method."); - status = BT_PLUGIN_STATUS_ERROR; - BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); - goto end; - } - } - - break; - default: - abort(); - } - - /* - * Add component class to the plugin object. - * - * This will call back - * bt_plugin_so_on_add_component_class() so that we can - * add a mapping in the component class list when we - * know the component class is successfully added. - */ - status = bt_plugin_add_component_class(plugin, - (void *) comp_class); - BT_OBJECT_PUT_REF_AND_RESET(comp_class); - if (status < 0) { - BT_LOGE("Cannot add component class to plugin."); - goto end; - } - } - -end: - g_array_free(comp_class_full_descriptors, TRUE); - return status; -} - -static -struct bt_plugin *bt_plugin_so_create_empty( - struct bt_plugin_so_shared_lib_handle *shared_lib_handle) -{ - struct bt_plugin *plugin; - struct bt_plugin_so_spec_data *spec; - - plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_SO); - if (!plugin) { - goto error; - } - - plugin->destroy_spec_data = bt_plugin_so_destroy_spec_data; - plugin->spec_data = g_new0(struct bt_plugin_so_spec_data, 1); - if (!plugin->spec_data) { - BT_LOGE_STR("Failed to allocate one SO plugin specific data structure."); - goto error; - } - - spec = plugin->spec_data; - spec->shared_lib_handle = shared_lib_handle; - bt_object_get_no_null_check(spec->shared_lib_handle); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(plugin); - -end: - return plugin; -} - -static -size_t count_non_null_items_in_section(const void *begin, const void *end) -{ - size_t count = 0; - const int * const *begin_int = (const int * const *) begin; - const int * const *end_int = (const int * const *) end; - const int * const *iter; - - for (iter = begin_int; iter != end_int; iter++) { - if (*iter) { - count++; - } - } - - return count; -} - -static -struct bt_plugin_set *bt_plugin_so_create_all_from_sections( - struct bt_plugin_so_shared_lib_handle *shared_lib_handle, - struct __bt_plugin_descriptor const * const *descriptors_begin, - struct __bt_plugin_descriptor const * const *descriptors_end, - struct __bt_plugin_descriptor_attribute const * const *attrs_begin, - struct __bt_plugin_descriptor_attribute const * const *attrs_end, - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin, - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end, - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin, - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end) -{ - size_t descriptor_count; - size_t attrs_count; - size_t cc_descriptors_count; - size_t cc_descr_attrs_count; - size_t i; - struct bt_plugin_set *plugin_set = NULL; - - descriptor_count = count_non_null_items_in_section(descriptors_begin, descriptors_end); - attrs_count = count_non_null_items_in_section(attrs_begin, attrs_end); - cc_descriptors_count = count_non_null_items_in_section(cc_descriptors_begin, cc_descriptors_end); - cc_descr_attrs_count = count_non_null_items_in_section(cc_descr_attrs_begin, cc_descr_attrs_end); - - BT_LOGD("Creating all SO plugins from sections: " - "plugin-path=\"%s\", " - "descr-begin-addr=%p, descr-end-addr=%p, " - "attrs-begin-addr=%p, attrs-end-addr=%p, " - "cc-descr-begin-addr=%p, cc-descr-end-addr=%p, " - "cc-descr-attrs-begin-addr=%p, cc-descr-attrs-end-addr=%p, " - "descr-count=%zu, attrs-count=%zu, " - "cc-descr-count=%zu, cc-descr-attrs-count=%zu", - shared_lib_handle->path ? shared_lib_handle->path->str : NULL, - descriptors_begin, descriptors_end, - attrs_begin, attrs_end, - cc_descriptors_begin, cc_descriptors_end, - cc_descr_attrs_begin, cc_descr_attrs_end, - descriptor_count, attrs_count, - cc_descriptors_count, cc_descr_attrs_count); - plugin_set = bt_plugin_set_create(); - if (!plugin_set) { - BT_LOGE_STR("Cannot create empty plugin set."); - goto error; - } - - for (i = 0; i < descriptors_end - descriptors_begin; i++) { - enum bt_plugin_status status; - const struct __bt_plugin_descriptor *descriptor = - descriptors_begin[i]; - struct bt_plugin *plugin; - - if (descriptor == NULL) { - continue; - } - - BT_LOGD("Creating plugin object for plugin: " - "name=\"%s\", abi-major=%d, abi-minor=%d", - descriptor->name, descriptor->major, descriptor->minor); - - if (descriptor->major > __BT_PLUGIN_VERSION_MAJOR) { - /* - * DEBUG-level logging because we're only - * _trying_ to open this file as a compatible - * Babeltrace plugin: if it's not, it's not an - * error. And because this can be tried during - * bt_plugin_find_all_from_dir(), it's not - * even a warning. - */ - BT_LOGD("Unknown ABI major version: abi-major=%d", - descriptor->major); - goto error; - } - - plugin = bt_plugin_so_create_empty(shared_lib_handle); - if (!plugin) { - BT_LOGE_STR("Cannot create empty shared library handle."); - goto error; - } - - if (shared_lib_handle && shared_lib_handle->path) { - bt_plugin_set_path(plugin, shared_lib_handle->path->str); - } - - status = bt_plugin_so_init(plugin, descriptor, attrs_begin, - attrs_end, cc_descriptors_begin, cc_descriptors_end, - cc_descr_attrs_begin, cc_descr_attrs_end); - if (status < 0) { - /* - * DEBUG-level logging because we're only - * _trying_ to open this file as a compatible - * Babeltrace plugin: if it's not, it's not an - * error. And because this can be tried during - * bt_plugin_find_all_from_dir(), it's not - * even a warning. - */ - BT_LOGD_STR("Cannot initialize SO plugin object from sections."); - BT_OBJECT_PUT_REF_AND_RESET(plugin); - goto error; - } - - /* Add to plugin set */ - bt_plugin_set_add_plugin(plugin_set, plugin); - bt_object_put_ref(plugin); - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(plugin_set); - -end: - return plugin_set; -} - -BT_HIDDEN -struct bt_plugin_set *bt_plugin_so_create_all_from_static(void) -{ - struct bt_plugin_set *plugin_set = NULL; - struct bt_plugin_so_shared_lib_handle *shared_lib_handle = - bt_plugin_so_shared_lib_handle_create(NULL); - - if (!shared_lib_handle) { - goto end; - } - - BT_LOGD_STR("Creating all SO plugins from built-in plugins."); - plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle, - __bt_get_begin_section_plugin_descriptors(), - __bt_get_end_section_plugin_descriptors(), - __bt_get_begin_section_plugin_descriptor_attributes(), - __bt_get_end_section_plugin_descriptor_attributes(), - __bt_get_begin_section_component_class_descriptors(), - __bt_get_end_section_component_class_descriptors(), - __bt_get_begin_section_component_class_descriptor_attributes(), - __bt_get_end_section_component_class_descriptor_attributes()); - -end: - BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); - - return plugin_set; -} - -BT_HIDDEN -struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path) -{ - size_t path_len; - struct bt_plugin_set *plugin_set = NULL; - struct __bt_plugin_descriptor const * const *descriptors_begin = NULL; - struct __bt_plugin_descriptor const * const *descriptors_end = NULL; - struct __bt_plugin_descriptor_attribute const * const *attrs_begin = NULL; - struct __bt_plugin_descriptor_attribute const * const *attrs_end = NULL; - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin = NULL; - struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end = NULL; - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin = NULL; - struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end = NULL; - struct __bt_plugin_descriptor const * const *(*get_begin_section_plugin_descriptors)(void); - struct __bt_plugin_descriptor const * const *(*get_end_section_plugin_descriptors)(void); - struct __bt_plugin_descriptor_attribute const * const *(*get_begin_section_plugin_descriptor_attributes)(void); - struct __bt_plugin_descriptor_attribute const * const *(*get_end_section_plugin_descriptor_attributes)(void); - struct __bt_plugin_component_class_descriptor const * const *(*get_begin_section_component_class_descriptors)(void); - struct __bt_plugin_component_class_descriptor const * const *(*get_end_section_component_class_descriptors)(void); - struct __bt_plugin_component_class_descriptor_attribute const * const *(*get_begin_section_component_class_descriptor_attributes)(void); - struct __bt_plugin_component_class_descriptor_attribute const * const *(*get_end_section_component_class_descriptor_attributes)(void); - bt_bool is_libtool_wrapper = BT_FALSE, is_shared_object = BT_FALSE; - struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL; - - if (!path) { - BT_LOGW_STR("Invalid parameter: path is NULL."); - goto end; - } - - BT_LOGD("Creating all SO plugins from file: path=\"%s\"", path); - path_len = strlen(path); - if (path_len <= PLUGIN_SUFFIX_LEN) { - BT_LOGW("Invalid parameter: path length is too short: " - "path-length=%zu", path_len); - goto end; - } - - path_len++; - /* - * Check if the file ends with a known plugin file type suffix (i.e. .so - * or .la on Linux). - */ - is_libtool_wrapper = !strncmp(LIBTOOL_PLUGIN_SUFFIX, - path + path_len - LIBTOOL_PLUGIN_SUFFIX_LEN, - LIBTOOL_PLUGIN_SUFFIX_LEN); - is_shared_object = !strncmp(NATIVE_PLUGIN_SUFFIX, - path + path_len - NATIVE_PLUGIN_SUFFIX_LEN, - NATIVE_PLUGIN_SUFFIX_LEN); - if (!is_shared_object && !is_libtool_wrapper) { - /* Name indicates this is not a plugin file; not an error */ - BT_LOGV("File is not a SO plugin file: path=\"%s\"", path); - goto end; - } - - shared_lib_handle = bt_plugin_so_shared_lib_handle_create(path); - if (!shared_lib_handle) { - BT_LOGD_STR("Cannot create shared library handle."); - goto end; - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_plugin_descriptors", - (gpointer *) &get_begin_section_plugin_descriptors)) { - descriptors_begin = get_begin_section_plugin_descriptors(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_begin_section_plugin_descriptors"); - goto end; - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_plugin_descriptors", - (gpointer *) &get_end_section_plugin_descriptors)) { - descriptors_end = get_end_section_plugin_descriptors(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_end_section_plugin_descriptors"); - goto end; - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_plugin_descriptor_attributes", - (gpointer *) &get_begin_section_plugin_descriptor_attributes)) { - attrs_begin = get_begin_section_plugin_descriptor_attributes(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_begin_section_plugin_descriptor_attributes"); - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_plugin_descriptor_attributes", - (gpointer *) &get_end_section_plugin_descriptor_attributes)) { - attrs_end = get_end_section_plugin_descriptor_attributes(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_end_section_plugin_descriptor_attributes"); - } - - if ((!!attrs_begin - !!attrs_end) != 0) { - BT_LOGD("Found section start or end symbol, but not both: " - "path=\"%s\", symbol-start=\"%s\", " - "symbol-end=\"%s\", symbol-start-addr=%p, " - "symbol-end-addr=%p", - path, "__bt_get_begin_section_plugin_descriptor_attributes", - "__bt_get_end_section_plugin_descriptor_attributes", - attrs_begin, attrs_end); - goto end; - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_component_class_descriptors", - (gpointer *) &get_begin_section_component_class_descriptors)) { - cc_descriptors_begin = get_begin_section_component_class_descriptors(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_begin_section_component_class_descriptors"); - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_component_class_descriptors", - (gpointer *) &get_end_section_component_class_descriptors)) { - cc_descriptors_end = get_end_section_component_class_descriptors(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_end_section_component_class_descriptors"); - } - - if ((!!cc_descriptors_begin - !!cc_descriptors_end) != 0) { - BT_LOGD("Found section start or end symbol, but not both: " - "path=\"%s\", symbol-start=\"%s\", " - "symbol-end=\"%s\", symbol-start-addr=%p, " - "symbol-end-addr=%p", - path, "__bt_get_begin_section_component_class_descriptors", - "__bt_get_end_section_component_class_descriptors", - cc_descriptors_begin, cc_descriptors_end); - goto end; - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_component_class_descriptor_attributes", - (gpointer *) &get_begin_section_component_class_descriptor_attributes)) { - cc_descr_attrs_begin = get_begin_section_component_class_descriptor_attributes(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_begin_section_component_class_descriptor_attributes"); - } - - if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_component_class_descriptor_attributes", - (gpointer *) &get_end_section_component_class_descriptor_attributes)) { - cc_descr_attrs_end = get_end_section_component_class_descriptor_attributes(); - } else { - BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " - "symbol=\"%s\"", path, - "__bt_get_end_section_component_class_descriptor_attributes"); - } - - if ((!!cc_descr_attrs_begin - !!cc_descr_attrs_end) != 0) { - BT_LOGD("Found section start or end symbol, but not both: " - "path=\"%s\", symbol-start=\"%s\", " - "symbol-end=\"%s\", symbol-start-addr=%p, " - "symbol-end-addr=%p", - path, "__bt_get_begin_section_component_class_descriptor_attributes", - "__bt_get_end_section_component_class_descriptor_attributes", - cc_descr_attrs_begin, cc_descr_attrs_end); - goto end; - } - - /* Initialize plugin */ - BT_LOGD_STR("Initializing plugin object."); - plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle, - descriptors_begin, descriptors_end, attrs_begin, attrs_end, - cc_descriptors_begin, cc_descriptors_end, - cc_descr_attrs_begin, cc_descr_attrs_end); - -end: - BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); - return plugin_set; -} - -static -void plugin_comp_class_destroy_listener(struct bt_component_class *comp_class, - void *data) -{ - bt_list_del(&comp_class->node); - BT_OBJECT_PUT_REF_AND_RESET(comp_class->so_handle); - BT_LOGV("Component class destroyed: removed entry from list: " - "comp-cls-addr=%p", comp_class); -} - -void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin, - struct bt_component_class *comp_class) -{ - struct bt_plugin_so_spec_data *spec = plugin->spec_data; - - BT_ASSERT(plugin->spec_data); - BT_ASSERT(plugin->type == BT_PLUGIN_TYPE_SO); - - bt_list_add(&comp_class->node, &component_class_list); - comp_class->so_handle = spec->shared_lib_handle; - bt_object_get_no_null_check(comp_class->so_handle); - - /* Add our custom destroy listener */ - bt_component_class_add_destroy_listener(comp_class, - plugin_comp_class_destroy_listener, NULL); -} diff --git a/lib/plugin/plugin.c b/lib/plugin/plugin.c deleted file mode 100644 index a6e9806d..00000000 --- a/lib/plugin/plugin.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PYTHON_PLUGIN_PROVIDER_FILENAME "libbabeltrace2-python-plugin-provider." G_MODULE_SUFFIX -#define PYTHON_PLUGIN_PROVIDER_SYM_NAME bt_plugin_python_create_all_from_file -#define PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR TOSTRING(PYTHON_PLUGIN_PROVIDER_SYM_NAME) - -#define APPEND_ALL_FROM_DIR_NFDOPEN_MAX 8 - -#ifdef BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT -#include - -static -struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path) = - bt_plugin_python_create_all_from_file; - -static -void init_python_plugin_provider(void) {} -#else /* BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT */ -static GModule *python_plugin_provider_module; -static -struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path); - -static -void init_python_plugin_provider(void) { - if (bt_plugin_python_create_all_from_file_sym != NULL) { - return; - } - - BT_LOGD_STR("Loading Python plugin provider module."); - python_plugin_provider_module = - g_module_open(PYTHON_PLUGIN_PROVIDER_FILENAME, 0); - if (!python_plugin_provider_module) { - BT_LOGI("Cannot open `%s`: %s: continuing without Python plugin support.", - PYTHON_PLUGIN_PROVIDER_FILENAME, g_module_error()); - return; - } - - if (!g_module_symbol(python_plugin_provider_module, - PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR, - (gpointer) &bt_plugin_python_create_all_from_file_sym)) { - BT_LOGI("Cannot find the Python plugin provider loading symbol: continuing without Python plugin support: " - "file=\"%s\", symbol=\"%s\"", - PYTHON_PLUGIN_PROVIDER_FILENAME, - PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR); - return; - } - - BT_LOGI("Loaded Python plugin provider module: addr=%p", - python_plugin_provider_module); -} - -__attribute__((destructor)) static -void fini_python_plugin_provider(void) { - if (python_plugin_provider_module) { - BT_LOGD("Unloading Python plugin provider module."); - - if (!g_module_close(python_plugin_provider_module)) { - BT_LOGE("Failed to close the Python plugin provider module: %s.", - g_module_error()); - } - - python_plugin_provider_module = NULL; - } -} -#endif - -uint64_t bt_plugin_set_get_plugin_count(struct bt_plugin_set *plugin_set) -{ - BT_ASSERT_PRE_NON_NULL(plugin_set, "Plugin set"); - return (uint64_t) plugin_set->plugins->len; -} - -const struct bt_plugin *bt_plugin_set_borrow_plugin_by_index_const( - const struct bt_plugin_set *plugin_set, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(plugin_set, "Plugin set"); - BT_ASSERT_PRE_VALID_INDEX(index, plugin_set->plugins->len); - return g_ptr_array_index(plugin_set->plugins, index); -} - -const struct bt_plugin_set *bt_plugin_find_all_from_static(void) -{ - /* bt_plugin_so_create_all_from_static() logs errors */ - return bt_plugin_so_create_all_from_static(); -} - -const struct bt_plugin_set *bt_plugin_find_all_from_file(const char *path) -{ - struct bt_plugin_set *plugin_set = NULL; - - BT_ASSERT_PRE_NON_NULL(path, "Path"); - BT_LOGD("Creating plugins from file: path=\"%s\"", path); - - /* Try shared object plugins */ - plugin_set = bt_plugin_so_create_all_from_file(path); - if (plugin_set) { - goto end; - } - - /* Try Python plugins if support is available */ - init_python_plugin_provider(); - if (bt_plugin_python_create_all_from_file_sym) { - plugin_set = bt_plugin_python_create_all_from_file_sym(path); - if (plugin_set) { - goto end; - } - } - -end: - if (plugin_set) { - BT_LOGD("Created %u plugins from file: " - "path=\"%s\", count=%u, plugin-set-addr=%p", - plugin_set->plugins->len, path, - plugin_set->plugins->len, plugin_set); - } else { - BT_LOGD("Found no plugins in file: path=\"%s\"", path); - } - - return plugin_set; -} - -static void destroy_gstring(void *data) -{ - g_string_free(data, TRUE); -} - -const struct bt_plugin *bt_plugin_find(const char *plugin_name) -{ - const char *system_plugin_dir; - char *home_plugin_dir = NULL; - const char *envvar; - const struct bt_plugin *plugin = NULL; - const struct bt_plugin_set *plugin_set = NULL; - GPtrArray *dirs = NULL; - int ret; - size_t i, j; - - BT_ASSERT_PRE_NON_NULL(plugin_name, "Name"); - BT_LOGD("Finding named plugin in standard directories and built-in plugins: " - "name=\"%s\"", plugin_name); - dirs = g_ptr_array_new_with_free_func((GDestroyNotify) destroy_gstring); - if (!dirs) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto end; - } - - /* - * Search order is: - * - * 1. BABELTRACE_PLUGIN_PATH environment variable - * (colon-separated list of directories) - * 2. ~/.local/lib/babeltrace2/plugins - * 3. Default system directory for Babeltrace plugins, usually - * /usr/lib/babeltrace2/plugins or - * /usr/local/lib/babeltrace2/plugins if installed - * locally - * 4. Built-in plugins (static) - * - * Directories are searched non-recursively. - */ - envvar = getenv("BABELTRACE_PLUGIN_PATH"); - if (envvar) { - ret = bt_common_append_plugin_path_dirs(envvar, dirs); - if (ret) { - BT_LOGE_STR("Failed to append plugin path to array of directories."); - goto end; - } - } - - home_plugin_dir = bt_common_get_home_plugin_path(); - if (home_plugin_dir) { - GString *home_plugin_dir_str = - g_string_new(home_plugin_dir); - - if (!home_plugin_dir_str) { - BT_LOGE_STR("Failed to allocate a GString."); - goto end; - } - - g_ptr_array_add(dirs, home_plugin_dir_str); - } - - system_plugin_dir = bt_common_get_system_plugin_path(); - if (system_plugin_dir) { - GString *system_plugin_dir_str = - g_string_new(system_plugin_dir); - - if (!system_plugin_dir_str) { - BT_LOGE_STR("Failed to allocate a GString."); - goto end; - } - - g_ptr_array_add(dirs, system_plugin_dir_str); - } - - for (i = 0; i < dirs->len; i++) { - GString *dir = g_ptr_array_index(dirs, i); - - BT_OBJECT_PUT_REF_AND_RESET(plugin_set); - - /* - * Skip this if the directory does not exist because - * bt_plugin_find_all_from_dir() would log a warning. - */ - if (!g_file_test(dir->str, G_FILE_TEST_IS_DIR)) { - BT_LOGV("Skipping nonexistent directory path: " - "path=\"%s\"", dir->str); - continue; - } - - /* bt_plugin_find_all_from_dir() logs details/errors */ - plugin_set = bt_plugin_find_all_from_dir(dir->str, BT_FALSE); - if (!plugin_set) { - BT_LOGD("No plugins found in directory: path=\"%s\"", - dir->str); - continue; - } - - for (j = 0; j < plugin_set->plugins->len; j++) { - const struct bt_plugin *candidate_plugin = - g_ptr_array_index(plugin_set->plugins, j); - - if (strcmp(bt_plugin_get_name(candidate_plugin), - plugin_name) == 0) { - BT_LOGD("Plugin found in directory: name=\"%s\", path=\"%s\"", - plugin_name, dir->str); - plugin = candidate_plugin; - bt_object_get_no_null_check(plugin); - goto end; - } - } - - BT_LOGD("Plugin not found in directory: name=\"%s\", path=\"%s\"", - plugin_name, dir->str); - } - - bt_object_put_ref(plugin_set); - plugin_set = bt_plugin_find_all_from_static(); - if (plugin_set) { - for (j = 0; j < plugin_set->plugins->len; j++) { - const struct bt_plugin *candidate_plugin = - g_ptr_array_index(plugin_set->plugins, j); - - if (strcmp(bt_plugin_get_name(candidate_plugin), - plugin_name) == 0) { - BT_LOGD("Plugin found in built-in plugins: " - "name=\"%s\"", plugin_name); - plugin = candidate_plugin; - bt_object_get_no_null_check(plugin); - goto end; - } - } - } - -end: - free(home_plugin_dir); - bt_object_put_ref(plugin_set); - - if (dirs) { - g_ptr_array_free(dirs, TRUE); - } - - if (plugin) { - BT_LIB_LOGD("Found plugin in standard directories and built-in plugins: " - "%!+l", plugin); - } else { - BT_LOGD("No plugin found in standard directories and built-in plugins: " - "name=\"%s\"", plugin_name); - } - - return plugin; -} - -static struct { - pthread_mutex_t lock; - struct bt_plugin_set *plugin_set; - bool recurse; -} append_all_from_dir_info = { - .lock = PTHREAD_MUTEX_INITIALIZER -}; - -static -int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag, - struct FTW *s) -{ - int ret = 0; - const char *name = file + s->base; - - /* Check for recursion */ - if (!append_all_from_dir_info.recurse && s->level > 1) { - goto end; - } - - switch (flag) { - case FTW_F: - { - const struct bt_plugin_set *plugins_from_file; - - if (name[0] == '.') { - /* Skip hidden files */ - BT_LOGV("Skipping hidden file: path=\"%s\"", file); - goto end; - } - - plugins_from_file = bt_plugin_find_all_from_file(file); - - if (plugins_from_file) { - size_t j; - - for (j = 0; j < plugins_from_file->plugins->len; j++) { - struct bt_plugin *plugin = - g_ptr_array_index(plugins_from_file->plugins, j); - - BT_LIB_LOGD("Adding plugin to plugin set: " - "plugin-path=\"%s\", %![plugin-]+l", - file, plugin); - bt_plugin_set_add_plugin( - append_all_from_dir_info.plugin_set, - plugin); - } - - bt_object_put_ref(plugins_from_file); - } - break; - } - case FTW_DNR: - /* Continue to next file / directory. */ - BT_LOGW("Cannot enter directory: continuing: path=\"%s\"", file); - break; - case FTW_NS: - /* Continue to next file / directory. */ - BT_LOGD("Cannot get file information: continuing: path=\"%s\"", file); - break; - } - -end: - return ret; -} - -static -enum bt_plugin_status bt_plugin_create_append_all_from_dir( - struct bt_plugin_set *plugin_set, const char *path, - bt_bool recurse) -{ - int nftw_flags = FTW_PHYS; - enum bt_plugin_status ret = BT_PLUGIN_STATUS_OK; - - BT_ASSERT(plugin_set); - BT_ASSERT(path); - BT_ASSERT(strlen(path) < PATH_MAX); - pthread_mutex_lock(&append_all_from_dir_info.lock); - append_all_from_dir_info.plugin_set = plugin_set; - append_all_from_dir_info.recurse = recurse; - ret = nftw(path, nftw_append_all_from_dir, - APPEND_ALL_FROM_DIR_NFDOPEN_MAX, nftw_flags); - pthread_mutex_unlock(&append_all_from_dir_info.lock); - if (ret != 0) { - BT_LOGW_ERRNO("Cannot open directory", ": path=\"%s\"", path); - ret = BT_PLUGIN_STATUS_ERROR; - } - - return ret; -} - -const struct bt_plugin_set *bt_plugin_find_all_from_dir(const char *path, - bt_bool recurse) -{ - struct bt_plugin_set *plugin_set; - enum bt_plugin_status status; - - BT_LOGD("Creating all plugins in directory: path=\"%s\", recurse=%d", - path, recurse); - plugin_set = bt_plugin_set_create(); - if (!plugin_set) { - BT_LOGE_STR("Cannot create empty plugin set."); - goto error; - } - - /* Append found plugins to array */ - status = bt_plugin_create_append_all_from_dir(plugin_set, path, - recurse); - if (status < 0) { - BT_LOGW("Cannot append plugins found in directory: " - "path=\"%s\", status=%s", - path, bt_plugin_status_string(status)); - goto error; - } - - BT_LOGD("Created %u plugins from directory: count=%u, path=\"%s\"", - plugin_set->plugins->len, plugin_set->plugins->len, path); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(plugin_set); - -end: - return plugin_set; -} - -const char *bt_plugin_get_name(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return plugin->info.name_set ? plugin->info.name->str : NULL; -} - -const char *bt_plugin_get_author(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return plugin->info.author_set ? plugin->info.author->str : NULL; -} - -const char *bt_plugin_get_license(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return plugin->info.license_set ? plugin->info.license->str : NULL; -} - -const char *bt_plugin_get_path(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return plugin->info.path_set ? plugin->info.path->str : NULL; -} - -const char *bt_plugin_get_description(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return plugin->info.description_set ? - plugin->info.description->str : NULL; -} - -enum bt_property_availability bt_plugin_get_version(const struct bt_plugin *plugin, - unsigned int *major, unsigned int *minor, unsigned int *patch, - const char **extra) -{ - enum bt_property_availability avail = - BT_PROPERTY_AVAILABILITY_AVAILABLE; - - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - - if (!plugin->info.version_set) { - BT_LIB_LOGV("Plugin's version is not set: %!+l", plugin); - avail = BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE; - goto end; - } - - if (major) { - *major = plugin->info.version.major; - } - - if (minor) { - *minor = plugin->info.version.minor; - } - - if (patch) { - *patch = plugin->info.version.patch; - } - - if (extra) { - *extra = plugin->info.version.extra->str; - } - -end: - return avail; -} - -uint64_t bt_plugin_get_source_component_class_count(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return (uint64_t) plugin->src_comp_classes->len; -} - -uint64_t bt_plugin_get_filter_component_class_count(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return (uint64_t) plugin->flt_comp_classes->len; -} - -uint64_t bt_plugin_get_sink_component_class_count(const struct bt_plugin *plugin) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - return (uint64_t) plugin->sink_comp_classes->len; -} - -static inline -struct bt_component_class *borrow_component_class_by_index( - const struct bt_plugin *plugin, GPtrArray *comp_classes, - uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - BT_ASSERT_PRE_VALID_INDEX(index, comp_classes->len); - return g_ptr_array_index(comp_classes, index); -} - -const struct bt_component_class_source * -bt_plugin_borrow_source_component_class_by_index_const( - const struct bt_plugin *plugin, uint64_t index) -{ - return (const void *) borrow_component_class_by_index(plugin, - plugin->src_comp_classes, index); -} - -const struct bt_component_class_filter * -bt_plugin_borrow_filter_component_class_by_index_const( - const struct bt_plugin *plugin, uint64_t index) -{ - return (const void *) borrow_component_class_by_index(plugin, - plugin->flt_comp_classes, index); -} - -const struct bt_component_class_sink * -bt_plugin_borrow_sink_component_class_by_index_const( - const struct bt_plugin *plugin, uint64_t index) -{ - return (const void *) borrow_component_class_by_index(plugin, - plugin->sink_comp_classes, index); -} - -static inline -struct bt_component_class *borrow_component_class_by_name( - const struct bt_plugin *plugin, GPtrArray *comp_classes, - const char *name) -{ - struct bt_component_class *comp_class = NULL; - size_t i; - - BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - - for (i = 0; i < comp_classes->len; i++) { - struct bt_component_class *comp_class_candidate = - g_ptr_array_index(comp_classes, i); - const char *comp_class_cand_name = - bt_component_class_get_name(comp_class_candidate); - - BT_ASSERT(comp_class_cand_name); - - if (strcmp(name, comp_class_cand_name) == 0) { - comp_class = comp_class_candidate; - break; - } - } - - return comp_class; -} - -const struct bt_component_class_source * -bt_plugin_borrow_source_component_class_by_name_const( - const struct bt_plugin *plugin, const char *name) -{ - return (const void *) borrow_component_class_by_name(plugin, - plugin->src_comp_classes, name); -} - -const struct bt_component_class_filter * -bt_plugin_borrow_filter_component_class_by_name_const( - const struct bt_plugin *plugin, const char *name) -{ - return (const void *) borrow_component_class_by_name(plugin, - plugin->flt_comp_classes, name); -} - -const struct bt_component_class_sink * -bt_plugin_borrow_sink_component_class_by_name_const( - const struct bt_plugin *plugin, const char *name) -{ - return (const void *) borrow_component_class_by_name(plugin, - plugin->sink_comp_classes, name); -} - -void bt_plugin_get_ref(const struct bt_plugin *plugin) -{ - bt_object_get_ref(plugin); -} - -void bt_plugin_put_ref(const struct bt_plugin *plugin) -{ - bt_object_put_ref(plugin); -} - -void bt_plugin_set_get_ref(const struct bt_plugin_set *plugin_set) -{ - bt_object_get_ref(plugin_set); -} - -void bt_plugin_set_put_ref(const struct bt_plugin_set *plugin_set) -{ - bt_object_put_ref(plugin_set); -} diff --git a/lib/prio_heap/Makefile.am b/lib/prio_heap/Makefile.am deleted file mode 100644 index d1e23b0f..00000000 --- a/lib/prio_heap/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -noinst_LTLIBRARIES = libprio_heap.la - -libprio_heap_la_SOURCES = prio_heap.c diff --git a/lib/prio_heap/prio_heap.c b/lib/prio_heap/prio_heap.c deleted file mode 100644 index 97a69ac4..00000000 --- a/lib/prio_heap/prio_heap.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Static-sized priority heap containing pointers. Based on CLRS, - * chapter 6. - * - * Copyright 2011 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#ifdef DEBUG_HEAP -void check_heap(const struct ptr_heap *heap) -{ - size_t i; - - if (!heap->len) - return; - - for (i = 1; i < heap->len; i++) - BT_ASSERT(!heap->gt(heap->ptrs[i], heap->ptrs[0])); -} -#endif - -static -size_t parent(size_t i) -{ - return (i - 1) >> 1; -} - -static -size_t left(size_t i) -{ - return (i << 1) + 1; -} - -static -size_t right(size_t i) -{ - return (i << 1) + 2; -} - -/* - * Copy of heap->ptrs pointer is invalid after heap_grow. - */ -static -int heap_grow(struct ptr_heap *heap, size_t new_len) -{ - void **new_ptrs; - - if (likely(heap->alloc_len >= new_len)) - return 0; - - heap->alloc_len = max_t(size_t, new_len, heap->alloc_len << 1); - new_ptrs = calloc(heap->alloc_len, sizeof(void *)); - if (unlikely(!new_ptrs)) - return -ENOMEM; - if (likely(heap->ptrs)) - memcpy(new_ptrs, heap->ptrs, heap->len * sizeof(void *)); - free(heap->ptrs); - heap->ptrs = new_ptrs; - return 0; -} - -static -int heap_set_len(struct ptr_heap *heap, size_t new_len) -{ - int ret; - - ret = heap_grow(heap, new_len); - if (unlikely(ret)) - return ret; - heap->len = new_len; - return 0; -} - -int bt_heap_init(struct ptr_heap *heap, size_t alloc_len, - int gt(void *a, void *b)) -{ - heap->ptrs = NULL; - heap->len = 0; - heap->alloc_len = 0; - heap->gt = gt; - /* - * Minimum size allocated is 1 entry to ensure memory allocation - * never fails within bt_heap_replace_max. - */ - return heap_grow(heap, max_t(size_t, 1, alloc_len)); -} - -void bt_heap_free(struct ptr_heap *heap) -{ - free(heap->ptrs); -} - -static void heapify(struct ptr_heap *heap, size_t i) -{ - void **ptrs = heap->ptrs; - size_t l, r, largest; - - for (;;) { - void *tmp; - - l = left(i); - r = right(i); - if (l < heap->len && heap->gt(ptrs[l], ptrs[i])) - largest = l; - else - largest = i; - if (r < heap->len && heap->gt(ptrs[r], ptrs[largest])) - largest = r; - if (unlikely(largest == i)) - break; - tmp = ptrs[i]; - ptrs[i] = ptrs[largest]; - ptrs[largest] = tmp; - i = largest; - } - check_heap(heap); -} - -void *bt_heap_replace_max(struct ptr_heap *heap, void *p) -{ - void *res; - - if (unlikely(!heap->len)) { - (void) heap_set_len(heap, 1); - heap->ptrs[0] = p; - check_heap(heap); - return NULL; - } - - /* Replace the current max and heapify */ - res = heap->ptrs[0]; - heap->ptrs[0] = p; - heapify(heap, 0); - return res; -} - -int bt_heap_insert(struct ptr_heap *heap, void *p) -{ - void **ptrs; - size_t pos; - int ret; - - ret = heap_set_len(heap, heap->len + 1); - if (unlikely(ret)) - return ret; - ptrs = heap->ptrs; - pos = heap->len - 1; - while (pos > 0 && heap->gt(p, ptrs[parent(pos)])) { - /* Move parent down until we find the right spot */ - ptrs[pos] = ptrs[parent(pos)]; - pos = parent(pos); - } - ptrs[pos] = p; - check_heap(heap); - return 0; -} - -void *bt_heap_remove(struct ptr_heap *heap) -{ - switch (heap->len) { - case 0: - return NULL; - case 1: - (void) heap_set_len(heap, 0); - return heap->ptrs[0]; - } - /* Shrink, replace the current max by previous last entry and heapify */ - heap_set_len(heap, heap->len - 1); - /* len changed. previous last entry is at heap->len */ - return bt_heap_replace_max(heap, heap->ptrs[heap->len]); -} - -void *bt_heap_cherrypick(struct ptr_heap *heap, void *p) -{ - size_t pos, len = heap->len; - - for (pos = 0; pos < len; pos++) - if (unlikely(heap->ptrs[pos] == p)) - goto found; - return NULL; -found: - if (unlikely(heap->len == 1)) { - (void) heap_set_len(heap, 0); - check_heap(heap); - return heap->ptrs[0]; - } - /* Replace p with previous last entry and heapify. */ - heap_set_len(heap, heap->len - 1); - /* len changed. previous last entry is at heap->len */ - heap->ptrs[pos] = heap->ptrs[heap->len]; - heapify(heap, pos); - return p; -} - -int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src) -{ - int ret; - - ret = bt_heap_init(dst, src->alloc_len, src->gt); - if (ret < 0) - goto end; - - ret = heap_set_len(dst, src->len); - if (ret < 0) - goto end; - - memcpy(dst->ptrs, src->ptrs, src->len * sizeof(void *)); - -end: - return ret; -} diff --git a/lib/trace-ir/Makefile.am b/lib/trace-ir/Makefile.am deleted file mode 100644 index 0b2107e0..00000000 --- a/lib/trace-ir/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -noinst_LTLIBRARIES = libtrace-ir.la - -libtrace_ir_la_SOURCES = \ - attributes.c \ - clock-class.c \ - clock-snapshot.c \ - event.c \ - event-class.c \ - field-wrapper.c \ - field.c \ - field-class.c \ - field-path.c \ - packet.c \ - packet-context-field.c \ - resolve-field-path.c \ - stream.c \ - stream-class.c \ - trace.c \ - trace-class.c \ - utils.c - -libtrace_ir_la_LIBADD = $(UUID_LIBS) diff --git a/lib/trace-ir/attributes.c b/lib/trace-ir/attributes.c deleted file mode 100644 index 8e85af5c..00000000 --- a/lib/trace-ir/attributes.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "ATTRS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ATTR_NAME_INDEX 0 -#define BT_ATTR_VALUE_INDEX 1 - -BT_HIDDEN -struct bt_value *bt_attributes_create(void) -{ - struct bt_value *attr_obj; - - /* - * Attributes: array value object of array value objects, each one - * containing two entries: a string value object (attributes - * field name), and a value object (attributes field value). - * - * Example (JSON representation): - * - * [ - * ["hostname", "eeppdesk"], - * ["sysname", "Linux"], - * ["tracer_major", 2], - * ["tracer_minor", 5] - * ] - */ - BT_LOGD_STR("Creating attributes object."); - attr_obj = bt_value_array_create(); - if (!attr_obj) { - BT_LOGE_STR("Failed to create array value."); - } else { - BT_LOGD("Created attributes object: addr=%p", - attr_obj); - } - - return attr_obj; -} - -BT_HIDDEN -void bt_attributes_destroy(struct bt_value *attr_obj) -{ - BT_LOGD("Destroying attributes object: addr=%p", attr_obj); - BT_OBJECT_PUT_REF_AND_RESET(attr_obj); -} - -BT_HIDDEN -int64_t bt_attributes_get_count(const struct bt_value *attr_obj) -{ - return bt_value_array_get_size(attr_obj); -} - -BT_HIDDEN -const char *bt_attributes_get_field_name(const struct bt_value *attr_obj, - uint64_t index) -{ - const char *ret = NULL; - const struct bt_value *attr_field_obj = NULL; - const struct bt_value *attr_field_name_obj = NULL; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - goto end; - } - - if (index >= bt_value_array_get_size(attr_obj)) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "index=%" PRIu64 ", count=%" PRId64, - index, bt_value_array_get_size(attr_obj)); - goto end; - } - - attr_field_obj = bt_value_array_borrow_element_by_index_const( - attr_obj, index); - if (!attr_field_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, index); - goto end; - } - - attr_field_name_obj = - bt_value_array_borrow_element_by_index_const(attr_field_obj, - BT_ATTR_NAME_INDEX); - if (!attr_field_name_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_ATTR_NAME_INDEX); - goto end; - } - - ret = bt_value_string_get(attr_field_name_obj); - -end: - return ret; -} - -BT_HIDDEN -struct bt_value *bt_attributes_borrow_field_value( - struct bt_value *attr_obj, uint64_t index) -{ - struct bt_value *value_obj = NULL; - struct bt_value *attr_field_obj = NULL; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - goto end; - } - - if (index >= bt_value_array_get_size(attr_obj)) { - BT_LOGW("Invalid parameter: index is out of bounds: " - "index=%" PRIu64 ", count=%" PRId64, - index, bt_value_array_get_size(attr_obj)); - goto end; - } - - attr_field_obj = - bt_value_array_borrow_element_by_index(attr_obj, index); - if (!attr_field_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, index); - goto end; - } - - value_obj = bt_value_array_borrow_element_by_index( - attr_field_obj, BT_ATTR_VALUE_INDEX); - if (!value_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_ATTR_VALUE_INDEX); - } - -end: - return value_obj; -} - -static -struct bt_value *bt_attributes_borrow_field_by_name( - struct bt_value *attr_obj, const char *name) -{ - uint64_t i; - int64_t attr_size; - struct bt_value *value_obj = NULL; - struct bt_value *attr_field_name_obj = NULL; - - attr_size = bt_value_array_get_size(attr_obj); - if (attr_size < 0) { - BT_LOGE("Cannot get array value's size: value-addr=%p", - attr_obj); - goto error; - } - - for (i = 0; i < attr_size; ++i) { - const char *field_name; - - value_obj = bt_value_array_borrow_element_by_index( - attr_obj, i); - if (!value_obj) { - BT_LOGE("Cannot get attributes object's array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_obj, i); - goto error; - } - - attr_field_name_obj = - bt_value_array_borrow_element_by_index( - value_obj, BT_ATTR_NAME_INDEX); - if (!attr_field_name_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, - value_obj, (int64_t) BT_ATTR_NAME_INDEX); - goto error; - } - - field_name = bt_value_string_get(attr_field_name_obj); - - if (!strcmp(field_name, name)) { - break; - } - - value_obj = NULL; - } - - return value_obj; - -error: - value_obj = NULL; - return value_obj; -} - -BT_HIDDEN -int bt_attributes_set_field_value(struct bt_value *attr_obj, - const char *name, struct bt_value *value_obj) -{ - int ret = 0; - struct bt_value *attr_field_obj = NULL; - - if (!attr_obj || !name || !value_obj) { - BT_LOGW("Invalid parameter: attributes object, name, or value object is NULL: " - "attr-value-addr=%p, name-addr=%p, value-addr=%p", - attr_obj, name, value_obj); - ret = -1; - goto end; - } - - attr_field_obj = bt_attributes_borrow_field_by_name(attr_obj, name); - if (attr_field_obj) { - ret = bt_value_array_set_element_by_index( - attr_field_obj, BT_ATTR_VALUE_INDEX, - value_obj); - attr_field_obj = NULL; - goto end; - } - - attr_field_obj = bt_value_array_create(); - if (!attr_field_obj) { - BT_LOGE_STR("Failed to create empty array value."); - ret = -1; - goto end; - } - - ret = bt_value_array_append_string_element(attr_field_obj, - name); - ret |= bt_value_array_append_element(attr_field_obj, - value_obj); - if (ret) { - BT_LOGE("Cannot append elements to array value: addr=%p", - attr_field_obj); - goto end; - } - - ret = bt_value_array_append_element(attr_obj, - attr_field_obj); - if (ret) { - BT_LOGE("Cannot append element to array value: " - "array-value-addr=%p, element-value-addr=%p", - attr_obj, attr_field_obj); - } - -end: - bt_object_put_ref(attr_field_obj); - return ret; -} - -BT_HIDDEN -struct bt_value *bt_attributes_borrow_field_value_by_name( - struct bt_value *attr_obj, const char *name) -{ - struct bt_value *value_obj = NULL; - struct bt_value *attr_field_obj = NULL; - - if (!attr_obj || !name) { - BT_LOGW("Invalid parameter: attributes object or name is NULL: " - "value-addr=%p, name-addr=%p", attr_obj, name); - goto end; - } - - attr_field_obj = bt_attributes_borrow_field_by_name(attr_obj, name); - if (!attr_field_obj) { - BT_LOGD("Cannot find attributes object's field by name: " - "value-addr=%p, name=\"%s\"", attr_obj, name); - goto end; - } - - value_obj = bt_value_array_borrow_element_by_index( - attr_field_obj, BT_ATTR_VALUE_INDEX); - if (!value_obj) { - BT_LOGE("Cannot get attribute array value's element by index: " - "value-addr=%p, index=%" PRIu64, attr_field_obj, - (uint64_t) BT_ATTR_VALUE_INDEX); - } - -end: - return value_obj; -} - -BT_HIDDEN -int bt_attributes_freeze(const struct bt_value *attr_obj) -{ - uint64_t i; - int64_t count; - int ret = 0; - - if (!attr_obj) { - BT_LOGW_STR("Invalid parameter: attributes object is NULL."); - ret = -1; - goto end; - } - - BT_LOGD("Freezing attributes object: value-addr=%p", attr_obj); - count = bt_value_array_get_size(attr_obj); - BT_ASSERT(count >= 0); - - /* - * We do not freeze the array value object itself here, since - * internal stuff could need to modify/add attributes. Each - * attribute is frozen one by one. - */ - for (i = 0; i < count; ++i) { - struct bt_value *obj = NULL; - - obj = bt_attributes_borrow_field_value( - (void *) attr_obj, i); - if (!obj) { - BT_LOGE("Cannot get attributes object's field value by index: " - "value-addr=%p, index=%" PRIu64, - attr_obj, i); - ret = -1; - goto end; - } - - bt_value_freeze(obj); - } - -end: - return ret; -} diff --git a/lib/trace-ir/clock-class.c b/lib/trace-ir/clock-class.c deleted file mode 100644 index 515cdb4f..00000000 --- a/lib/trace-ir/clock-class.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CLOCK-CLASS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_CLOCK_CLASS_HOT(_cc) \ - BT_ASSERT_PRE_HOT((_cc), "Clock class", ": %!+K", (_cc)) - -static -void destroy_clock_class(struct bt_object *obj) -{ - struct bt_clock_class *clock_class = (void *) obj; - - BT_LIB_LOGD("Destroying clock class: %!+K", clock_class); - - if (clock_class->name.str) { - g_string_free(clock_class->name.str, TRUE); - clock_class->name.str = NULL; - clock_class->name.value = NULL; - } - - if (clock_class->description.str) { - g_string_free(clock_class->description.str, TRUE); - clock_class->description.str = NULL; - clock_class->description.value = NULL; - } - - bt_object_pool_finalize(&clock_class->cs_pool); - g_free(clock_class); -} - -static -void free_clock_snapshot(struct bt_clock_snapshot *clock_snapshot, - struct bt_clock_class *clock_class) -{ - bt_clock_snapshot_destroy(clock_snapshot); -} - -static inline -void set_base_offset(struct bt_clock_class *clock_class) -{ - clock_class->base_offset.overflows = bt_util_get_base_offset_ns( - clock_class->offset_seconds, clock_class->offset_cycles, - clock_class->frequency, &clock_class->base_offset.value_ns); -} - -struct bt_clock_class *bt_clock_class_create(bt_self_component *self_comp) -{ - int ret; - struct bt_clock_class *clock_class = NULL; - - BT_ASSERT_PRE_NON_NULL(self_comp, "Self component"); - BT_LOGD_STR("Creating default clock class object"); - - clock_class = g_new0(struct bt_clock_class, 1); - if (!clock_class) { - BT_LOGE_STR("Failed to allocate one clock class."); - goto error; - } - - bt_object_init_shared(&clock_class->base, destroy_clock_class); - clock_class->name.str = g_string_new(NULL); - if (!clock_class->name.str) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - clock_class->description.str = g_string_new(NULL); - if (!clock_class->description.str) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - clock_class->frequency = UINT64_C(1000000000); - clock_class->origin_is_unix_epoch = BT_TRUE; - set_base_offset(clock_class); - ret = bt_object_pool_initialize(&clock_class->cs_pool, - (bt_object_pool_new_object_func) bt_clock_snapshot_new, - (bt_object_pool_destroy_object_func) - free_clock_snapshot, - clock_class); - if (ret) { - BT_LOGE("Failed to initialize clock snapshot pool: ret=%d", - ret); - goto error; - } - - BT_LIB_LOGD("Created clock class object: %!+K", clock_class); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(clock_class); - -end: - return clock_class; -} - -const char *bt_clock_class_get_name(const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return clock_class->name.value; -} - -enum bt_clock_class_status bt_clock_class_set_name( - struct bt_clock_class *clock_class, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - g_string_assign(clock_class->name.str, name); - clock_class->name.value = clock_class->name.str->str; - BT_LIB_LOGV("Set clock class's name: %!+K", clock_class); - return BT_CLOCK_CLASS_STATUS_OK; -} - -const char *bt_clock_class_get_description( - const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return clock_class->description.value; -} - -enum bt_clock_class_status bt_clock_class_set_description( - struct bt_clock_class *clock_class, const char *descr) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_NON_NULL(descr, "Description"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - g_string_assign(clock_class->description.str, descr); - clock_class->description.value = clock_class->description.str->str; - BT_LIB_LOGV("Set clock class's description: %!+K", - clock_class); - return BT_CLOCK_CLASS_STATUS_OK; -} - -uint64_t bt_clock_class_get_frequency(const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return clock_class->frequency; -} - -void bt_clock_class_set_frequency(struct bt_clock_class *clock_class, - uint64_t frequency) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - BT_ASSERT_PRE(frequency != UINT64_C(-1) && frequency != 0, - "Invalid frequency: %![cc-]+K, new-freq=%" PRIu64, - clock_class, frequency); - BT_ASSERT_PRE(clock_class->offset_cycles < frequency, - "Offset (cycles) is greater than clock class's frequency: " - "%![cc-]+K, new-freq=%" PRIu64, clock_class, frequency); - clock_class->frequency = frequency; - set_base_offset(clock_class); - BT_LIB_LOGV("Set clock class's frequency: %!+K", clock_class); -} - -uint64_t bt_clock_class_get_precision(const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return clock_class->precision; -} - -void bt_clock_class_set_precision(struct bt_clock_class *clock_class, - uint64_t precision) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - BT_ASSERT_PRE(precision != UINT64_C(-1), - "Invalid precision: %![cc-]+K, new-precision=%" PRIu64, - clock_class, precision); - clock_class->precision = precision; - BT_LIB_LOGV("Set clock class's precision: %!+K", clock_class); -} - -void bt_clock_class_get_offset(const struct bt_clock_class *clock_class, - int64_t *seconds, uint64_t *cycles) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_NON_NULL(seconds, "Seconds (output)"); - BT_ASSERT_PRE_NON_NULL(cycles, "Cycles (output)"); - *seconds = clock_class->offset_seconds; - *cycles = clock_class->offset_cycles; -} - -void bt_clock_class_set_offset(struct bt_clock_class *clock_class, - int64_t seconds, uint64_t cycles) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - BT_ASSERT_PRE(cycles < clock_class->frequency, - "Offset (cycles) is greater than clock class's frequency: " - "%![cc-]+K, new-offset-cycles=%" PRIu64, clock_class, cycles); - clock_class->offset_seconds = seconds; - clock_class->offset_cycles = cycles; - set_base_offset(clock_class); - BT_LIB_LOGV("Set clock class's offset: %!+K", clock_class); -} - -bt_bool bt_clock_class_origin_is_unix_epoch(const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return (bool) clock_class->origin_is_unix_epoch; -} - -void bt_clock_class_set_origin_is_unix_epoch(struct bt_clock_class *clock_class, - bt_bool origin_is_unix_epoch) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - clock_class->origin_is_unix_epoch = (bool) origin_is_unix_epoch; - BT_LIB_LOGV("Set clock class's origin is Unix epoch property: %!+K", - clock_class); -} - -bt_uuid bt_clock_class_get_uuid(const struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - return clock_class->uuid.value; -} - -void bt_clock_class_set_uuid(struct bt_clock_class *clock_class, - bt_uuid uuid) -{ - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_NON_NULL(uuid, "UUID"); - BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); - memcpy(clock_class->uuid.uuid, uuid, BABELTRACE_UUID_LEN); - clock_class->uuid.value = clock_class->uuid.uuid; - BT_LIB_LOGV("Set clock class's UUID: %!+K", clock_class); -} - -BT_HIDDEN -void _bt_clock_class_freeze(const struct bt_clock_class *clock_class) -{ - BT_ASSERT(clock_class); - - if (clock_class->frozen) { - return; - } - - BT_LIB_LOGD("Freezing clock class: %!+K", clock_class); - ((struct bt_clock_class *) clock_class)->frozen = 1; -} - -enum bt_clock_class_status bt_clock_class_cycles_to_ns_from_origin( - const struct bt_clock_class *clock_class, - uint64_t cycles, int64_t *ns) -{ - int ret; - - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_NON_NULL(ns, "Nanoseconds (output)"); - ret = bt_util_ns_from_origin_clock_class(clock_class, cycles, ns); - if (ret) { - ret = BT_CLOCK_CLASS_STATUS_OVERFLOW; - BT_LIB_LOGW("Cannot convert cycles to nanoseconds " - "from origin for given clock class: " - "value overflows the signed 64-bit integer range: " - "%![cc-]+K, cycles=%" PRIu64, - clock_class, cycles); - } - - return ret; -} - -void bt_clock_class_get_ref(const struct bt_clock_class *clock_class) -{ - bt_object_get_ref(clock_class); -} - -void bt_clock_class_put_ref(const struct bt_clock_class *clock_class) -{ - bt_object_put_ref(clock_class); -} diff --git a/lib/trace-ir/clock-snapshot.c b/lib/trace-ir/clock-snapshot.c deleted file mode 100644 index 050f82ac..00000000 --- a/lib/trace-ir/clock-snapshot.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "CLOCK-SNAPSHOT" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot) -{ - BT_LIB_LOGD("Destroying clock snapshot: %!+k", clock_snapshot); - BT_OBJECT_PUT_REF_AND_RESET(clock_snapshot->clock_class); - g_free(clock_snapshot); -} - -BT_HIDDEN -struct bt_clock_snapshot *bt_clock_snapshot_new( - struct bt_clock_class *clock_class) -{ - struct bt_clock_snapshot *ret = NULL; - - BT_ASSERT(clock_class); - BT_LIB_LOGD("Creating clock snapshot object: %![cc-]+K=", - clock_class); - ret = g_new0(struct bt_clock_snapshot, 1); - if (!ret) { - BT_LOGE_STR("Failed to allocate one clock snapshot."); - goto end; - } - - bt_object_init_unique(&ret->base); - ret->clock_class = clock_class; - bt_object_get_no_null_check(clock_class); - bt_clock_class_freeze(clock_class); - BT_LIB_LOGD("Created clock snapshot object: %!+k", ret); - -end: - return ret; -} - -BT_HIDDEN -struct bt_clock_snapshot *bt_clock_snapshot_create( - struct bt_clock_class *clock_class) -{ - struct bt_clock_snapshot *clock_snapshot = NULL; - - BT_ASSERT(clock_class); - clock_snapshot = bt_object_pool_create_object(&clock_class->cs_pool); - if (!clock_snapshot) { - BT_LIB_LOGE("Cannot allocate one clock snapshot from clock class's clock snapshot pool: " - "%![cc-]+K", clock_class); - goto error; - } - - if (likely(!clock_snapshot->clock_class)) { - clock_snapshot->clock_class = clock_class; - bt_object_get_no_null_check(clock_class); - } - - goto end; - -error: - if (clock_snapshot) { - bt_clock_snapshot_recycle(clock_snapshot); - clock_snapshot = NULL; - } - -end: - return clock_snapshot; -} - -BT_HIDDEN -void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot) -{ - struct bt_clock_class *clock_class; - - BT_ASSERT(clock_snapshot); - BT_LIB_LOGD("Recycling clock snapshot: %!+k", clock_snapshot); - - /* - * Those are the important ordered steps: - * - * 1. Reset the clock snapshot object, but do NOT put its clock - * class's reference. This clock class contains the pool to - * which we're about to recycle this clock snapshot object, - * so we must guarantee its existence thanks to this existing - * reference. - * - * 2. Move the clock class reference to our `clock_class` - * variable so that we can set the clock snapshot's clock - * class member to NULL before recycling it. We CANNOT do - * this after we put the clock class reference because this - * bt_object_put_ref() could destroy the clock class, also - * destroying its clock snapshot pool, thus also destroying - * our clock snapshot object (this would result in an invalid - * write access). - * - * 3. Recycle the clock snapshot object. - * - * 4. Put our clock class reference. - */ - bt_clock_snapshot_reset(clock_snapshot); - clock_class = clock_snapshot->clock_class; - BT_ASSERT(clock_class); - clock_snapshot->clock_class = NULL; - bt_object_pool_recycle_object(&clock_class->cs_pool, clock_snapshot); - bt_object_put_ref(clock_class); -} - -uint64_t bt_clock_snapshot_get_value( - const struct bt_clock_snapshot *clock_snapshot) -{ - BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); - BT_ASSERT_PRE(clock_snapshot->is_set, - "Clock snapshot is not set: %!+k", clock_snapshot); - return clock_snapshot->value_cycles; -} - -enum bt_clock_snapshot_status bt_clock_snapshot_get_ns_from_origin( - const struct bt_clock_snapshot *clock_snapshot, - int64_t *ret_value_ns) -{ - int ret = BT_CLOCK_SNAPSHOT_STATUS_OK; - - BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); - BT_ASSERT_PRE_NON_NULL(ret_value_ns, "Value (ns) (output)"); - BT_ASSERT_PRE(clock_snapshot->is_set, - "Clock snapshot is not set: %!+k", clock_snapshot); - - if (clock_snapshot->ns_from_origin_overflows) { - BT_LIB_LOGD("Clock snapshot, once converted to nanoseconds from origin, " - "overflows the signed 64-bit integer range: " - "%![cs-]+k", clock_snapshot); - ret = BT_CLOCK_SNAPSHOT_STATUS_OVERFLOW; - goto end; - } - - *ret_value_ns = clock_snapshot->ns_from_origin; - -end: - return ret; -} - -const struct bt_clock_class *bt_clock_snapshot_borrow_clock_class_const( - const struct bt_clock_snapshot *clock_snapshot) -{ - BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); - return clock_snapshot->clock_class; -} diff --git a/lib/trace-ir/event-class.c b/lib/trace-ir/event-class.c deleted file mode 100644 index db3911a0..00000000 --- a/lib/trace-ir/event-class.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "EVENT-CLASS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_EVENT_CLASS_HOT(_ec) \ - BT_ASSERT_PRE_HOT(((const struct bt_event_class *) (_ec)), \ - "Event class", ": %!+E", (_ec)) - -static -void destroy_event_class(struct bt_object *obj) -{ - struct bt_event_class *event_class = (void *) obj; - - BT_LIB_LOGD("Destroying event class: %!+E", event_class); - - if (event_class->name.str) { - g_string_free(event_class->name.str, TRUE); - event_class->name.str = NULL; - } - - if (event_class->emf_uri.str) { - g_string_free(event_class->emf_uri.str, TRUE); - event_class->emf_uri.str = NULL; - } - - BT_LOGD_STR("Putting context field class."); - BT_OBJECT_PUT_REF_AND_RESET(event_class->specific_context_fc); - BT_LOGD_STR("Putting payload field class."); - BT_OBJECT_PUT_REF_AND_RESET(event_class->payload_fc); - bt_object_pool_finalize(&event_class->event_pool); - g_free(obj); -} - -static -void free_event(struct bt_event *event, - struct bt_event_class *event_class) -{ - bt_event_destroy(event); -} - -BT_ASSERT_PRE_FUNC -static -bool event_class_id_is_unique(const struct bt_stream_class *stream_class, - uint64_t id) -{ - uint64_t i; - bool is_unique = true; - - for (i = 0; i < stream_class->event_classes->len; i++) { - const struct bt_event_class *ec = - stream_class->event_classes->pdata[i]; - - if (ec->id == id) { - is_unique = false; - goto end; - } - } - -end: - return is_unique; -} - -static -struct bt_event_class *create_event_class_with_id( - struct bt_stream_class *stream_class, uint64_t id) -{ - int ret; - struct bt_event_class *event_class; - - BT_ASSERT(stream_class); - BT_ASSERT_PRE(event_class_id_is_unique(stream_class, id), - "Duplicate event class ID: %![sc-]+S, id=%" PRIu64, - stream_class, id); - BT_LIB_LOGD("Creating event class object: %![sc-]+S, id=%" PRIu64, - stream_class, id); - event_class = g_new0(struct bt_event_class, 1); - if (!event_class) { - BT_LOGE_STR("Failed to allocate one event class."); - goto error; - } - - bt_object_init_shared_with_parent(&event_class->base, - destroy_event_class); - event_class->id = id; - bt_property_uint_init(&event_class->log_level, - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, 0); - event_class->name.str = g_string_new(NULL); - if (!event_class->name.str) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = -1; - goto end; - } - - event_class->emf_uri.str = g_string_new(NULL); - if (!event_class->emf_uri.str) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = -1; - goto end; - } - - ret = bt_object_pool_initialize(&event_class->event_pool, - (bt_object_pool_new_object_func) bt_event_new, - (bt_object_pool_destroy_object_func) free_event, - event_class); - if (ret) { - BT_LOGE("Failed to initialize event pool: ret=%d", - ret); - goto error; - } - - bt_object_set_parent(&event_class->base, &stream_class->base); - g_ptr_array_add(stream_class->event_classes, event_class); - bt_stream_class_freeze(stream_class); - BT_LIB_LOGD("Created event class object: %!+E", event_class); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(event_class); - -end: - return event_class; -} - -struct bt_event_class *bt_event_class_create( - struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE(stream_class->assigns_automatic_event_class_id, - "Stream class does not automatically assigns event class IDs: " - "%![sc-]+S", stream_class); - return create_event_class_with_id(stream_class, - (uint64_t) stream_class->event_classes->len); -} - -struct bt_event_class *bt_event_class_create_with_id( - struct bt_stream_class *stream_class, uint64_t id) -{ - BT_ASSERT_PRE(!stream_class->assigns_automatic_event_class_id, - "Stream class automatically assigns event class IDs: " - "%![sc-]+S", stream_class); - return create_event_class_with_id(stream_class, id); -} - -const char *bt_event_class_get_name(const struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->name.value; -} - -enum bt_event_class_status bt_event_class_set_name( - struct bt_event_class *event_class, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); - g_string_assign(event_class->name.str, name); - event_class->name.value = event_class->name.str->str; - BT_LIB_LOGV("Set event class's name: %!+E", event_class); - return BT_EVENT_CLASS_STATUS_OK; -} - -uint64_t bt_event_class_get_id(const struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->id; -} - -enum bt_property_availability bt_event_class_get_log_level( - const struct bt_event_class *event_class, - enum bt_event_class_log_level *log_level) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(log_level, "Log level (output)"); - *log_level = (enum bt_event_class_log_level) - event_class->log_level.value; - return event_class->log_level.base.avail; -} - -void bt_event_class_set_log_level( - struct bt_event_class *event_class, - enum bt_event_class_log_level log_level) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); - bt_property_uint_set(&event_class->log_level, - (uint64_t) log_level); - BT_LIB_LOGV("Set event class's log level: %!+E", event_class); -} - -const char *bt_event_class_get_emf_uri(const struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->emf_uri.value; -} - -enum bt_event_class_status bt_event_class_set_emf_uri( - struct bt_event_class *event_class, - const char *emf_uri) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(emf_uri, "EMF URI"); - BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); - g_string_assign(event_class->emf_uri.str, emf_uri); - event_class->emf_uri.value = event_class->emf_uri.str->str; - BT_LIB_LOGV("Set event class's EMF URI: %!+E", event_class); - return BT_EVENT_CLASS_STATUS_OK; -} - -struct bt_stream_class *bt_event_class_borrow_stream_class( - struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return bt_event_class_borrow_stream_class_inline(event_class); -} - -const struct bt_stream_class * -bt_event_class_borrow_stream_class_const( - const struct bt_event_class *event_class) -{ - return bt_event_class_borrow_stream_class((void *) event_class); -} - -const struct bt_field_class * -bt_event_class_borrow_specific_context_field_class_const( - const struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->specific_context_fc; -} - -struct bt_field_class * -bt_event_class_borrow_specific_context_field_class( - struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->specific_context_fc; -} - -enum bt_event_class_status bt_event_class_set_specific_context_field_class( - struct bt_event_class *event_class, - struct bt_field_class *field_class) -{ - int ret; - struct bt_stream_class *stream_class; - struct bt_resolve_field_path_context resolve_ctx = { - .packet_context = NULL, - .event_common_context = NULL, - .event_specific_context = field_class, - .event_payload = NULL, - }; - - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); - BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); - BT_ASSERT_PRE(bt_field_class_get_type(field_class) == - BT_FIELD_CLASS_TYPE_STRUCTURE, - "Specific context field class is not a structure field class: " - "%!+F", field_class); - stream_class = bt_event_class_borrow_stream_class_inline( - event_class); - resolve_ctx.packet_context = stream_class->packet_context_fc; - resolve_ctx.event_common_context = - stream_class->event_common_context_fc; - - ret = bt_resolve_field_paths(field_class, &resolve_ctx); - if (ret) { - /* - * This is the only reason for which - * bt_resolve_field_paths() can fail: anything else - * would be because a precondition is not satisfied. - */ - ret = BT_EVENT_CLASS_STATUS_NOMEM; - goto end; - } - - bt_field_class_make_part_of_trace_class(field_class); - bt_object_put_ref(event_class->specific_context_fc); - event_class->specific_context_fc = field_class; - bt_object_get_no_null_check(event_class->specific_context_fc); - bt_field_class_freeze(field_class); - BT_LIB_LOGV("Set event class's specific context field class: %!+E", - event_class); - -end: - return ret; -} - -const struct bt_field_class *bt_event_class_borrow_payload_field_class_const( - const struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->payload_fc; -} - -struct bt_field_class *bt_event_class_borrow_payload_field_class( - struct bt_event_class *event_class) -{ - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - return event_class->payload_fc; -} - -enum bt_event_class_status bt_event_class_set_payload_field_class( - struct bt_event_class *event_class, - struct bt_field_class *field_class) -{ - int ret; - struct bt_stream_class *stream_class; - struct bt_resolve_field_path_context resolve_ctx = { - .packet_context = NULL, - .event_common_context = NULL, - .event_specific_context = NULL, - .event_payload = field_class, - }; - - BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); - BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); - BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); - BT_ASSERT_PRE(bt_field_class_get_type(field_class) == - BT_FIELD_CLASS_TYPE_STRUCTURE, - "Payload field class is not a structure field class: %!+F", - field_class); - stream_class = bt_event_class_borrow_stream_class_inline( - event_class); - resolve_ctx.packet_context = stream_class->packet_context_fc; - resolve_ctx.event_common_context = - stream_class->event_common_context_fc; - resolve_ctx.event_specific_context = event_class->specific_context_fc; - - ret = bt_resolve_field_paths(field_class, &resolve_ctx); - if (ret) { - /* - * This is the only reason for which - * bt_resolve_field_paths() can fail: anything else - * would be because a precondition is not satisfied. - */ - ret = BT_EVENT_CLASS_STATUS_NOMEM; - goto end; - } - - bt_field_class_make_part_of_trace_class(field_class); - bt_object_put_ref(event_class->payload_fc); - event_class->payload_fc = field_class; - bt_object_get_no_null_check(event_class->payload_fc); - bt_field_class_freeze(field_class); - BT_LIB_LOGV("Set event class's payload field class: %!+E", event_class); - -end: - return ret; -} - -BT_HIDDEN -void _bt_event_class_freeze(const struct bt_event_class *event_class) -{ - /* The field classes are already frozen */ - BT_ASSERT(event_class); - BT_LIB_LOGD("Freezing event class: %!+E", event_class); - ((struct bt_event_class *) event_class)->frozen = true; -} - -void bt_event_class_get_ref(const struct bt_event_class *event_class) -{ - bt_object_get_ref(event_class); -} - -void bt_event_class_put_ref(const struct bt_event_class *event_class) -{ - bt_object_put_ref(event_class); -} diff --git a/lib/trace-ir/event.c b/lib/trace-ir/event.c deleted file mode 100644 index 5f094856..00000000 --- a/lib/trace-ir/event.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "EVENT" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -BT_HIDDEN -void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen) -{ - BT_ASSERT(event); - BT_LIB_LOGD("Setting event's frozen state: %!+e, is-frozen=%d", - event, is_frozen); - - if (event->common_context_field) { - BT_LOGD_STR("Setting event's common context field's frozen state."); - bt_field_set_is_frozen( - event->common_context_field, is_frozen); - } - - if (event->specific_context_field) { - BT_LOGD_STR("Setting event's specific context field's frozen state."); - bt_field_set_is_frozen(event->specific_context_field, - is_frozen); - } - - if (event->payload_field) { - BT_LOGD_STR("Setting event's payload field's frozen state."); - bt_field_set_is_frozen(event->payload_field, - is_frozen); - } - - ((struct bt_event *) event)->frozen = is_frozen; - BT_LOGD_STR("Setting event's packet's frozen state."); - bt_packet_set_is_frozen(event->packet, is_frozen); -} - -BT_HIDDEN -struct bt_event *bt_event_new(struct bt_event_class *event_class) -{ - struct bt_event *event = NULL; - struct bt_stream_class *stream_class; - struct bt_field_class *fc; - - BT_ASSERT(event_class); - event = g_new0(struct bt_event, 1); - if (!event) { - BT_LOGE_STR("Failed to allocate one event."); - goto error; - } - - bt_object_init_unique(&event->base); - stream_class = bt_event_class_borrow_stream_class(event_class); - BT_ASSERT(stream_class); - fc = stream_class->event_common_context_fc; - if (fc) { - event->common_context_field = bt_field_create(fc); - if (!event->common_context_field) { - /* bt_field_create() logs errors */ - goto error; - } - } - - fc = event_class->specific_context_fc; - if (fc) { - event->specific_context_field = bt_field_create(fc); - if (!event->specific_context_field) { - /* bt_field_create() logs errors */ - goto error; - } - } - - fc = event_class->payload_fc; - if (fc) { - event->payload_field = bt_field_create(fc); - if (!event->payload_field) { - /* bt_field_create() logs errors */ - goto error; - } - } - - goto end; - -error: - if (event) { - bt_event_destroy(event); - event = NULL; - } - -end: - return event; -} - -struct bt_event_class *bt_event_borrow_class(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->class; -} - -const struct bt_event_class *bt_event_borrow_class_const( - const struct bt_event *event) -{ - return bt_event_borrow_class((void *) event); -} - -struct bt_stream *bt_event_borrow_stream(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->packet ? event->packet->stream : NULL; -} - -const struct bt_stream *bt_event_borrow_stream_const( - const struct bt_event *event) -{ - return bt_event_borrow_stream((void *) event); -} - -struct bt_field *bt_event_borrow_common_context_field(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->common_context_field; -} - -const struct bt_field *bt_event_borrow_common_context_field_const( - const struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->common_context_field; -} - -struct bt_field *bt_event_borrow_specific_context_field(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->specific_context_field; -} - -const struct bt_field *bt_event_borrow_specific_context_field_const( - const struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->specific_context_field; -} - -struct bt_field *bt_event_borrow_payload_field(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->payload_field; -} - -const struct bt_field *bt_event_borrow_payload_field_const( - const struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->payload_field; -} - -BT_HIDDEN -void bt_event_destroy(struct bt_event *event) -{ - BT_ASSERT(event); - BT_LIB_LOGD("Destroying event: %!+e", event); - - if (event->common_context_field) { - BT_LOGD_STR("Destroying event's stream event context field."); - bt_field_destroy(event->common_context_field); - event->common_context_field = NULL; - } - - if (event->specific_context_field) { - BT_LOGD_STR("Destroying event's context field."); - bt_field_destroy(event->specific_context_field); - event->specific_context_field = NULL; - } - - if (event->payload_field) { - BT_LOGD_STR("Destroying event's payload field."); - bt_field_destroy(event->payload_field); - event->payload_field = NULL; - } - - BT_LOGD_STR("Putting event's class."); - bt_object_put_ref(event->class); - BT_LOGD_STR("Putting event's packet."); - BT_OBJECT_PUT_REF_AND_RESET(event->packet); - g_free(event); -} - -struct bt_packet *bt_event_borrow_packet(struct bt_event *event) -{ - BT_ASSERT_PRE_NON_NULL(event, "Event"); - return event->packet; -} - -const struct bt_packet *bt_event_borrow_packet_const( - const struct bt_event *event) -{ - return bt_event_borrow_packet((void *) event); -} diff --git a/lib/trace-ir/field-class.c b/lib/trace-ir/field-class.c deleted file mode 100644 index c1f18058..00000000 --- a/lib/trace-ir/field-class.c +++ /dev/null @@ -1,1355 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "FIELD-CLASSES" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum bt_field_class_type bt_field_class_get_type( - const struct bt_field_class *fc) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - return fc->type; -} - -static -void init_field_class(struct bt_field_class *fc, enum bt_field_class_type type, - bt_object_release_func release_func) -{ - BT_ASSERT(fc); - BT_ASSERT(bt_field_class_has_known_type(fc)); - BT_ASSERT(release_func); - bt_object_init_shared(&fc->base, release_func); - fc->type = type; -} - -static -void init_integer_field_class(struct bt_field_class_integer *fc, - enum bt_field_class_type type, - bt_object_release_func release_func) -{ - init_field_class((void *) fc, type, release_func); - fc->range = 64; - fc->base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; -} - -static -void destroy_integer_field_class(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_LIB_LOGD("Destroying integer field class object: %!+F", obj); - g_free(obj); -} - -static inline -struct bt_field_class *create_integer_field_class(bt_trace_class *trace_class, - enum bt_field_class_type type) -{ - struct bt_field_class_integer *int_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD("Creating default integer field class object: type=%s", - bt_common_field_class_type_string(type)); - int_fc = g_new0(struct bt_field_class_integer, 1); - if (!int_fc) { - BT_LOGE_STR("Failed to allocate one integer field class."); - goto error; - } - - init_integer_field_class(int_fc, type, destroy_integer_field_class); - BT_LIB_LOGD("Created integer field class object: %!+F", int_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(int_fc); - -end: - return (void *) int_fc; -} - -struct bt_field_class *bt_field_class_unsigned_integer_create( - bt_trace_class *trace_class) -{ - return create_integer_field_class(trace_class, - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); -} - -struct bt_field_class *bt_field_class_signed_integer_create( - bt_trace_class *trace_class) -{ - return create_integer_field_class(trace_class, - BT_FIELD_CLASS_TYPE_SIGNED_INTEGER); -} - -uint64_t bt_field_class_integer_get_field_value_range( - const struct bt_field_class *fc) -{ - const struct bt_field_class_integer *int_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); - return int_fc->range; -} - -BT_ASSERT_PRE_FUNC -static -bool size_is_valid_for_enumeration_field_class(struct bt_field_class *fc, - uint64_t size) -{ - // TODO - return true; -} - -void bt_field_class_integer_set_field_value_range( - struct bt_field_class *fc, uint64_t size) -{ - struct bt_field_class_integer *int_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); - BT_ASSERT_PRE_FC_HOT(fc, "Field class"); - BT_ASSERT_PRE(size <= 64, - "Unsupported size for integer field class's field value range " - "(maximum is 64): size=%" PRIu64, size); - BT_ASSERT_PRE( - int_fc->common.type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || - int_fc->common.type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || - size_is_valid_for_enumeration_field_class(fc, size), - "Invalid field value range for enumeration field class: " - "at least one of the current mapping ranges contains values " - "which are outside this range: %!+F, size=%" PRIu64, fc, size); - int_fc->range = size; - BT_LIB_LOGV("Set integer field class's field value range: %!+F", fc); -} - -enum bt_field_class_integer_preferred_display_base -bt_field_class_integer_get_preferred_display_base(const struct bt_field_class *fc) -{ - const struct bt_field_class_integer *int_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); - return int_fc->base; -} - -void bt_field_class_integer_set_preferred_display_base( - struct bt_field_class *fc, - enum bt_field_class_integer_preferred_display_base base) -{ - struct bt_field_class_integer *int_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); - BT_ASSERT_PRE_FC_HOT(fc, "Field class"); - int_fc->base = base; - BT_LIB_LOGV("Set integer field class's preferred display base: %!+F", fc); -} - -static -void finalize_enumeration_field_class_mapping( - struct bt_field_class_enumeration_mapping *mapping) -{ - BT_ASSERT(mapping); - - if (mapping->label) { - g_string_free(mapping->label, TRUE); - } - - if (mapping->ranges) { - g_array_free(mapping->ranges, TRUE); - } -} - -static -void destroy_enumeration_field_class(struct bt_object *obj) -{ - struct bt_field_class_enumeration *fc = (void *) obj; - - BT_ASSERT(fc); - BT_LIB_LOGD("Destroying enumeration field class object: %!+F", fc); - - if (fc->mappings) { - uint64_t i; - - for (i = 0; i < fc->mappings->len; i++) { - finalize_enumeration_field_class_mapping( - BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, i)); - } - - g_array_free(fc->mappings, TRUE); - fc->mappings = NULL; - } - - if (fc->label_buf) { - g_ptr_array_free(fc->label_buf, TRUE); - fc->label_buf = NULL; - } - - g_free(fc); -} - -static -struct bt_field_class *create_enumeration_field_class( - bt_trace_class *trace_class, enum bt_field_class_type type) -{ - struct bt_field_class_enumeration *enum_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD("Creating default enumeration field class object: type=%s", - bt_common_field_class_type_string(type)); - enum_fc = g_new0(struct bt_field_class_enumeration, 1); - if (!enum_fc) { - BT_LOGE_STR("Failed to allocate one enumeration field class."); - goto error; - } - - init_integer_field_class((void *) enum_fc, type, - destroy_enumeration_field_class); - enum_fc->mappings = g_array_new(FALSE, TRUE, - sizeof(struct bt_field_class_enumeration_mapping)); - if (!enum_fc->mappings) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - enum_fc->label_buf = g_ptr_array_new(); - if (!enum_fc->label_buf) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - BT_LIB_LOGD("Created enumeration field class object: %!+F", enum_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(enum_fc); - -end: - return (void *) enum_fc; -} - -struct bt_field_class *bt_field_class_unsigned_enumeration_create( - bt_trace_class *trace_class) -{ - return create_enumeration_field_class(trace_class, - BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION); -} - -struct bt_field_class *bt_field_class_signed_enumeration_create( - bt_trace_class *trace_class) -{ - return create_enumeration_field_class(trace_class, - BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); -} - -uint64_t bt_field_class_enumeration_get_mapping_count( - const struct bt_field_class *fc) -{ - const struct bt_field_class_enumeration *enum_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_ENUM(fc, "Field class"); - return (uint64_t) enum_fc->mappings->len; -} - -const struct bt_field_class_unsigned_enumeration_mapping * -bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( - const struct bt_field_class *fc, uint64_t index) -{ - const struct bt_field_class_enumeration *enum_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_VALID_INDEX(index, enum_fc->mappings->len); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, - "Field class"); - return (const void *) BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, index); -} - -const struct bt_field_class_signed_enumeration_mapping * -bt_field_class_signed_enumeration_borrow_mapping_by_index_const( - const struct bt_field_class *fc, uint64_t index) -{ - const struct bt_field_class_enumeration *enum_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_VALID_INDEX(index, enum_fc->mappings->len); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, - "Field class"); - return (const void *) BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, index); -} - -const char *bt_field_class_enumeration_mapping_get_label( - const struct bt_field_class_enumeration_mapping *mapping) -{ - BT_ASSERT_PRE_NON_NULL(mapping, "Enumeration field class mapping"); - return mapping->label->str; -} - -uint64_t bt_field_class_enumeration_mapping_get_range_count( - const struct bt_field_class_enumeration_mapping *mapping) -{ - BT_ASSERT_PRE_NON_NULL(mapping, "Enumeration field class mapping"); - return (uint64_t) mapping->ranges->len; -} - -static inline -void get_enumeration_field_class_mapping_range_at_index( - const struct bt_field_class_enumeration_mapping *mapping, - uint64_t index, uint64_t *lower, uint64_t *upper) -{ - const struct bt_field_class_enumeration_mapping_range *range; - - BT_ASSERT_PRE_NON_NULL(mapping, "Ranges"); - BT_ASSERT_PRE_NON_NULL(lower, "Range's lower (output)"); - BT_ASSERT_PRE_NON_NULL(upper, "Range's upper (output)"); - BT_ASSERT_PRE_VALID_INDEX(index, mapping->ranges->len); - range = BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(mapping, index); - *lower = range->lower.u; - *upper = range->upper.u; -} - -void bt_field_class_unsigned_enumeration_mapping_get_range_by_index( - const struct bt_field_class_unsigned_enumeration_mapping *ranges, - uint64_t index, uint64_t *lower, uint64_t *upper) -{ - get_enumeration_field_class_mapping_range_at_index( - (const void *) ranges, index, lower, upper); -} - -void bt_field_class_signed_enumeration_mapping_get_range_by_index( - const struct bt_field_class_signed_enumeration_mapping *ranges, - uint64_t index, int64_t *lower, int64_t *upper) -{ - get_enumeration_field_class_mapping_range_at_index( - (const void *) ranges, index, - (uint64_t *) lower, (uint64_t *) upper); -} - -enum bt_field_class_status -bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( - const struct bt_field_class *fc, uint64_t value, - bt_field_class_enumeration_mapping_label_array *label_array, - uint64_t *count) -{ - const struct bt_field_class_enumeration *enum_fc = (const void *) fc; - uint64_t i; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); - BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, - "Field class"); - g_ptr_array_set_size(enum_fc->label_buf, 0); - - for (i = 0; i < enum_fc->mappings->len; i++) { - uint64_t j; - const struct bt_field_class_enumeration_mapping *mapping = - BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); - - for (j = 0; j < mapping->ranges->len; j++) { - const struct bt_field_class_enumeration_mapping_range *range = - BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX( - mapping, j); - - if (value >= range->lower.u && - value <= range->upper.u) { - g_ptr_array_add(enum_fc->label_buf, - mapping->label->str); - break; - } - } - } - - *label_array = (void *) enum_fc->label_buf->pdata; - *count = (uint64_t) enum_fc->label_buf->len; - return BT_FIELD_CLASS_STATUS_OK; -} - -enum bt_field_class_status -bt_field_class_signed_enumeration_get_mapping_labels_by_value( - const struct bt_field_class *fc, int64_t value, - bt_field_class_enumeration_mapping_label_array *label_array, - uint64_t *count) -{ - const struct bt_field_class_enumeration *enum_fc = (const void *) fc; - uint64_t i; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); - BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, - "Field class"); - g_ptr_array_set_size(enum_fc->label_buf, 0); - - for (i = 0; i < enum_fc->mappings->len; i++) { - uint64_t j; - const struct bt_field_class_enumeration_mapping *mapping = - BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); - - for (j = 0; j < mapping->ranges->len; j++) { - const struct bt_field_class_enumeration_mapping_range *range = - BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX( - mapping, j); - - if (value >= range->lower.i && - value <= range->upper.i) { - g_ptr_array_add(enum_fc->label_buf, - mapping->label->str); - break; - } - } - } - - *label_array = (void *) enum_fc->label_buf->pdata; - *count = (uint64_t) enum_fc->label_buf->len; - return BT_FIELD_CLASS_STATUS_OK; -} - -static inline -enum bt_field_class_status add_mapping_to_enumeration_field_class( - struct bt_field_class *fc, - const char *label, uint64_t lower, uint64_t upper) -{ - int ret = BT_FIELD_CLASS_STATUS_OK; - uint64_t i; - struct bt_field_class_enumeration *enum_fc = (void *) fc; - struct bt_field_class_enumeration_mapping *mapping = NULL; - struct bt_field_class_enumeration_mapping_range *range; - - BT_ASSERT(fc); - BT_ASSERT_PRE_NON_NULL(label, "Label"); - - /* Find existing mapping identified by this label */ - for (i = 0; i < enum_fc->mappings->len; i++) { - struct bt_field_class_enumeration_mapping *mapping_candidate = - BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); - - if (strcmp(mapping_candidate->label->str, label) == 0) { - mapping = mapping_candidate; - break; - } - } - - if (!mapping) { - /* Create new mapping for this label */ - g_array_set_size(enum_fc->mappings, enum_fc->mappings->len + 1); - mapping = BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, - enum_fc->mappings->len - 1); - mapping->ranges = g_array_new(FALSE, TRUE, - sizeof(struct bt_field_class_enumeration_mapping_range)); - if (!mapping->ranges) { - finalize_enumeration_field_class_mapping(mapping); - g_array_set_size(enum_fc->mappings, - enum_fc->mappings->len - 1); - ret = BT_FIELD_CLASS_STATUS_NOMEM; - goto end; - } - - mapping->label = g_string_new(label); - if (!mapping->label) { - finalize_enumeration_field_class_mapping(mapping); - g_array_set_size(enum_fc->mappings, - enum_fc->mappings->len - 1); - ret = BT_FIELD_CLASS_STATUS_NOMEM; - goto end; - } - } - - /* Add range */ - BT_ASSERT(mapping); - g_array_set_size(mapping->ranges, mapping->ranges->len + 1); - range = BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(mapping, - mapping->ranges->len - 1); - range->lower.u = lower; - range->upper.u = upper; - BT_LIB_LOGV("Added mapping to enumeration field class: " - "%![fc-]+F, label=\"%s\", lower-unsigned=%" PRIu64 ", " - "upper-unsigned=%" PRIu64, fc, label, lower, upper); - -end: - return ret; -} - -enum bt_field_class_status bt_field_class_unsigned_enumeration_map_range( - struct bt_field_class *fc, const char *label, - uint64_t range_lower, uint64_t range_upper) -{ - struct bt_field_class_enumeration *enum_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, - "Field class"); - BT_ASSERT_PRE(range_lower <= range_upper, - "Range's upper bound is less than lower bound: " - "upper=%" PRIu64 ", lower=%" PRIu64, - range_lower, range_upper); - BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned(enum_fc->common.range, - range_lower), - "Range's lower bound is outside the enumeration field class's value range: " - "%![fc-]+F, lower=%" PRIu64, fc, range_lower); - BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned(enum_fc->common.range, - range_upper), - "Range's upper bound is outside the enumeration field class's value range: " - "%![fc-]+F, upper=%" PRIu64, fc, range_upper); - return add_mapping_to_enumeration_field_class(fc, label, range_lower, - range_upper); -} - -enum bt_field_class_status bt_field_class_signed_enumeration_map_range( - struct bt_field_class *fc, const char *label, - int64_t range_lower, int64_t range_upper) -{ - struct bt_field_class_enumeration *enum_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, - "Field class"); - BT_ASSERT_PRE(range_lower <= range_upper, - "Range's upper bound is less than lower bound: " - "upper=%" PRId64 ", lower=%" PRId64, - range_lower, range_upper); - BT_ASSERT_PRE(bt_util_value_is_in_range_signed(enum_fc->common.range, - range_lower), - "Range's lower bound is outside the enumeration field class's value range: " - "%![fc-]+F, lower=%" PRId64, fc, range_lower); - BT_ASSERT_PRE(bt_util_value_is_in_range_signed(enum_fc->common.range, - range_upper), - "Range's upper bound is outside the enumeration field class's value range: " - "%![fc-]+F, upper=%" PRId64, fc, range_upper); - return add_mapping_to_enumeration_field_class(fc, label, range_lower, - range_upper); -} - -static -void destroy_real_field_class(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_LIB_LOGD("Destroying real field class object: %!+F", obj); - g_free(obj); -} - -struct bt_field_class *bt_field_class_real_create(bt_trace_class *trace_class) -{ - struct bt_field_class_real *real_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD_STR("Creating default real field class object."); - real_fc = g_new0(struct bt_field_class_real, 1); - if (!real_fc) { - BT_LOGE_STR("Failed to allocate one real field class."); - goto error; - } - - init_field_class((void *) real_fc, BT_FIELD_CLASS_TYPE_REAL, - destroy_real_field_class); - BT_LIB_LOGD("Created real field class object: %!+F", real_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(real_fc); - -end: - return (void *) real_fc; -} - -bt_bool bt_field_class_real_is_single_precision(const struct bt_field_class *fc) -{ - const struct bt_field_class_real *real_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_REAL, "Field class"); - return real_fc->is_single_precision; -} - -void bt_field_class_real_set_is_single_precision(struct bt_field_class *fc, - bt_bool is_single_precision) -{ - struct bt_field_class_real *real_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_REAL, "Field class"); - BT_ASSERT_PRE_FC_HOT(fc, "Field class"); - real_fc->is_single_precision = (bool) is_single_precision; - BT_LIB_LOGV("Set real field class's \"is single precision\" property: " - "%!+F", fc); -} - -static -int init_named_field_classes_container( - struct bt_field_class_named_field_class_container *fc, - enum bt_field_class_type type, - bt_object_release_func release_func) -{ - int ret = 0; - - init_field_class((void *) fc, type, release_func); - fc->named_fcs = g_array_new(FALSE, TRUE, - sizeof(struct bt_named_field_class)); - if (!fc->named_fcs) { - BT_LOGE_STR("Failed to allocate a GArray."); - ret = -1; - goto end; - } - - fc->name_to_index = g_hash_table_new(g_str_hash, g_str_equal); - if (!fc->name_to_index) { - BT_LOGE_STR("Failed to allocate a GHashTable."); - ret = -1; - goto end; - } - -end: - return ret; -} - -static -void finalize_named_field_class(struct bt_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - BT_LIB_LOGD("Finalizing named field class: " - "addr=%p, name=\"%s\", %![fc-]+F", - named_fc, named_fc->name ? named_fc->name->str : NULL, - named_fc->fc); - - if (named_fc->name) { - g_string_free(named_fc->name, TRUE); - } - - BT_LOGD_STR("Putting named field class's field class."); - BT_OBJECT_PUT_REF_AND_RESET(named_fc->fc); -} - -static -void finalize_named_field_classes_container( - struct bt_field_class_named_field_class_container *fc) -{ - uint64_t i; - - BT_ASSERT(fc); - - if (fc->named_fcs) { - for (i = 0; i < fc->named_fcs->len; i++) { - finalize_named_field_class( - &g_array_index(fc->named_fcs, - struct bt_named_field_class, i)); - } - - g_array_free(fc->named_fcs, TRUE); - } - - if (fc->name_to_index) { - g_hash_table_destroy(fc->name_to_index); - } -} - -static -void destroy_structure_field_class(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_LIB_LOGD("Destroying structure field class object: %!+F", obj); - finalize_named_field_classes_container((void *) obj); - g_free(obj); -} - -struct bt_field_class *bt_field_class_structure_create( - bt_trace_class *trace_class) -{ - int ret; - struct bt_field_class_structure *struct_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD_STR("Creating default structure field class object."); - struct_fc = g_new0(struct bt_field_class_structure, 1); - if (!struct_fc) { - BT_LOGE_STR("Failed to allocate one structure field class."); - goto error; - } - - ret = init_named_field_classes_container((void *) struct_fc, - BT_FIELD_CLASS_TYPE_STRUCTURE, destroy_structure_field_class); - if (ret) { - goto error; - } - - BT_LIB_LOGD("Created structure field class object: %!+F", struct_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(struct_fc); - -end: - return (void *) struct_fc; -} - -static -enum bt_field_class_status append_named_field_class_to_container_field_class( - struct bt_field_class_named_field_class_container *container_fc, - const char *name, struct bt_field_class *fc) -{ - int ret = BT_FIELD_CLASS_STATUS_OK; - struct bt_named_field_class *named_fc; - GString *name_str; - - BT_ASSERT(container_fc); - BT_ASSERT_PRE_FC_HOT(container_fc, "Field class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE(!bt_g_hash_table_contains(container_fc->name_to_index, - name), - "Duplicate member/option name in structure/variant field class: " - "%![container-fc-]+F, name=\"%s\"", container_fc, name); - name_str = g_string_new(name); - if (!name_str) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = BT_FIELD_CLASS_STATUS_NOMEM; - goto end; - } - - g_array_set_size(container_fc->named_fcs, - container_fc->named_fcs->len + 1); - named_fc = &g_array_index(container_fc->named_fcs, - struct bt_named_field_class, container_fc->named_fcs->len - 1); - named_fc->name = name_str; - named_fc->fc = fc; - bt_object_get_no_null_check(fc); - g_hash_table_insert(container_fc->name_to_index, named_fc->name->str, - GUINT_TO_POINTER(container_fc->named_fcs->len - 1)); - - /* - * Freeze the field class, but not the named field class (the - * user can still modify it, if possible, until the container - * itself is frozen). - */ - bt_field_class_freeze(fc); - -end: - return ret; -} - -enum bt_field_class_status bt_field_class_structure_append_member( - struct bt_field_class *fc, const char *name, - struct bt_field_class *member_fc) -{ - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return append_named_field_class_to_container_field_class((void *) fc, - name, member_fc); -} - -uint64_t bt_field_class_structure_get_member_count( - const struct bt_field_class *fc) -{ - struct bt_field_class_structure *struct_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return (uint64_t) struct_fc->common.named_fcs->len; -} - -static -struct bt_named_field_class * -borrow_named_field_class_from_container_field_class_at_index( - struct bt_field_class_named_field_class_container *fc, - uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT_PRE_VALID_INDEX(index, fc->named_fcs->len); - return BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, index); -} - -const struct bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_index_const( - const struct bt_field_class *fc, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return (const void *) - borrow_named_field_class_from_container_field_class_at_index( - (void *) fc, index); -} - -struct bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_index( - struct bt_field_class *fc, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return (void *) - borrow_named_field_class_from_container_field_class_at_index( - (void *) fc, index); -} - -static -struct bt_named_field_class * -borrow_named_field_class_from_container_field_class_by_name( - struct bt_field_class_named_field_class_container *fc, - const char *name) -{ - struct bt_named_field_class *named_fc = NULL; - gpointer orig_key; - gpointer value; - - BT_ASSERT(fc); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - if (!g_hash_table_lookup_extended(fc->name_to_index, name, &orig_key, - &value)) { - goto end; - } - - named_fc = BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, - GPOINTER_TO_UINT(value)); - -end: - return named_fc; -} - -const struct bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_name_const( - const struct bt_field_class *fc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return (const void *) - borrow_named_field_class_from_container_field_class_by_name( - (void *) fc, name); -} - -struct bt_field_class_structure_member * -bt_field_class_structure_borrow_member_by_name( - struct bt_field_class *fc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, - "Field class"); - return (void *) - borrow_named_field_class_from_container_field_class_by_name( - (void *) fc, name); -} - -const char *bt_field_class_structure_member_get_name( - const struct bt_field_class_structure_member *member) -{ - const struct bt_named_field_class *named_fc = (const void *) member; - - BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); - return named_fc->name->str; -} - -const struct bt_field_class * -bt_field_class_structure_member_borrow_field_class_const( - const struct bt_field_class_structure_member *member) -{ - const struct bt_named_field_class *named_fc = (const void *) member; - - BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); - return named_fc->fc; -} - -struct bt_field_class * -bt_field_class_structure_member_borrow_field_class( - struct bt_field_class_structure_member *member) -{ - struct bt_named_field_class *named_fc = (void *) member; - - BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); - return named_fc->fc; -} - -static -void destroy_variant_field_class(struct bt_object *obj) -{ - struct bt_field_class_variant *fc = (void *) obj; - - BT_ASSERT(fc); - BT_LIB_LOGD("Destroying variant field class object: %!+F", fc); - finalize_named_field_classes_container((void *) fc); - BT_LOGD_STR("Putting selector field path."); - BT_OBJECT_PUT_REF_AND_RESET(fc->selector_field_path); - BT_LOGD_STR("Putting selector field class."); - BT_OBJECT_PUT_REF_AND_RESET(fc->selector_fc); - g_free(fc); -} - -struct bt_field_class *bt_field_class_variant_create( - bt_trace_class *trace_class) -{ - int ret; - struct bt_field_class_variant *var_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD_STR("Creating default variant field class object."); - var_fc = g_new0(struct bt_field_class_variant, 1); - if (!var_fc) { - BT_LOGE_STR("Failed to allocate one variant field class."); - goto error; - } - - ret = init_named_field_classes_container((void *) var_fc, - BT_FIELD_CLASS_TYPE_VARIANT, destroy_variant_field_class); - if (ret) { - goto error; - } - - BT_LIB_LOGD("Created variant field class object: %!+F", var_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(var_fc); - -end: - return (void *) var_fc; -} - -enum bt_field_class_status bt_field_class_variant_set_selector_field_class( - struct bt_field_class *fc, - struct bt_field_class *selector_fc) -{ - struct bt_field_class_variant *var_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Variant field class"); - BT_ASSERT_PRE_NON_NULL(selector_fc, "Selector field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - BT_ASSERT_PRE_FC_IS_ENUM(selector_fc, "Selector field class"); - BT_ASSERT_PRE_FC_HOT(fc, "Variant field class"); - var_fc->selector_fc = selector_fc; - bt_object_get_no_null_check(selector_fc); - bt_field_class_freeze(selector_fc); - return BT_FIELD_CLASS_STATUS_OK; -} - -enum bt_field_class_status bt_field_class_variant_append_option( - struct bt_field_class *fc, - const char *name, struct bt_field_class *option_fc) -{ - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return append_named_field_class_to_container_field_class((void *) fc, - name, option_fc); -} - -const struct bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_name_const( - const struct bt_field_class *fc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return (const void *) - borrow_named_field_class_from_container_field_class_by_name( - (void *) fc, name); -} - -struct bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_name( - struct bt_field_class *fc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return (void *) - borrow_named_field_class_from_container_field_class_by_name( - (void *) fc, name); -} - -uint64_t bt_field_class_variant_get_option_count(const struct bt_field_class *fc) -{ - const struct bt_field_class_variant *var_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return (uint64_t) var_fc->common.named_fcs->len; -} - -const struct bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_index_const( - const struct bt_field_class *fc, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return (const void *) - borrow_named_field_class_from_container_field_class_at_index( - (void *) fc, index); -} - -struct bt_field_class_variant_option * -bt_field_class_variant_borrow_option_by_index( - struct bt_field_class *fc, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); - return (void *) - borrow_named_field_class_from_container_field_class_at_index( - (void *) fc, index); -} - -const char *bt_field_class_variant_option_get_name( - const struct bt_field_class_variant_option *option) -{ - const struct bt_named_field_class *named_fc = (const void *) option; - - BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); - return named_fc->name->str; -} - -const struct bt_field_class * -bt_field_class_variant_option_borrow_field_class_const( - const struct bt_field_class_variant_option *option) -{ - const struct bt_named_field_class *named_fc = (const void *) option; - - BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); - return named_fc->fc; -} - -struct bt_field_class * -bt_field_class_variant_option_borrow_field_class( - struct bt_field_class_variant_option *option) -{ - struct bt_named_field_class *named_fc = (void *) option; - - BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); - return named_fc->fc; -} - -const struct bt_field_path * -bt_field_class_variant_borrow_selector_field_path_const( - const struct bt_field_class *fc) -{ - const struct bt_field_class_variant *var_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, - "Field class"); - return var_fc->selector_field_path; -} - -static -void init_array_field_class(struct bt_field_class_array *fc, - enum bt_field_class_type type, bt_object_release_func release_func, - struct bt_field_class *element_fc) -{ - BT_ASSERT(element_fc); - init_field_class((void *) fc, type, release_func); - fc->element_fc = element_fc; - bt_object_get_no_null_check(element_fc); - bt_field_class_freeze(element_fc); -} - -static -void finalize_array_field_class(struct bt_field_class_array *array_fc) -{ - BT_ASSERT(array_fc); - BT_LOGD_STR("Putting element field class."); - BT_OBJECT_PUT_REF_AND_RESET(array_fc->element_fc); -} - -static -void destroy_static_array_field_class(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_LIB_LOGD("Destroying static array field class object: %!+F", obj); - finalize_array_field_class((void *) obj); - g_free(obj); -} - -struct bt_field_class * -bt_field_class_static_array_create(bt_trace_class *trace_class, - struct bt_field_class *element_fc, uint64_t length) -{ - struct bt_field_class_static_array *array_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_ASSERT_PRE_NON_NULL(element_fc, "Element field class"); - BT_LOGD_STR("Creating default static array field class object."); - array_fc = g_new0(struct bt_field_class_static_array, 1); - if (!array_fc) { - BT_LOGE_STR("Failed to allocate one static array field class."); - goto error; - } - - init_array_field_class((void *) array_fc, BT_FIELD_CLASS_TYPE_STATIC_ARRAY, - destroy_static_array_field_class, element_fc); - array_fc->length = length; - BT_LIB_LOGD("Created static array field class object: %!+F", array_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(array_fc); - -end: - return (void *) array_fc; -} - -const struct bt_field_class * -bt_field_class_array_borrow_element_field_class_const( - const struct bt_field_class *fc) -{ - const struct bt_field_class_array *array_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_ARRAY(fc, "Field class"); - return array_fc->element_fc; -} - -struct bt_field_class * -bt_field_class_array_borrow_element_field_class(struct bt_field_class *fc) -{ - struct bt_field_class_array *array_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_IS_ARRAY(fc, "Field class"); - return array_fc->element_fc; -} - -uint64_t bt_field_class_static_array_get_length(const struct bt_field_class *fc) -{ - const struct bt_field_class_static_array *array_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STATIC_ARRAY, - "Field class"); - return (uint64_t) array_fc->length; -} - -static -void destroy_dynamic_array_field_class(struct bt_object *obj) -{ - struct bt_field_class_dynamic_array *fc = (void *) obj; - - BT_ASSERT(fc); - BT_LIB_LOGD("Destroying dynamic array field class object: %!+F", fc); - finalize_array_field_class((void *) fc); - BT_LOGD_STR("Putting length field path."); - BT_OBJECT_PUT_REF_AND_RESET(fc->length_field_path); - BT_LOGD_STR("Putting length field class."); - BT_OBJECT_PUT_REF_AND_RESET(fc->length_fc); - g_free(fc); -} - -struct bt_field_class *bt_field_class_dynamic_array_create( - bt_trace_class *trace_class, - struct bt_field_class *element_fc) -{ - struct bt_field_class_dynamic_array *array_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_ASSERT_PRE_NON_NULL(element_fc, "Element field class"); - BT_LOGD_STR("Creating default dynamic array field class object."); - array_fc = g_new0(struct bt_field_class_dynamic_array, 1); - if (!array_fc) { - BT_LOGE_STR("Failed to allocate one dynamic array field class."); - goto error; - } - - init_array_field_class((void *) array_fc, - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, - destroy_dynamic_array_field_class, element_fc); - BT_LIB_LOGD("Created dynamic array field class object: %!+F", array_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(array_fc); - -end: - return (void *) array_fc; -} - -enum bt_field_class_status bt_field_class_dynamic_array_set_length_field_class( - struct bt_field_class *fc, - struct bt_field_class *length_fc) -{ - struct bt_field_class_dynamic_array *array_fc = (void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Dynamic array field class"); - BT_ASSERT_PRE_NON_NULL(length_fc, "Length field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, - "Field class"); - BT_ASSERT_PRE_FC_IS_UNSIGNED_INT(length_fc, "Length field class"); - BT_ASSERT_PRE_FC_HOT(fc, "Dynamic array field class"); - array_fc->length_fc = length_fc; - bt_object_get_no_null_check(length_fc); - bt_field_class_freeze(length_fc); - return BT_FIELD_CLASS_STATUS_OK; -} - -const struct bt_field_path * -bt_field_class_dynamic_array_borrow_length_field_path_const( - const struct bt_field_class *fc) -{ - const struct bt_field_class_dynamic_array *seq_fc = (const void *) fc; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, - "Field class"); - return seq_fc->length_field_path; -} - -static -void destroy_string_field_class(struct bt_object *obj) -{ - BT_ASSERT(obj); - BT_LIB_LOGD("Destroying string field class object: %!+F", obj); - g_free(obj); -} - -struct bt_field_class *bt_field_class_string_create(bt_trace_class *trace_class) -{ - struct bt_field_class_string *string_fc = NULL; - - BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); - BT_LOGD_STR("Creating default string field class object."); - string_fc = g_new0(struct bt_field_class_string, 1); - if (!string_fc) { - BT_LOGE_STR("Failed to allocate one string field class."); - goto error; - } - - init_field_class((void *) string_fc, BT_FIELD_CLASS_TYPE_STRING, - destroy_string_field_class); - BT_LIB_LOGD("Created string field class object: %!+F", string_fc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(string_fc); - -end: - return (void *) string_fc; -} - -BT_HIDDEN -void _bt_field_class_freeze(const struct bt_field_class *c_fc) -{ - struct bt_field_class *fc = (void *) c_fc; - - /* - * Element/member/option field classes are frozen when added to - * their owner. - */ - BT_ASSERT(fc); - fc->frozen = true; - - switch (fc->type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_field_class_named_field_class_container *container_fc = - (void *) fc; - uint64_t i; - - for (i = 0; i < container_fc->named_fcs->len; i++) { - struct bt_named_field_class *named_fc = - BT_FIELD_CLASS_NAMED_FC_AT_INDEX( - container_fc, i); - - bt_named_field_class_freeze(named_fc); - } - - break; - } - default: - break; - } -} - -BT_HIDDEN -void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - ((struct bt_named_field_class *) named_fc)->frozen = true; - bt_field_class_freeze(named_fc->fc); -} - -BT_HIDDEN -void _bt_field_class_make_part_of_trace_class(const struct bt_field_class *c_fc) -{ - struct bt_field_class *fc = (void *) c_fc; - - BT_ASSERT(fc); - BT_ASSERT_PRE(!fc->part_of_trace_class, - "Field class is already part of a trace: %!+F", fc); - fc->part_of_trace_class = true; - - switch (fc->type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_field_class_named_field_class_container *container_fc = - (void *) fc; - uint64_t i; - - for (i = 0; i < container_fc->named_fcs->len; i++) { - struct bt_named_field_class *named_fc = - BT_FIELD_CLASS_NAMED_FC_AT_INDEX( - container_fc, i); - - bt_field_class_make_part_of_trace_class(named_fc->fc); - } - - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - struct bt_field_class_array *array_fc = (void *) fc; - - bt_field_class_make_part_of_trace_class(array_fc->element_fc); - break; - } - default: - break; - } -} - -void bt_field_class_get_ref(const struct bt_field_class *field_class) -{ - bt_object_get_ref(field_class); -} - -void bt_field_class_put_ref(const struct bt_field_class *field_class) -{ - bt_object_put_ref(field_class); -} diff --git a/lib/trace-ir/field-path.c b/lib/trace-ir/field-path.c deleted file mode 100644 index 23b65a75..00000000 --- a/lib/trace-ir/field-path.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2016-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "FIELD-PATH" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void destroy_field_path(struct bt_object *obj) -{ - struct bt_field_path *field_path = (struct bt_field_path *) obj; - - BT_ASSERT(field_path); - BT_LIB_LOGD("Destroying field path: %!+P", field_path); - g_array_free(field_path->items, TRUE); - field_path->items = NULL; - g_free(field_path); -} - -BT_HIDDEN -struct bt_field_path *bt_field_path_create(void) -{ - struct bt_field_path *field_path = NULL; - - BT_LOGD_STR("Creating empty field path object."); - - field_path = g_new0(struct bt_field_path, 1); - if (!field_path) { - BT_LOGE_STR("Failed to allocate one field path."); - goto error; - } - - bt_object_init_shared(&field_path->base, destroy_field_path); - field_path->items = g_array_new(FALSE, FALSE, - sizeof(struct bt_field_path_item)); - if (!field_path->items) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - BT_LIB_LOGD("Created empty field path object: %!+P", field_path); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(field_path); - -end: - return field_path; -} - -enum bt_scope bt_field_path_get_root_scope( - const struct bt_field_path *field_path) -{ - BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); - return field_path->root; -} - -uint64_t bt_field_path_get_item_count(const struct bt_field_path *field_path) -{ - BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); - return (uint64_t) field_path->items->len; -} - -const struct bt_field_path_item *bt_field_path_borrow_item_by_index_const( - const struct bt_field_path *field_path, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); - BT_ASSERT_PRE_VALID_INDEX(index, field_path->items->len); - return bt_field_path_borrow_item_by_index_inline(field_path, index); -} - -enum bt_field_path_item_type bt_field_path_item_get_type( - const struct bt_field_path_item *field_path_item) -{ - BT_ASSERT_PRE_NON_NULL(field_path_item, "Field path item"); - return field_path_item->type; -} - -uint64_t bt_field_path_item_index_get_index( - const struct bt_field_path_item *field_path_item) -{ - BT_ASSERT_PRE_NON_NULL(field_path_item, "Field path item"); - BT_ASSERT_PRE(field_path_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX, - "Field path item is not an index field path item: " - "addr=%p, type=%s", field_path_item, - bt_field_path_item_type_string(field_path_item->type)); - return field_path_item->index; -} - -void bt_field_path_get_ref(const struct bt_field_path *field_path) -{ - bt_object_get_ref(field_path); -} - -void bt_field_path_put_ref(const struct bt_field_path *field_path) -{ - bt_object_put_ref(field_path); -} diff --git a/lib/trace-ir/field-wrapper.c b/lib/trace-ir/field-wrapper.c deleted file mode 100644 index 8d0fdc79..00000000 --- a/lib/trace-ir/field-wrapper.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "FIELD-WRAPPER" -#include - -#include -#include -#include -#include -#include - -BT_HIDDEN -struct bt_field_wrapper *bt_field_wrapper_new(void *data) -{ - struct bt_field_wrapper *field_wrapper = - g_new0(struct bt_field_wrapper, 1); - - BT_LOGD_STR("Creating empty field wrapper object."); - - if (!field_wrapper) { - BT_LOGE("Failed to allocate one field wrapper."); - goto end; - } - - bt_object_init_unique(&field_wrapper->base); - BT_LOGD("Created empty field wrapper object: addr=%p", - field_wrapper); - -end: - return field_wrapper; -} - -BT_HIDDEN -void bt_field_wrapper_destroy(struct bt_field_wrapper *field_wrapper) -{ - BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper); - - if (field_wrapper->field) { - BT_LOGD_STR("Destroying field."); - bt_field_destroy((void *) field_wrapper->field); - field_wrapper->field = NULL; - } - - BT_LOGD_STR("Putting stream class."); - g_free(field_wrapper); -} - -BT_HIDDEN -struct bt_field_wrapper *bt_field_wrapper_create( - struct bt_object_pool *pool, struct bt_field_class *fc) -{ - struct bt_field_wrapper *field_wrapper = NULL; - - BT_ASSERT(pool); - BT_ASSERT(fc); - field_wrapper = bt_object_pool_create_object(pool); - if (!field_wrapper) { - BT_LIB_LOGE("Cannot allocate one field wrapper from field wrapper pool: " - "%![pool-]+o", pool); - goto error; - } - - if (!field_wrapper->field) { - field_wrapper->field = (void *) bt_field_create(fc); - if (!field_wrapper->field) { - BT_LIB_LOGE("Cannot create field wrapper from field class: " - "%![fc-]+F", fc); - goto error; - } - - BT_LIB_LOGD("Created initial field wrapper object: " - "wrapper-addr=%p, %![field-]+f", field_wrapper, - field_wrapper->field); - } - - goto end; - -error: - if (field_wrapper) { - bt_field_wrapper_destroy(field_wrapper); - field_wrapper = NULL; - } - -end: - return field_wrapper; -} diff --git a/lib/trace-ir/field.c b/lib/trace-ir/field.c deleted file mode 100644 index 4cf15425..00000000 --- a/lib/trace-ir/field.c +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "FIELDS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -void reset_single_field(struct bt_field *field); - -static -void reset_array_field(struct bt_field *field); - -static -void reset_structure_field(struct bt_field *field); - -static -void reset_variant_field(struct bt_field *field); - -static -void set_single_field_is_frozen(struct bt_field *field, bool is_frozen); - -static -void set_array_field_is_frozen(struct bt_field *field, bool is_frozen); - -static -void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen); - -static -void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen); - -static -bool single_field_is_set(const struct bt_field *field); - -static -bool array_field_is_set(const struct bt_field *field); - -static -bool structure_field_is_set(const struct bt_field *field); - -static -bool variant_field_is_set(const struct bt_field *field); - -static -struct bt_field_methods integer_field_methods = { - .set_is_frozen = set_single_field_is_frozen, - .is_set = single_field_is_set, - .reset = reset_single_field, -}; - -static -struct bt_field_methods real_field_methods = { - .set_is_frozen = set_single_field_is_frozen, - .is_set = single_field_is_set, - .reset = reset_single_field, -}; - -static -struct bt_field_methods string_field_methods = { - .set_is_frozen = set_single_field_is_frozen, - .is_set = single_field_is_set, - .reset = reset_single_field, -}; - -static -struct bt_field_methods structure_field_methods = { - .set_is_frozen = set_structure_field_is_frozen, - .is_set = structure_field_is_set, - .reset = reset_structure_field, -}; - -static -struct bt_field_methods array_field_methods = { - .set_is_frozen = set_array_field_is_frozen, - .is_set = array_field_is_set, - .reset = reset_array_field, -}; - -static -struct bt_field_methods variant_field_methods = { - .set_is_frozen = set_variant_field_is_frozen, - .is_set = variant_field_is_set, - .reset = reset_variant_field, -}; - -static -struct bt_field *create_integer_field(struct bt_field_class *); - -static -struct bt_field *create_real_field(struct bt_field_class *); - -static -struct bt_field *create_string_field(struct bt_field_class *); - -static -struct bt_field *create_structure_field(struct bt_field_class *); - -static -struct bt_field *create_static_array_field(struct bt_field_class *); - -static -struct bt_field *create_dynamic_array_field(struct bt_field_class *); - -static -struct bt_field *create_variant_field(struct bt_field_class *); - -static -struct bt_field *(* const field_create_funcs[])(struct bt_field_class *) = { - [BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER] = create_integer_field, - [BT_FIELD_CLASS_TYPE_SIGNED_INTEGER] = create_integer_field, - [BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION] = create_integer_field, - [BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION] = create_integer_field, - [BT_FIELD_CLASS_TYPE_REAL] = create_real_field, - [BT_FIELD_CLASS_TYPE_STRING] = create_string_field, - [BT_FIELD_CLASS_TYPE_STRUCTURE] = create_structure_field, - [BT_FIELD_CLASS_TYPE_STATIC_ARRAY] = create_static_array_field, - [BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY] = create_dynamic_array_field, - [BT_FIELD_CLASS_TYPE_VARIANT] = create_variant_field, -}; - -static -void destroy_integer_field(struct bt_field *field); - -static -void destroy_real_field(struct bt_field *field); - -static -void destroy_string_field(struct bt_field *field); - -static -void destroy_structure_field(struct bt_field *field); - -static -void destroy_array_field(struct bt_field *field); - -static -void destroy_variant_field(struct bt_field *field); - -static -void (* const field_destroy_funcs[])(struct bt_field *) = { - [BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER] = destroy_integer_field, - [BT_FIELD_CLASS_TYPE_SIGNED_INTEGER] = destroy_integer_field, - [BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION] = destroy_integer_field, - [BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION] = destroy_integer_field, - [BT_FIELD_CLASS_TYPE_REAL] = destroy_real_field, - [BT_FIELD_CLASS_TYPE_STRING] = destroy_string_field, - [BT_FIELD_CLASS_TYPE_STRUCTURE] = destroy_structure_field, - [BT_FIELD_CLASS_TYPE_STATIC_ARRAY] = destroy_array_field, - [BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY] = destroy_array_field, - [BT_FIELD_CLASS_TYPE_VARIANT] = destroy_variant_field, -}; - -struct bt_field_class *bt_field_borrow_class(const struct bt_field *field) -{ - BT_ASSERT_PRE_NON_NULL(field, "Field"); - return field->class; -} - -const struct bt_field_class *bt_field_borrow_class_const( - const struct bt_field *field) -{ - BT_ASSERT_PRE_NON_NULL(field, "Field"); - return field->class; -} - -enum bt_field_class_type bt_field_get_class_type(const struct bt_field *field) -{ - BT_ASSERT_PRE_NON_NULL(field, "Field"); - return field->class->type; -} - -BT_HIDDEN -struct bt_field *bt_field_create(struct bt_field_class *fc) -{ - struct bt_field *field = NULL; - - BT_ASSERT_PRE_NON_NULL(fc, "Field class"); - BT_ASSERT(bt_field_class_has_known_type(fc)); - field = field_create_funcs[fc->type](fc); - if (!field) { - BT_LIB_LOGE("Cannot create field object from field class: " - "%![fc-]+F", fc); - goto end; - } - -end: - return field; -} - -static inline -void init_field(struct bt_field *field, struct bt_field_class *fc, - struct bt_field_methods *methods) -{ - BT_ASSERT(field); - BT_ASSERT(fc); - bt_object_init_unique(&field->base); - field->methods = methods; - field->class = fc; - bt_object_get_no_null_check(fc); -} - -static -struct bt_field *create_integer_field(struct bt_field_class *fc) -{ - struct bt_field_integer *int_field; - - BT_LIB_LOGD("Creating integer field object: %![fc-]+F", fc); - int_field = g_new0(struct bt_field_integer, 1); - if (!int_field) { - BT_LOGE_STR("Failed to allocate one integer field."); - goto end; - } - - init_field((void *) int_field, fc, &integer_field_methods); - BT_LIB_LOGD("Created integer field object: %!+f", int_field); - -end: - return (void *) int_field; -} - -static -struct bt_field *create_real_field(struct bt_field_class *fc) -{ - struct bt_field_real *real_field; - - BT_LIB_LOGD("Creating real field object: %![fc-]+F", fc); - real_field = g_new0(struct bt_field_real, 1); - if (!real_field) { - BT_LOGE_STR("Failed to allocate one real field."); - goto end; - } - - init_field((void *) real_field, fc, &real_field_methods); - BT_LIB_LOGD("Created real field object: %!+f", real_field); - -end: - return (void *) real_field; -} - -static -struct bt_field *create_string_field(struct bt_field_class *fc) -{ - struct bt_field_string *string_field; - - BT_LIB_LOGD("Creating string field object: %![fc-]+F", fc); - string_field = g_new0(struct bt_field_string, 1); - if (!string_field) { - BT_LOGE_STR("Failed to allocate one string field."); - goto end; - } - - init_field((void *) string_field, fc, &string_field_methods); - string_field->buf = g_array_sized_new(FALSE, FALSE, - sizeof(char), 1); - if (!string_field->buf) { - BT_LOGE_STR("Failed to allocate a GArray."); - BT_OBJECT_PUT_REF_AND_RESET(string_field); - goto end; - } - - g_array_index(string_field->buf, char, 0) = '\0'; - BT_LIB_LOGD("Created string field object: %!+f", string_field); - -end: - return (void *) string_field; -} - -static inline -int create_fields_from_named_field_classes( - struct bt_field_class_named_field_class_container *fc, - GPtrArray **fields) -{ - int ret = 0; - uint64_t i; - - *fields = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_field_destroy); - if (!*fields) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - ret = -1; - goto end; - } - - g_ptr_array_set_size(*fields, fc->named_fcs->len); - - for (i = 0; i < fc->named_fcs->len; i++) { - struct bt_field *field; - struct bt_named_field_class *named_fc = - BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, i); - - field = bt_field_create(named_fc->fc); - if (!field) { - BT_LIB_LOGE("Failed to create structure member or variant option field: " - "name=\"%s\", %![fc-]+F", - named_fc->name->str, named_fc->fc); - ret = -1; - goto end; - } - - g_ptr_array_index(*fields, i) = field; - } - -end: - return ret; -} - -static -struct bt_field *create_structure_field(struct bt_field_class *fc) -{ - struct bt_field_structure *struct_field; - - BT_LIB_LOGD("Creating structure field object: %![fc-]+F", fc); - struct_field = g_new0(struct bt_field_structure, 1); - if (!struct_field) { - BT_LOGE_STR("Failed to allocate one structure field."); - goto end; - } - - init_field((void *) struct_field, fc, &structure_field_methods); - - if (create_fields_from_named_field_classes((void *) fc, - &struct_field->fields)) { - BT_LIB_LOGE("Cannot create structure member fields: " - "%![fc-]+F", fc); - BT_OBJECT_PUT_REF_AND_RESET(struct_field); - goto end; - } - - BT_LIB_LOGD("Created structure field object: %!+f", struct_field); - -end: - return (void *) struct_field; -} - -static -struct bt_field *create_variant_field(struct bt_field_class *fc) -{ - struct bt_field_variant *var_field; - - BT_LIB_LOGD("Creating variant field object: %![fc-]+F", fc); - var_field = g_new0(struct bt_field_variant, 1); - if (!var_field) { - BT_LOGE_STR("Failed to allocate one variant field."); - goto end; - } - - init_field((void *) var_field, fc, &variant_field_methods); - - if (create_fields_from_named_field_classes((void *) fc, - &var_field->fields)) { - BT_LIB_LOGE("Cannot create variant member fields: " - "%![fc-]+F", fc); - BT_OBJECT_PUT_REF_AND_RESET(var_field); - goto end; - } - - BT_LIB_LOGD("Created variant field object: %!+f", var_field); - -end: - return (void *) var_field; -} - -static inline -int init_array_field_fields(struct bt_field_array *array_field) -{ - int ret = 0; - uint64_t i; - struct bt_field_class_array *array_fc; - - BT_ASSERT(array_field); - array_fc = (void *) array_field->common.class; - array_field->fields = g_ptr_array_sized_new(array_field->length); - if (!array_field->fields) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - ret = -1; - goto end; - } - - g_ptr_array_set_free_func(array_field->fields, - (GDestroyNotify) bt_field_destroy); - g_ptr_array_set_size(array_field->fields, array_field->length); - - for (i = 0; i < array_field->length; i++) { - array_field->fields->pdata[i] = bt_field_create( - array_fc->element_fc); - if (!array_field->fields->pdata[i]) { - BT_LIB_LOGE("Cannot create array field's element field: " - "index=%" PRIu64 ", %![fc-]+F", i, array_fc); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -static -struct bt_field *create_static_array_field(struct bt_field_class *fc) -{ - struct bt_field_class_static_array *array_fc = (void *) fc; - struct bt_field_array *array_field; - - BT_LIB_LOGD("Creating static array field object: %![fc-]+F", fc); - array_field = g_new0(struct bt_field_array, 1); - if (!array_field) { - BT_LOGE_STR("Failed to allocate one static array field."); - goto end; - } - - init_field((void *) array_field, fc, &array_field_methods); - array_field->length = array_fc->length; - - if (init_array_field_fields(array_field)) { - BT_LIB_LOGE("Cannot create static array fields: " - "%![fc-]+F", fc); - BT_OBJECT_PUT_REF_AND_RESET(array_field); - goto end; - } - - BT_LIB_LOGD("Created static array field object: %!+f", array_field); - -end: - return (void *) array_field; -} - -static -struct bt_field *create_dynamic_array_field(struct bt_field_class *fc) -{ - struct bt_field_array *array_field; - - BT_LIB_LOGD("Creating dynamic array field object: %![fc-]+F", fc); - array_field = g_new0(struct bt_field_array, 1); - if (!array_field) { - BT_LOGE_STR("Failed to allocate one dynamic array field."); - goto end; - } - - init_field((void *) array_field, fc, &array_field_methods); - - if (init_array_field_fields(array_field)) { - BT_LIB_LOGE("Cannot create dynamic array fields: " - "%![fc-]+F", fc); - BT_OBJECT_PUT_REF_AND_RESET(array_field); - goto end; - } - - BT_LIB_LOGD("Created dynamic array field object: %!+f", array_field); - -end: - return (void *) array_field; -} - -int64_t bt_field_signed_integer_get_value(const struct bt_field *field) -{ - const struct bt_field_integer *int_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(field, "Field"); - return int_field->value.i; -} - -void bt_field_signed_integer_set_value(struct bt_field *field, int64_t value) -{ - struct bt_field_integer *int_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(field, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE(bt_util_value_is_in_range_signed( - ((struct bt_field_class_integer *) field->class)->range, value), - "Value is out of bounds: value=%" PRId64 ", %![field-]+f, " - "%![fc-]+F", value, field, field->class); - int_field->value.i = value; - bt_field_set_single(field, true); -} - -uint64_t bt_field_unsigned_integer_get_value(const struct bt_field *field) -{ - const struct bt_field_integer *int_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(field, "Field"); - return int_field->value.u; -} - -void bt_field_unsigned_integer_set_value(struct bt_field *field, uint64_t value) -{ - struct bt_field_integer *int_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(field, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned( - ((struct bt_field_class_integer *) field->class)->range, value), - "Value is out of bounds: value=%" PRIu64 ", %![field-]+f, " - "%![fc-]+F", value, field, field->class); - int_field->value.u = value; - bt_field_set_single(field, true); -} - -double bt_field_real_get_value(const struct bt_field *field) -{ - const struct bt_field_real *real_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_REAL, "Field"); - return real_field->value; -} - -void bt_field_real_set_value(struct bt_field *field, double value) -{ - struct bt_field_real *real_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_REAL, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE( - !((struct bt_field_class_real *) field->class)->is_single_precision || - (double) (float) value == value, - "Invalid value for a single-precision real number: value=%f, " - "%![fc-]+F", value, field->class); - real_field->value = value; - bt_field_set_single(field, true); -} - -enum bt_field_status bt_field_unsigned_enumeration_get_mapping_labels( - const struct bt_field *field, - bt_field_class_enumeration_mapping_label_array *label_array, - uint64_t *count) -{ - const struct bt_field_integer *int_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); - BT_ASSERT_PRE_NON_NULL(label_array, "Count (output)"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, "Field"); - return (int) - bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( - field->class, int_field->value.u, label_array, count); -} - -enum bt_field_status bt_field_signed_enumeration_get_mapping_labels( - const struct bt_field *field, - bt_field_class_enumeration_mapping_label_array *label_array, - uint64_t *count) -{ - const struct bt_field_integer *int_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); - BT_ASSERT_PRE_NON_NULL(label_array, "Count (output)"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, "Field"); - return (int) - bt_field_class_signed_enumeration_get_mapping_labels_by_value( - field->class, int_field->value.i, label_array, count); -} - -const char *bt_field_string_get_value(const struct bt_field *field) -{ - const struct bt_field_string *string_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, - "Field"); - return (const char *) string_field->buf->data; -} - -uint64_t bt_field_string_get_length(const struct bt_field *field) -{ - const struct bt_field_string *string_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, - "Field"); - return string_field->length; -} - -static inline -void clear_string_field(struct bt_field *field) -{ - struct bt_field_string *string_field = (void *) field; - - BT_ASSERT(field); - string_field->length = 0; - bt_field_set_single(field, true); -} - -enum bt_field_status bt_field_string_set_value(struct bt_field *field, - const char *value) -{ - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_NON_NULL(value, "Value"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, - "Field"); - clear_string_field(field); - return bt_field_string_append_with_length(field, value, - (uint64_t) strlen(value)); -} - -enum bt_field_status bt_field_string_append(struct bt_field *field, const char *value) -{ - return bt_field_string_append_with_length(field, - value, (uint64_t) strlen(value)); -} - -enum bt_field_status bt_field_string_append_with_length(struct bt_field *field, - const char *value, uint64_t length) -{ - struct bt_field_string *string_field = (void *) field; - char *data; - uint64_t new_length; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_NON_NULL(value, "Value"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_STRING, "Field"); - - /* Make sure no null bytes are appended */ - BT_ASSERT_PRE(memchr(value, '\0', length) == NULL, - "String value to append contains a null character: " - "partial-value=\"%.32s\", length=%" PRIu64, value, length); - - new_length = length + string_field->length; - - if (unlikely(new_length + 1 > string_field->buf->len)) { - g_array_set_size(string_field->buf, new_length + 1); - } - - data = string_field->buf->data; - memcpy(data + string_field->length, value, length); - ((char *) string_field->buf->data)[new_length] = '\0'; - string_field->length = new_length; - bt_field_set_single(field, true); - return BT_FIELD_STATUS_OK; -} - -enum bt_field_status bt_field_string_clear(struct bt_field *field) -{ - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_STRING, "Field"); - clear_string_field(field); - return BT_FIELD_STATUS_OK; -} - -uint64_t bt_field_array_get_length(const struct bt_field *field) -{ - const struct bt_field_array *array_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_ARRAY(field, "Field"); - return array_field->length; -} - -enum bt_field_status bt_field_dynamic_array_set_length(struct bt_field *field, - uint64_t length) -{ - int ret = BT_FIELD_STATUS_OK; - struct bt_field_array *array_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - - if (unlikely(length > array_field->fields->len)) { - /* Make more room */ - struct bt_field_class_array *array_fc; - uint64_t cur_len = array_field->fields->len; - uint64_t i; - - g_ptr_array_set_size(array_field->fields, length); - array_fc = (void *) field->class; - - for (i = cur_len; i < array_field->fields->len; i++) { - struct bt_field *elem_field = bt_field_create( - array_fc->element_fc); - - if (!elem_field) { - BT_LIB_LOGE("Cannot create element field for " - "dynamic array field: " - "index=%" PRIu64 ", " - "%![array-field-]+f", i, field); - ret = BT_FIELD_STATUS_NOMEM; - goto end; - } - - BT_ASSERT(!array_field->fields->pdata[i]); - array_field->fields->pdata[i] = elem_field; - } - } - - array_field->length = length; - -end: - return ret; -} - -static inline -struct bt_field *borrow_array_field_element_field_by_index( - struct bt_field *field, uint64_t index) -{ - struct bt_field_array *array_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_IS_ARRAY(field, "Field"); - BT_ASSERT_PRE_VALID_INDEX(index, array_field->length); - return array_field->fields->pdata[index]; -} - -struct bt_field *bt_field_array_borrow_element_field_by_index( - struct bt_field *field, uint64_t index) -{ - return borrow_array_field_element_field_by_index(field, index); -} - -const struct bt_field * -bt_field_array_borrow_element_field_by_index_const( - const struct bt_field *field, uint64_t index) -{ - return borrow_array_field_element_field_by_index((void *) field, index); -} - -static inline -struct bt_field *borrow_structure_field_member_field_by_index( - struct bt_field *field, uint64_t index) -{ - struct bt_field_structure *struct_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_STRUCTURE, "Field"); - BT_ASSERT_PRE_VALID_INDEX(index, struct_field->fields->len); - return struct_field->fields->pdata[index]; -} - -struct bt_field *bt_field_structure_borrow_member_field_by_index( - struct bt_field *field, uint64_t index) -{ - return borrow_structure_field_member_field_by_index(field, - index); -} - -const struct bt_field * -bt_field_structure_borrow_member_field_by_index_const( - const struct bt_field *field, uint64_t index) -{ - return borrow_structure_field_member_field_by_index( - (void *) field, index); -} - -static inline -struct bt_field *borrow_structure_field_member_field_by_name( - struct bt_field *field, const char *name) -{ - struct bt_field *ret_field = NULL; - struct bt_field_class_structure *struct_fc; - struct bt_field_structure *struct_field = (void *) field; - gpointer orig_key; - gpointer index; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_NON_NULL(name, "Field name"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_STRUCTURE, "Field"); - struct_fc = (void *) field->class; - - if (!g_hash_table_lookup_extended(struct_fc->common.name_to_index, name, - &orig_key, &index)) { - goto end; - } - - ret_field = struct_field->fields->pdata[GPOINTER_TO_UINT(index)]; - BT_ASSERT(ret_field); - -end: - return ret_field; -} - -struct bt_field *bt_field_structure_borrow_member_field_by_name( - struct bt_field *field, const char *name) -{ - return borrow_structure_field_member_field_by_name(field, name); -} - -const struct bt_field *bt_field_structure_borrow_member_field_by_name_const( - const struct bt_field *field, const char *name) -{ - return borrow_structure_field_member_field_by_name( - (void *) field, name); -} - -static inline -struct bt_field *borrow_variant_field_selected_option_field( - struct bt_field *field) -{ - struct bt_field_variant *var_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_VARIANT, "Field"); - BT_ASSERT_PRE(var_field->selected_field, - "Variant field has no selected field: %!+f", field); - return var_field->selected_field; -} - -struct bt_field *bt_field_variant_borrow_selected_option_field( - struct bt_field *field) -{ - return borrow_variant_field_selected_option_field(field); -} - -const struct bt_field *bt_field_variant_borrow_selected_option_field_const( - const struct bt_field *field) -{ - return borrow_variant_field_selected_option_field((void *) field); -} - -enum bt_field_status bt_field_variant_select_option_field( - struct bt_field *field, uint64_t index) -{ - struct bt_field_variant *var_field = (void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_VARIANT, "Field"); - BT_ASSERT_PRE_FIELD_HOT(field, "Field"); - BT_ASSERT_PRE_VALID_INDEX(index, var_field->fields->len); - var_field->selected_field = var_field->fields->pdata[index]; - var_field->selected_index = index; - return BT_FIELD_STATUS_OK; -} - -uint64_t bt_field_variant_get_selected_option_field_index( - const struct bt_field *field) -{ - const struct bt_field_variant *var_field = (const void *) field; - - BT_ASSERT_PRE_NON_NULL(field, "Field"); - BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, - BT_FIELD_CLASS_TYPE_VARIANT, "Field"); - BT_ASSERT_PRE(var_field->selected_field, - "Variant field has no selected field: %!+f", field); - return var_field->selected_index; -} - -static inline -void bt_field_finalize(struct bt_field *field) -{ - BT_ASSERT(field); - BT_LOGD_STR("Putting field's class."); - BT_OBJECT_PUT_REF_AND_RESET(field->class); -} - -static -void destroy_integer_field(struct bt_field *field) -{ - BT_ASSERT(field); - BT_LIB_LOGD("Destroying integer field object: %!+f", field); - bt_field_finalize(field); - g_free(field); -} - -static -void destroy_real_field(struct bt_field *field) -{ - BT_ASSERT(field); - BT_LIB_LOGD("Destroying real field object: %!+f", field); - bt_field_finalize(field); - g_free(field); -} - -static -void destroy_structure_field(struct bt_field *field) -{ - struct bt_field_structure *struct_field = (void *) field; - - BT_ASSERT(field); - BT_LIB_LOGD("Destroying structure field object: %!+f", field); - bt_field_finalize(field); - - if (struct_field->fields) { - g_ptr_array_free(struct_field->fields, TRUE); - struct_field->fields = NULL; - } - - g_free(field); -} - -static -void destroy_variant_field(struct bt_field *field) -{ - struct bt_field_variant *var_field = (void *) field; - - BT_ASSERT(field); - BT_LIB_LOGD("Destroying variant field object: %!+f", field); - bt_field_finalize(field); - - if (var_field->fields) { - g_ptr_array_free(var_field->fields, TRUE); - var_field->fields = NULL; - } - - g_free(field); -} - -static -void destroy_array_field(struct bt_field *field) -{ - struct bt_field_array *array_field = (void *) field; - - BT_ASSERT(field); - BT_LIB_LOGD("Destroying array field object: %!+f", field); - bt_field_finalize(field); - - if (array_field->fields) { - g_ptr_array_free(array_field->fields, TRUE); - array_field->fields = NULL; - } - - g_free(field); -} - -static -void destroy_string_field(struct bt_field *field) -{ - struct bt_field_string *string_field = (void *) field; - - BT_ASSERT(field); - BT_LIB_LOGD("Destroying string field object: %!+f", field); - bt_field_finalize(field); - - if (string_field->buf) { - g_array_free(string_field->buf, TRUE); - string_field->buf = NULL; - } - - g_free(field); -} - -BT_HIDDEN -void bt_field_destroy(struct bt_field *field) -{ - BT_ASSERT(field); - BT_ASSERT(bt_field_class_has_known_type(field->class)); - field_destroy_funcs[field->class->type](field); -} - -static -void reset_single_field(struct bt_field *field) -{ - BT_ASSERT(field); - field->is_set = false; -} - -static -void reset_structure_field(struct bt_field *field) -{ - uint64_t i; - struct bt_field_structure *struct_field = (void *) field; - - BT_ASSERT(field); - - for (i = 0; i < struct_field->fields->len; i++) { - bt_field_reset(struct_field->fields->pdata[i]); - } -} - -static -void reset_variant_field(struct bt_field *field) -{ - uint64_t i; - struct bt_field_variant *var_field = (void *) field; - - BT_ASSERT(field); - - for (i = 0; i < var_field->fields->len; i++) { - bt_field_reset(var_field->fields->pdata[i]); - } -} - -static -void reset_array_field(struct bt_field *field) -{ - uint64_t i; - struct bt_field_array *array_field = (void *) field; - - BT_ASSERT(field); - - for (i = 0; i < array_field->fields->len; i++) { - bt_field_reset(array_field->fields->pdata[i]); - } -} - -static -void set_single_field_is_frozen(struct bt_field *field, bool is_frozen) -{ - field->frozen = is_frozen; -} - -static -void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen) -{ - uint64_t i; - struct bt_field_structure *struct_field = (void *) field; - - BT_LIB_LOGD("Setting structure field's frozen state: " - "%![field-]+f, is-frozen=%d", field, is_frozen); - - for (i = 0; i < struct_field->fields->len; i++) { - struct bt_field *member_field = struct_field->fields->pdata[i]; - - BT_LIB_LOGD("Setting structure field's member field's " - "frozen state: %![field-]+f, index=%" PRIu64, - member_field, i); - bt_field_set_is_frozen(member_field, is_frozen); - } - - set_single_field_is_frozen(field, is_frozen); -} - -static -void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen) -{ - uint64_t i; - struct bt_field_variant *var_field = (void *) field; - - BT_LIB_LOGD("Setting variant field's frozen state: " - "%![field-]+f, is-frozen=%d", field, is_frozen); - - for (i = 0; i < var_field->fields->len; i++) { - struct bt_field *option_field = var_field->fields->pdata[i]; - - BT_LIB_LOGD("Setting variant field's option field's " - "frozen state: %![field-]+f, index=%" PRIu64, - option_field, i); - bt_field_set_is_frozen(option_field, is_frozen); - } - - set_single_field_is_frozen(field, is_frozen); -} - -static -void set_array_field_is_frozen(struct bt_field *field, bool is_frozen) -{ - uint64_t i; - struct bt_field_array *array_field = (void *) field; - - BT_LIB_LOGD("Setting array field's frozen state: " - "%![field-]+f, is-frozen=%d", field, is_frozen); - - for (i = 0; i < array_field->fields->len; i++) { - struct bt_field *elem_field = array_field->fields->pdata[i]; - - BT_LIB_LOGD("Setting array field's element field's " - "frozen state: %![field-]+f, index=%" PRIu64, - elem_field, i); - bt_field_set_is_frozen(elem_field, is_frozen); - } - - set_single_field_is_frozen(field, is_frozen); -} - -BT_HIDDEN -void _bt_field_set_is_frozen(const struct bt_field *field, - bool is_frozen) -{ - BT_ASSERT(field); - BT_LIB_LOGD("Setting field object's frozen state: %!+f, is-frozen=%d", - field, is_frozen); - BT_ASSERT(field->methods->set_is_frozen); - field->methods->set_is_frozen((void *) field, is_frozen); -} - -static -bool single_field_is_set(const struct bt_field *field) -{ - BT_ASSERT(field); - return field->is_set; -} - -static -bool structure_field_is_set(const struct bt_field *field) -{ - bool is_set = true; - uint64_t i; - const struct bt_field_structure *struct_field = (const void *) field; - - BT_ASSERT(field); - - for (i = 0; i < struct_field->fields->len; i++) { - is_set = bt_field_is_set(struct_field->fields->pdata[i]); - if (!is_set) { - goto end; - } - } - -end: - return is_set; -} - -static -bool variant_field_is_set(const struct bt_field *field) -{ - const struct bt_field_variant *var_field = (const void *) field; - bool is_set = false; - - BT_ASSERT(field); - - if (var_field->selected_field) { - is_set = bt_field_is_set(var_field->selected_field); - } - - return is_set; -} - -static -bool array_field_is_set(const struct bt_field *field) -{ - bool is_set = true; - uint64_t i; - const struct bt_field_array *array_field = (const void *) field; - - BT_ASSERT(field); - - for (i = 0; i < array_field->length; i++) { - is_set = bt_field_is_set(array_field->fields->pdata[i]); - if (!is_set) { - goto end; - } - } - -end: - return is_set; -} diff --git a/lib/trace-ir/packet-context-field.c b/lib/trace-ir/packet-context-field.c deleted file mode 100644 index dbebab8e..00000000 --- a/lib/trace-ir/packet-context-field.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PACKET-CONTEXT-FIELD" -#include - -#include -#include -#include -#include -#include -#include - -struct bt_field *bt_packet_context_field_borrow_field( - struct bt_packet_context_field *context_field) -{ - struct bt_field_wrapper *field_wrapper = (void *) context_field; - - BT_ASSERT_PRE_NON_NULL(field_wrapper, "Packet context field"); - return (void *) field_wrapper->field; -} - -void bt_packet_context_field_release( - struct bt_packet_context_field *context_field) -{ - struct bt_field_wrapper *field_wrapper = (void *) context_field; - - BT_ASSERT_PRE_NON_NULL(field_wrapper, "Packet context field"); - - /* - * Do not recycle because the pool could be destroyed at this - * point. This function is only called when there's an error - * anyway because the goal of a packet context field wrapper is - * to eventually move it to a packet with - * bt_packet_move_context() after creating it. - */ - bt_field_wrapper_destroy(field_wrapper); -} - -struct bt_packet_context_field *bt_packet_context_field_create( - struct bt_stream_class *stream_class) -{ - struct bt_field_wrapper *field_wrapper; - - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE(stream_class->frozen, - "Stream class is not part of a trace class: %!+S", - stream_class); - BT_ASSERT_PRE(stream_class->packet_context_fc, - "Stream class has no packet context field class: %!+S", - stream_class); - field_wrapper = bt_field_wrapper_create( - &stream_class->packet_context_field_pool, - (void *) stream_class->packet_context_fc); - if (!field_wrapper) { - BT_LIB_LOGE("Cannot allocate one packet context field from stream class: " - "%![sc-]+S", stream_class); - goto error; - } - - BT_ASSERT(field_wrapper->field); - bt_stream_class_freeze(stream_class); - goto end; - -error: - if (field_wrapper) { - bt_field_wrapper_destroy(field_wrapper); - field_wrapper = NULL; - } - -end: - return (void *) field_wrapper; -} diff --git a/lib/trace-ir/packet.c b/lib/trace-ir/packet.c deleted file mode 100644 index e005ceaa..00000000 --- a/lib/trace-ir/packet.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2016-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PACKET" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_PACKET_HOT(_packet) \ - BT_ASSERT_PRE_HOT((_packet), "Packet", ": %!+a", (_packet)) - -struct bt_stream *bt_packet_borrow_stream(struct bt_packet *packet) -{ - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - return packet->stream; -} - -const struct bt_stream *bt_packet_borrow_stream_const( - const struct bt_packet *packet) -{ - return bt_packet_borrow_stream((void *) packet); -} - -struct bt_field *bt_packet_borrow_context_field(struct bt_packet *packet) -{ - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - return packet->context_field ? packet->context_field->field : NULL; -} - -const struct bt_field *bt_packet_borrow_context_field_const( - const struct bt_packet *packet) -{ - return bt_packet_borrow_context_field((void *) packet); -} - -BT_HIDDEN -void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen) -{ - if (!packet) { - return; - } - - BT_LIB_LOGD("Setting packet's frozen state: %![packet-]+a, " - "is-frozen=%d", packet, is_frozen); - - if (packet->context_field) { - BT_LOGD_STR("Setting packet's context field's frozen state."); - bt_field_set_is_frozen(packet->context_field->field, - is_frozen); - } - - ((struct bt_packet *) packet)->frozen = is_frozen; -} - -static inline -void reset_packet(struct bt_packet *packet) -{ - BT_ASSERT(packet); - BT_LIB_LOGD("Resetting packet: %!+a", packet); - bt_packet_set_is_frozen(packet, false); - - if (packet->context_field) { - bt_field_set_is_frozen(packet->context_field->field, false); - bt_field_reset(packet->context_field->field); - } -} - -static -void recycle_context_field(struct bt_field_wrapper *context_field, - struct bt_stream_class *stream_class) -{ - BT_ASSERT(context_field); - BT_LIB_LOGD("Recycling packet context field: " - "addr=%p, %![sc-]+S, %![field-]+f", context_field, - stream_class, context_field->field); - bt_object_pool_recycle_object(&stream_class->packet_context_field_pool, - context_field); -} - -BT_HIDDEN -void bt_packet_recycle(struct bt_packet *packet) -{ - struct bt_stream *stream; - - BT_ASSERT(packet); - BT_LIB_LOGD("Recycling packet: %!+a", packet); - - /* - * Those are the important ordered steps: - * - * 1. Reset the packet object (put any permanent reference it - * has, unfreeze it and its fields in developer mode, etc.), - * but do NOT put its stream's reference. This stream - * contains the pool to which we're about to recycle this - * packet object, so we must guarantee its existence thanks - * to this existing reference. - * - * 2. Move the stream reference to our `stream` - * variable so that we can set the packet's stream member - * to NULL before recycling it. We CANNOT do this after - * we put the stream reference because this bt_object_put_ref() - * could destroy the stream, also destroying its - * packet pool, thus also destroying our packet object (this - * would result in an invalid write access). - * - * 3. Recycle the packet object. - * - * 4. Put our stream reference. - */ - reset_packet(packet); - stream = packet->stream; - BT_ASSERT(stream); - packet->stream = NULL; - bt_object_pool_recycle_object(&stream->packet_pool, packet); - bt_object_put_no_null_check(&stream->base); -} - -BT_HIDDEN -void bt_packet_destroy(struct bt_packet *packet) -{ - BT_LIB_LOGD("Destroying packet: %!+a", packet); - - if (packet->context_field) { - if (packet->stream) { - BT_LOGD_STR("Recycling packet's context field."); - recycle_context_field(packet->context_field, - packet->stream->class); - } else { - bt_field_wrapper_destroy(packet->context_field); - } - - packet->context_field = NULL; - } - - BT_LOGD_STR("Putting packet's stream."); - BT_OBJECT_PUT_REF_AND_RESET(packet->stream); - g_free(packet); -} - -BT_HIDDEN -struct bt_packet *bt_packet_new(struct bt_stream *stream) -{ - struct bt_packet *packet = NULL; - struct bt_trace_class *trace_class = NULL; - - BT_ASSERT(stream); - BT_LIB_LOGD("Creating packet object: %![stream-]+s", stream); - packet = g_new0(struct bt_packet, 1); - if (!packet) { - BT_LOGE_STR("Failed to allocate one packet object."); - goto error; - } - - bt_object_init_shared(&packet->base, - (bt_object_release_func) bt_packet_recycle); - packet->stream = stream; - bt_object_get_no_null_check(stream); - trace_class = bt_stream_class_borrow_trace_class_inline(stream->class); - BT_ASSERT(trace_class); - - if (stream->class->packet_context_fc) { - BT_LOGD_STR("Creating initial packet context field."); - packet->context_field = bt_field_wrapper_create( - &stream->class->packet_context_field_pool, - stream->class->packet_context_fc); - if (!packet->context_field) { - BT_LOGE_STR("Cannot create packet context field wrapper."); - goto error; - } - } - - BT_LIB_LOGD("Created packet object: %!+a", packet); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(packet); - -end: - return packet; -} - -struct bt_packet *bt_packet_create(const struct bt_stream *c_stream) -{ - struct bt_packet *packet = NULL; - struct bt_stream *stream = (void *) c_stream; - - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - packet = bt_object_pool_create_object(&stream->packet_pool); - if (unlikely(!packet)) { - BT_LIB_LOGE("Cannot allocate one packet from stream's packet pool: " - "%![stream-]+s", stream); - goto end; - } - - if (likely(!packet->stream)) { - packet->stream = stream; - bt_object_get_no_null_check_no_parent_check( - &packet->stream->base); - } - -end: - return (void *) packet; -} - -enum bt_packet_status bt_packet_move_context_field(struct bt_packet *packet, - struct bt_packet_context_field *context_field) -{ - struct bt_stream_class *stream_class; - struct bt_field_wrapper *field_wrapper = (void *) context_field; - - BT_ASSERT_PRE_NON_NULL(packet, "Packet"); - BT_ASSERT_PRE_NON_NULL(field_wrapper, "Context field"); - BT_ASSERT_PRE_HOT(packet, "Packet", ": %!+a", packet); - stream_class = packet->stream->class; - BT_ASSERT_PRE(stream_class->packet_context_fc, - "Stream class has no packet context field class: %!+S", - stream_class); - BT_ASSERT_PRE(field_wrapper->field->class == - stream_class->packet_context_fc, - "Unexpected packet context field's class: " - "%![fc-]+F, %![expected-fc-]+F", field_wrapper->field->class, - stream_class->packet_context_fc); - - /* Recycle current context field: always exists */ - BT_ASSERT(packet->context_field); - recycle_context_field(packet->context_field, stream_class); - - /* Move new field */ - packet->context_field = field_wrapper; - return BT_PACKET_STATUS_OK; -} - -void bt_packet_get_ref(const struct bt_packet *packet) -{ - bt_object_get_ref(packet); -} - -void bt_packet_put_ref(const struct bt_packet *packet) -{ - bt_object_put_ref(packet); -} diff --git a/lib/trace-ir/resolve-field-path.c b/lib/trace-ir/resolve-field-path.c deleted file mode 100644 index 1f7b5cd2..00000000 --- a/lib/trace-ir/resolve-field-path.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright 2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "RESOLVE-FIELD-PATH" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static -bool find_field_class_recursive(struct bt_field_class *fc, - struct bt_field_class *tgt_fc, struct bt_field_path *field_path) -{ - bool found = false; - - if (tgt_fc == fc) { - found = true; - goto end; - } - - switch (fc->type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_field_class_named_field_class_container *container_fc = - (void *) fc; - uint64_t i; - - for (i = 0; i < container_fc->named_fcs->len; i++) { - struct bt_named_field_class *named_fc = - BT_FIELD_CLASS_NAMED_FC_AT_INDEX( - container_fc, i); - struct bt_field_path_item item = { - .type = BT_FIELD_PATH_ITEM_TYPE_INDEX, - .index = i, - }; - - bt_field_path_append_item(field_path, &item); - found = find_field_class_recursive(named_fc->fc, - tgt_fc, field_path); - if (found) { - goto end; - } - - bt_field_path_remove_last_item(field_path); - } - - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - struct bt_field_class_array *array_fc = (void *) fc; - struct bt_field_path_item item = { - .type = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT, - .index = UINT64_C(-1), - }; - - bt_field_path_append_item(field_path, &item); - found = find_field_class_recursive(array_fc->element_fc, - tgt_fc, field_path); - if (found) { - goto end; - } - - bt_field_path_remove_last_item(field_path); - break; - } - default: - break; - } - -end: - return found; -} - -static -int find_field_class(struct bt_field_class *root_fc, - enum bt_scope root_scope, struct bt_field_class *tgt_fc, - struct bt_field_path **ret_field_path) -{ - int ret = 0; - struct bt_field_path *field_path = NULL; - - if (!root_fc) { - goto end; - } - - field_path = bt_field_path_create(); - if (!field_path) { - ret = -1; - goto end; - } - - field_path->root = root_scope; - if (!find_field_class_recursive(root_fc, tgt_fc, field_path)) { - /* Not found here */ - BT_OBJECT_PUT_REF_AND_RESET(field_path); - } - -end: - *ret_field_path = field_path; - return ret; -} - -static -struct bt_field_path *find_field_class_in_ctx(struct bt_field_class *fc, - struct bt_resolve_field_path_context *ctx) -{ - struct bt_field_path *field_path = NULL; - int ret; - - ret = find_field_class(ctx->packet_context, BT_SCOPE_PACKET_CONTEXT, - fc, &field_path); - if (ret || field_path) { - goto end; - } - - ret = find_field_class(ctx->event_common_context, - BT_SCOPE_EVENT_COMMON_CONTEXT, fc, &field_path); - if (ret || field_path) { - goto end; - } - - ret = find_field_class(ctx->event_specific_context, - BT_SCOPE_EVENT_SPECIFIC_CONTEXT, fc, &field_path); - if (ret || field_path) { - goto end; - } - - ret = find_field_class(ctx->event_payload, BT_SCOPE_EVENT_PAYLOAD, - fc, &field_path); - if (ret || field_path) { - goto end; - } - -end: - return field_path; -} - -BT_ASSERT_PRE_FUNC -static inline -bool target_is_before_source(struct bt_field_path *src_field_path, - struct bt_field_path *tgt_field_path) -{ - bool is_valid = true; - uint64_t src_i = 0, tgt_i = 0; - - if (tgt_field_path->root < src_field_path->root) { - goto end; - } - - if (tgt_field_path->root > src_field_path->root) { - is_valid = false; - goto end; - } - - BT_ASSERT(tgt_field_path->root == src_field_path->root); - - for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && - tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { - struct bt_field_path_item *src_fp_item = - bt_field_path_borrow_item_by_index_inline( - src_field_path, src_i); - struct bt_field_path_item *tgt_fp_item = - bt_field_path_borrow_item_by_index_inline( - tgt_field_path, tgt_i); - - if (src_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX && - tgt_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX) { - if (tgt_fp_item->index > src_fp_item->index) { - is_valid = false; - goto end; - } - } - - src_i++; - tgt_i++; - } - -end: - return is_valid; -} - -BT_ASSERT_PRE_FUNC -static inline -struct bt_field_class *borrow_root_field_class( - struct bt_resolve_field_path_context *ctx, enum bt_scope scope) -{ - switch (scope) { - case BT_SCOPE_PACKET_CONTEXT: - return ctx->packet_context; - case BT_SCOPE_EVENT_COMMON_CONTEXT: - return ctx->event_common_context; - case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: - return ctx->event_specific_context; - case BT_SCOPE_EVENT_PAYLOAD: - return ctx->event_payload; - default: - abort(); - } - - return NULL; -} - -BT_ASSERT_PRE_FUNC -static inline -struct bt_field_class *borrow_child_field_class( - struct bt_field_class *parent_fc, - struct bt_field_path_item *fp_item) -{ - struct bt_field_class *child_fc = NULL; - - switch (parent_fc->type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_named_field_class *named_fc; - - BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX); - named_fc = BT_FIELD_CLASS_NAMED_FC_AT_INDEX(parent_fc, - fp_item->index); - child_fc = named_fc->fc; - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - struct bt_field_class_array *array_fc = (void *) parent_fc; - - BT_ASSERT(fp_item->type == - BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); - child_fc = array_fc->element_fc; - break; - } - default: - break; - } - - return child_fc; -} - -BT_ASSERT_PRE_FUNC -static inline -bool target_field_path_in_different_scope_has_struct_fc_only( - struct bt_field_path *src_field_path, - struct bt_field_path *tgt_field_path, - struct bt_resolve_field_path_context *ctx) -{ - bool is_valid = true; - uint64_t i = 0; - struct bt_field_class *fc; - - if (src_field_path->root == tgt_field_path->root) { - goto end; - } - - fc = borrow_root_field_class(ctx, tgt_field_path->root); - - for (i = 0; i < tgt_field_path->items->len; i++) { - struct bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_inline( - tgt_field_path, i); - - if (fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || - fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY || - fc->type == BT_FIELD_CLASS_TYPE_VARIANT) { - is_valid = false; - goto end; - } - - BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX); - fc = borrow_child_field_class(fc, fp_item); - } - -end: - return is_valid; -} - -BT_ASSERT_PRE_FUNC -static inline -bool lca_is_structure_field_class(struct bt_field_path *src_field_path, - struct bt_field_path *tgt_field_path, - struct bt_resolve_field_path_context *ctx) -{ - bool is_valid = true; - struct bt_field_class *src_fc; - struct bt_field_class *tgt_fc; - struct bt_field_class *prev_fc = NULL; - uint64_t src_i = 0, tgt_i = 0; - - if (src_field_path->root != tgt_field_path->root) { - goto end; - } - - src_fc = borrow_root_field_class(ctx, src_field_path->root); - tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root); - BT_ASSERT(src_fc); - BT_ASSERT(tgt_fc); - - for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && - tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { - struct bt_field_path_item *src_fp_item = - bt_field_path_borrow_item_by_index_inline( - src_field_path, src_i); - struct bt_field_path_item *tgt_fp_item = - bt_field_path_borrow_item_by_index_inline( - tgt_field_path, tgt_i); - - if (src_fc != tgt_fc) { - if (!prev_fc) { - /* - * This is correct: the LCA is the root - * scope field class, which must be a - * structure field class. - */ - break; - } - - if (prev_fc->type != BT_FIELD_CLASS_TYPE_STRUCTURE) { - is_valid = false; - } - - break; - } - - prev_fc = src_fc; - src_fc = borrow_child_field_class(src_fc, src_fp_item); - tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); - } - -end: - return is_valid; -} - -BT_ASSERT_PRE_FUNC -static inline -bool lca_to_target_has_struct_fc_only(struct bt_field_path *src_field_path, - struct bt_field_path *tgt_field_path, - struct bt_resolve_field_path_context *ctx) -{ - bool is_valid = true; - struct bt_field_class *src_fc; - struct bt_field_class *tgt_fc; - uint64_t src_i = 0, tgt_i = 0; - - if (src_field_path->root != tgt_field_path->root) { - goto end; - } - - src_fc = borrow_root_field_class(ctx, src_field_path->root); - tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root); - BT_ASSERT(src_fc); - BT_ASSERT(tgt_fc); - BT_ASSERT(src_fc == tgt_fc); - - /* Find LCA */ - for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && - tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { - struct bt_field_path_item *src_fp_item = - bt_field_path_borrow_item_by_index_inline( - src_field_path, src_i); - struct bt_field_path_item *tgt_fp_item = - bt_field_path_borrow_item_by_index_inline( - tgt_field_path, tgt_i); - - if (src_i != tgt_i) { - /* Next field class is different: LCA is `tgt_fc` */ - break; - } - - src_fc = borrow_child_field_class(src_fc, src_fp_item); - tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); - } - - /* Only structure field classes to the target */ - for (; tgt_i < tgt_field_path->items->len; tgt_i++) { - struct bt_field_path_item *tgt_fp_item = - bt_field_path_borrow_item_by_index_inline( - tgt_field_path, tgt_i); - - if (tgt_fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || - tgt_fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY || - tgt_fc->type == BT_FIELD_CLASS_TYPE_VARIANT) { - is_valid = false; - goto end; - } - - tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); - } - -end: - return is_valid; -} - -BT_ASSERT_PRE_FUNC -static inline -bool field_path_is_valid(struct bt_field_class *src_fc, - struct bt_field_class *tgt_fc, - struct bt_resolve_field_path_context *ctx) -{ - bool is_valid = true; - struct bt_field_path *src_field_path = find_field_class_in_ctx( - src_fc, ctx); - struct bt_field_path *tgt_field_path = find_field_class_in_ctx( - tgt_fc, ctx); - - if (!src_field_path) { - BT_ASSERT_PRE_MSG("Cannot find requesting field class in " - "resolving context: %!+F", src_fc); - is_valid = false; - goto end; - } - - if (!tgt_field_path) { - BT_ASSERT_PRE_MSG("Cannot find target field class in " - "resolving context: %!+F", tgt_fc); - is_valid = false; - goto end; - } - - /* Target must be before source */ - if (!target_is_before_source(src_field_path, tgt_field_path)) { - BT_ASSERT_PRE_MSG("Target field class is located after " - "requesting field class: %![req-fc-]+F, %![tgt-fc-]+F", - src_fc, tgt_fc); - is_valid = false; - goto end; - } - - /* - * If target is in a different scope than source, there are no - * array or variant field classes on the way to the target. - */ - if (!target_field_path_in_different_scope_has_struct_fc_only( - src_field_path, tgt_field_path, ctx)) { - BT_ASSERT_PRE_MSG("Target field class is located in a " - "different scope than requesting field class, " - "but within an array or a variant field class: " - "%![req-fc-]+F, %![tgt-fc-]+F", - src_fc, tgt_fc); - is_valid = false; - goto end; - } - - /* Same scope: LCA must be a structure field class */ - if (!lca_is_structure_field_class(src_field_path, tgt_field_path, ctx)) { - BT_ASSERT_PRE_MSG("Lowest common ancestor of target and " - "requesting field classes is not a structure field class: " - "%![req-fc-]+F, %![tgt-fc-]+F", - src_fc, tgt_fc); - is_valid = false; - goto end; - } - - /* Same scope: path from LCA to target has no array/variant FTs */ - if (!lca_to_target_has_struct_fc_only(src_field_path, tgt_field_path, - ctx)) { - BT_ASSERT_PRE_MSG("Path from lowest common ancestor of target " - "and requesting field classes to target field class " - "contains an array or a variant field class: " - "%![req-fc-]+F, %![tgt-fc-]+F", src_fc, tgt_fc); - is_valid = false; - goto end; - } - -end: - bt_object_put_ref(src_field_path); - bt_object_put_ref(tgt_field_path); - return is_valid; -} - -static -struct bt_field_path *resolve_field_path(struct bt_field_class *src_fc, - struct bt_field_class *tgt_fc, - struct bt_resolve_field_path_context *ctx) -{ - BT_ASSERT_PRE(field_path_is_valid(src_fc, tgt_fc, ctx), - "Invalid target field class: %![req-fc-]+F, %![tgt-fc-]+F", - src_fc, tgt_fc); - return find_field_class_in_ctx(tgt_fc, ctx); -} - -BT_HIDDEN -int bt_resolve_field_paths(struct bt_field_class *fc, - struct bt_resolve_field_path_context *ctx) -{ - int ret = 0; - - BT_ASSERT(fc); - - /* Resolving part for dynamic array and variant field classes */ - switch (fc->type) { - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - struct bt_field_class_dynamic_array *dyn_array_fc = (void *) fc; - - if (dyn_array_fc->length_fc) { - BT_ASSERT(!dyn_array_fc->length_field_path); - dyn_array_fc->length_field_path = resolve_field_path( - fc, dyn_array_fc->length_fc, ctx); - if (!dyn_array_fc->length_field_path) { - ret = -1; - goto end; - } - } - - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_field_class_variant *var_fc = (void *) fc; - - if (var_fc->selector_fc) { - BT_ASSERT(!var_fc->selector_field_path); - var_fc->selector_field_path = - resolve_field_path(fc, - var_fc->selector_fc, ctx); - if (!var_fc->selector_field_path) { - ret = -1; - goto end; - } - } - } - default: - break; - } - - /* Recursive part */ - switch (fc->type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - case BT_FIELD_CLASS_TYPE_VARIANT: - { - struct bt_field_class_named_field_class_container *container_fc = - (void *) fc; - uint64_t i; - - for (i = 0; i < container_fc->named_fcs->len; i++) { - struct bt_named_field_class *named_fc = - BT_FIELD_CLASS_NAMED_FC_AT_INDEX( - container_fc, i); - - ret = bt_resolve_field_paths(named_fc->fc, ctx); - if (ret) { - goto end; - } - } - - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - struct bt_field_class_array *array_fc = (void *) fc; - - ret = bt_resolve_field_paths(array_fc->element_fc, ctx); - break; - } - default: - break; - } - -end: - return ret; -} diff --git a/lib/trace-ir/stream-class.c b/lib/trace-ir/stream-class.c deleted file mode 100644 index 1f6732d8..00000000 --- a/lib/trace-ir/stream-class.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "STREAM-CLASS" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_STREAM_CLASS_HOT(_sc) \ - BT_ASSERT_PRE_HOT((_sc), "Stream class", ": %!+S", (_sc)) - -static -void destroy_stream_class(struct bt_object *obj) -{ - struct bt_stream_class *stream_class = (void *) obj; - - BT_LIB_LOGD("Destroying stream class: %!+S", stream_class); - BT_LOGD_STR("Putting default clock class."); - BT_OBJECT_PUT_REF_AND_RESET(stream_class->default_clock_class); - - if (stream_class->event_classes) { - BT_LOGD_STR("Destroying event classes."); - g_ptr_array_free(stream_class->event_classes, TRUE); - stream_class->event_classes = NULL; - } - - if (stream_class->name.str) { - g_string_free(stream_class->name.str, TRUE); - stream_class->name.str = NULL; - stream_class->name.value = NULL; - } - - BT_LOGD_STR("Putting packet context field class."); - BT_OBJECT_PUT_REF_AND_RESET(stream_class->packet_context_fc); - BT_LOGD_STR("Putting event common context field class."); - BT_OBJECT_PUT_REF_AND_RESET(stream_class->event_common_context_fc); - bt_object_pool_finalize(&stream_class->packet_context_field_pool); - g_free(stream_class); -} - -static -void free_field_wrapper(struct bt_field_wrapper *field_wrapper, - struct bt_stream_class *stream_class) -{ - bt_field_wrapper_destroy((void *) field_wrapper); -} - -BT_ASSERT_PRE_FUNC -static -bool stream_class_id_is_unique(const struct bt_trace_class *tc, uint64_t id) -{ - uint64_t i; - bool is_unique = true; - - for (i = 0; i < tc->stream_classes->len; i++) { - const struct bt_stream_class *sc = - tc->stream_classes->pdata[i]; - - if (sc->id == id) { - is_unique = false; - goto end; - } - } - -end: - return is_unique; -} - -static -struct bt_stream_class *create_stream_class_with_id( - struct bt_trace_class *tc, uint64_t id) -{ - struct bt_stream_class *stream_class = NULL; - int ret; - - BT_ASSERT(tc); - BT_ASSERT_PRE(stream_class_id_is_unique(tc, id), - "Duplicate stream class ID: %![tc-]+T, id=%" PRIu64, tc, id); - BT_LIB_LOGD("Creating stream class object: %![tc-]+T, id=%" PRIu64, - tc, id); - stream_class = g_new0(struct bt_stream_class, 1); - if (!stream_class) { - BT_LOGE_STR("Failed to allocate one stream class."); - goto error; - } - - bt_object_init_shared_with_parent(&stream_class->base, - destroy_stream_class); - - stream_class->name.str = g_string_new(NULL); - if (!stream_class->name.str) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = -1; - goto end; - } - - stream_class->id = id; - stream_class->assigns_automatic_event_class_id = true; - stream_class->assigns_automatic_stream_id = true; - stream_class->event_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!stream_class->event_classes) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - ret = bt_object_pool_initialize(&stream_class->packet_context_field_pool, - (bt_object_pool_new_object_func) bt_field_wrapper_new, - (bt_object_pool_destroy_object_func) free_field_wrapper, - stream_class); - if (ret) { - BT_LOGE("Failed to initialize packet context field pool: ret=%d", - ret); - goto error; - } - - bt_object_set_parent(&stream_class->base, &tc->base); - g_ptr_array_add(tc->stream_classes, stream_class); - bt_trace_class_freeze(tc); - BT_LIB_LOGD("Created stream class object: %!+S", stream_class); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(stream_class); - -end: - return stream_class; -} - -struct bt_stream_class *bt_stream_class_create(struct bt_trace_class *tc) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE(tc->assigns_automatic_stream_class_id, - "Trace class does not automatically assigns stream class IDs: " - "%![sc-]+T", tc); - return create_stream_class_with_id(tc, - (uint64_t) tc->stream_classes->len); -} - -struct bt_stream_class *bt_stream_class_create_with_id( - struct bt_trace_class *tc, uint64_t id) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE(!tc->assigns_automatic_stream_class_id, - "Trace class automatically assigns stream class IDs: " - "%![sc-]+T", tc); - return create_stream_class_with_id(tc, id); -} - -struct bt_trace_class *bt_stream_class_borrow_trace_class( - struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return bt_stream_class_borrow_trace_class_inline(stream_class); -} - -const struct bt_trace_class *bt_stream_class_borrow_trace_class_const( - const struct bt_stream_class *stream_class) -{ - return bt_stream_class_borrow_trace_class((void *) stream_class); -} - -const char *bt_stream_class_get_name(const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->name.value; -} - -enum bt_stream_class_status bt_stream_class_set_name( - struct bt_stream_class *stream_class, - const char *name) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - g_string_assign(stream_class->name.str, name); - stream_class->name.value = stream_class->name.str->str; - BT_LIB_LOGV("Set stream class's name: %!+S", stream_class); - return BT_STREAM_CLASS_STATUS_OK; -} - -uint64_t bt_stream_class_get_id(const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->id; -} - -uint64_t bt_stream_class_get_event_class_count( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (uint64_t) stream_class->event_classes->len; -} - -struct bt_event_class *bt_stream_class_borrow_event_class_by_index( - struct bt_stream_class *stream_class, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_VALID_INDEX(index, stream_class->event_classes->len); - return g_ptr_array_index(stream_class->event_classes, index); -} - -const struct bt_event_class * -bt_stream_class_borrow_event_class_by_index_const( - const struct bt_stream_class *stream_class, uint64_t index) -{ - return bt_stream_class_borrow_event_class_by_index( - (void *) stream_class, index); -} - -struct bt_event_class *bt_stream_class_borrow_event_class_by_id( - struct bt_stream_class *stream_class, uint64_t id) -{ - struct bt_event_class *event_class = NULL; - uint64_t i; - - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - - for (i = 0; i < stream_class->event_classes->len; i++) { - struct bt_event_class *event_class_candidate = - g_ptr_array_index(stream_class->event_classes, i); - - if (event_class_candidate->id == id) { - event_class = event_class_candidate; - goto end; - } - } - -end: - return event_class; -} - -const struct bt_event_class * -bt_stream_class_borrow_event_class_by_id_const( - const struct bt_stream_class *stream_class, uint64_t id) -{ - return bt_stream_class_borrow_event_class_by_id( - (void *) stream_class, id); -} - -const struct bt_field_class * -bt_stream_class_borrow_packet_context_field_class_const( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->packet_context_fc; -} - -struct bt_field_class * -bt_stream_class_borrow_packet_context_field_class( - struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->packet_context_fc; -} - -enum bt_stream_class_status bt_stream_class_set_packet_context_field_class( - struct bt_stream_class *stream_class, - struct bt_field_class *field_class) -{ - int ret; - struct bt_resolve_field_path_context resolve_ctx = { - .packet_context = field_class, - .event_common_context = NULL, - .event_specific_context = NULL, - .event_payload = NULL, - }; - - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(bt_field_class_get_type(field_class) == - BT_FIELD_CLASS_TYPE_STRUCTURE, - "Packet context field class is not a structure field class: %!+F", - field_class); - ret = bt_resolve_field_paths(field_class, &resolve_ctx); - if (ret) { - /* - * This is the only reason for which - * bt_resolve_field_paths() can fail: anything else - * would be because a precondition is not satisfied. - */ - ret = BT_STREAM_CLASS_STATUS_NOMEM; - goto end; - } - - bt_field_class_make_part_of_trace_class(field_class); - bt_object_put_ref(stream_class->packet_context_fc); - stream_class->packet_context_fc = field_class; - bt_object_get_no_null_check(stream_class->packet_context_fc); - bt_field_class_freeze(field_class); - BT_LIB_LOGV("Set stream class's packet context field class: %!+S", - stream_class); - -end: - return ret; -} - -const struct bt_field_class * -bt_stream_class_borrow_event_common_context_field_class_const( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->event_common_context_fc; -} - -struct bt_field_class * -bt_stream_class_borrow_event_common_context_field_class( - struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->event_common_context_fc; -} - -enum bt_stream_class_status -bt_stream_class_set_event_common_context_field_class( - struct bt_stream_class *stream_class, - struct bt_field_class *field_class) -{ - int ret; - struct bt_resolve_field_path_context resolve_ctx = { - .packet_context = NULL, - .event_common_context = field_class, - .event_specific_context = NULL, - .event_payload = NULL, - }; - - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(bt_field_class_get_type(field_class) == - BT_FIELD_CLASS_TYPE_STRUCTURE, - "Event common context field class is not a structure field class: %!+F", - field_class); - resolve_ctx.packet_context = stream_class->packet_context_fc; - ret = bt_resolve_field_paths(field_class, &resolve_ctx); - if (ret) { - /* - * This is the only reason for which - * bt_resolve_field_paths() can fail: anything else - * would be because a precondition is not satisfied. - */ - ret = BT_STREAM_CLASS_STATUS_NOMEM; - goto end; - } - - bt_field_class_make_part_of_trace_class(field_class); - bt_object_put_ref(stream_class->event_common_context_fc); - stream_class->event_common_context_fc = field_class; - bt_object_get_no_null_check(stream_class->event_common_context_fc); - bt_field_class_freeze(field_class); - BT_LIB_LOGV("Set stream class's event common context field class: %!+S", - stream_class); - -end: - return ret; -} - -BT_HIDDEN -void _bt_stream_class_freeze(const struct bt_stream_class *stream_class) -{ - /* The field classes and default clock class are already frozen */ - BT_ASSERT(stream_class); - BT_LIB_LOGD("Freezing stream class: %!+S", stream_class); - ((struct bt_stream_class *) stream_class)->frozen = true; -} - -enum bt_stream_class_status bt_stream_class_set_default_clock_class( - struct bt_stream_class *stream_class, - struct bt_clock_class *clock_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - bt_object_put_ref(stream_class->default_clock_class); - stream_class->default_clock_class = clock_class; - bt_object_get_no_null_check(stream_class->default_clock_class); - bt_clock_class_freeze(clock_class); - BT_LIB_LOGV("Set stream class's default clock class: %!+S", - stream_class); - return BT_STREAM_CLASS_STATUS_OK; -} - -struct bt_clock_class *bt_stream_class_borrow_default_clock_class( - struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->default_clock_class; -} - -const struct bt_clock_class *bt_stream_class_borrow_default_clock_class_const( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return stream_class->default_clock_class; -} - -bt_bool bt_stream_class_assigns_automatic_event_class_id( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->assigns_automatic_event_class_id; -} - -void bt_stream_class_set_assigns_automatic_event_class_id( - struct bt_stream_class *stream_class, - bt_bool value) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - stream_class->assigns_automatic_event_class_id = (bool) value; - BT_LIB_LOGV("Set stream class's automatic event class ID " - "assignment property: %!+S", stream_class); -} - -bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->packets_have_beginning_default_clock_snapshot; -} - -void bt_stream_class_set_packets_have_beginning_default_clock_snapshot( - struct bt_stream_class *stream_class, bt_bool value) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(!value || stream_class->default_clock_class, - "Stream class has no default clock class: %!+S", stream_class); - stream_class->packets_have_beginning_default_clock_snapshot = - (bool) value; - BT_LIB_LOGV("Set stream class's \"packets have default beginning " - "clock snapshot\" property: %!+S", stream_class); -} - -bt_bool bt_stream_class_packets_have_end_default_clock_snapshot( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->packets_have_end_default_clock_snapshot; -} - -void bt_stream_class_set_packets_have_end_default_clock_snapshot( - struct bt_stream_class *stream_class, bt_bool value) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(!value || stream_class->default_clock_class, - "Stream class has no default clock class: %!+S", stream_class); - stream_class->packets_have_end_default_clock_snapshot = - (bool) value; - BT_LIB_LOGV("Set stream class's \"packets have default end " - "clock snapshot\" property: %!+S", stream_class); -} - -bt_bool bt_stream_class_assigns_automatic_stream_id( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->assigns_automatic_stream_id; -} - -void bt_stream_class_set_supports_discarded_events( - struct bt_stream_class *stream_class, - bt_bool supports_discarded_events, - bt_bool with_default_clock_snapshots) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(supports_discarded_events || - !with_default_clock_snapshots, - "Discarded events cannot have default clock snapshots when " - "not supported: %!+S", stream_class); - BT_ASSERT_PRE(!with_default_clock_snapshots || - stream_class->default_clock_class, - "Stream class has no default clock class: %!+S", stream_class); - stream_class->supports_discarded_events = - (bool) supports_discarded_events; - stream_class->discarded_events_have_default_clock_snapshots = - (bool) with_default_clock_snapshots; - BT_LIB_LOGV("Set stream class's discarded events support property: " - "%!+S", stream_class); -} - -bt_bool bt_stream_class_supports_discarded_events( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->supports_discarded_events; -} - -bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->discarded_events_have_default_clock_snapshots; -} - -void bt_stream_class_set_supports_discarded_packets( - struct bt_stream_class *stream_class, - bt_bool supports_discarded_packets, - bt_bool with_default_clock_snapshots) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - BT_ASSERT_PRE(supports_discarded_packets || - !with_default_clock_snapshots, - "Discarded packets cannot have default clock snapshots when " - "not supported: %!+S", stream_class); - BT_ASSERT_PRE(!with_default_clock_snapshots || - stream_class->default_clock_class, - "Stream class has no default clock class: %!+S", stream_class); - stream_class->supports_discarded_packets = - (bool) supports_discarded_packets; - stream_class->discarded_packets_have_default_clock_snapshots = - (bool) with_default_clock_snapshots; - BT_LIB_LOGV("Set stream class's discarded packets support property: " - "%!+S", stream_class); -} - -bt_bool bt_stream_class_supports_discarded_packets( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->supports_discarded_packets; -} - -bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots( - const struct bt_stream_class *stream_class) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - return (bt_bool) stream_class->discarded_packets_have_default_clock_snapshots; -} - -void bt_stream_class_set_assigns_automatic_stream_id( - struct bt_stream_class *stream_class, - bt_bool value) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); - stream_class->assigns_automatic_stream_id = (bool) value; - BT_LIB_LOGV("Set stream class's automatic stream ID " - "assignment property: %!+S", stream_class); -} - -void bt_stream_class_get_ref(const struct bt_stream_class *stream_class) -{ - bt_object_get_ref(stream_class); -} - -void bt_stream_class_put_ref(const struct bt_stream_class *stream_class) -{ - bt_object_put_ref(stream_class); -} diff --git a/lib/trace-ir/stream.c b/lib/trace-ir/stream.c deleted file mode 100644 index 3af38f32..00000000 --- a/lib/trace-ir/stream.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2013, 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "STREAM" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_ASSERT_PRE_STREAM_HOT(_stream) \ - BT_ASSERT_PRE_HOT((_stream), "Stream", ": %!+s", (_stream)) - -static -void destroy_stream(struct bt_object *obj) -{ - struct bt_stream *stream = (void *) obj; - - BT_LIB_LOGD("Destroying stream object: %!+s", stream); - - if (stream->name.str) { - g_string_free(stream->name.str, TRUE); - stream->name.str = NULL; - stream->name.value = NULL; - } - - BT_LOGD_STR("Putting stream's class."); - bt_object_put_ref(stream->class); - bt_object_pool_finalize(&stream->packet_pool); - g_free(stream); -} - -static -void bt_stream_free_packet(struct bt_packet *packet, struct bt_stream *stream) -{ - bt_packet_destroy(packet); -} - -BT_ASSERT_PRE_FUNC -static inline -bool stream_id_is_unique(struct bt_trace *trace, - struct bt_stream_class *stream_class, uint64_t id) -{ - uint64_t i; - bool is_unique = true; - - for (i = 0; i < trace->streams->len; i++) { - struct bt_stream *stream = trace->streams->pdata[i]; - - if (stream->class != stream_class) { - continue; - } - - if (stream->id == id) { - is_unique = false; - goto end; - } - } - -end: - return is_unique; -} - -static -struct bt_stream *create_stream_with_id(struct bt_stream_class *stream_class, - struct bt_trace *trace, uint64_t id) -{ - int ret; - struct bt_stream *stream; - - BT_ASSERT(stream_class); - BT_ASSERT(trace); - BT_ASSERT_PRE(trace->class == - bt_stream_class_borrow_trace_class_inline(stream_class), - "Trace's class is different from stream class's parent trace class: " - "%![sc-]+S, %![trace-]+t", stream_class, trace); - BT_ASSERT_PRE(stream_id_is_unique(trace, stream_class, id), - "Duplicate stream ID: %![trace-]+t, id=%" PRIu64, trace, id); - BT_LIB_LOGD("Creating stream object: %![trace-]+t, id=%" PRIu64, - trace, id); - stream = g_new0(struct bt_stream, 1); - if (!stream) { - BT_LOGE_STR("Failed to allocate one stream."); - goto error; - } - - bt_object_init_shared_with_parent(&stream->base, destroy_stream); - stream->name.str = g_string_new(NULL); - if (!stream->name.str) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - stream->id = id; - ret = bt_object_pool_initialize(&stream->packet_pool, - (bt_object_pool_new_object_func) bt_packet_new, - (bt_object_pool_destroy_object_func) bt_stream_free_packet, - stream); - if (ret) { - BT_LOGE("Failed to initialize packet pool: ret=%d", ret); - goto error; - } - - stream->class = stream_class; - bt_object_get_no_null_check(stream_class); - - /* bt_trace_add_stream() sets the parent trace, and freezes the trace */ - bt_trace_add_stream(trace, stream); - - bt_stream_class_freeze(stream_class); - BT_LIB_LOGD("Created stream object: %!+s", stream); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(stream); - -end: - return stream; -} - -struct bt_stream *bt_stream_create(struct bt_stream_class *stream_class, - struct bt_trace *trace) -{ - uint64_t id; - - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE(stream_class->assigns_automatic_stream_id, - "Stream class does not automatically assigns stream IDs: " - "%![sc-]+S", stream_class); - id = bt_trace_get_automatic_stream_id(trace, stream_class); - return create_stream_with_id(stream_class, trace, id); -} - -struct bt_stream *bt_stream_create_with_id(struct bt_stream_class *stream_class, - struct bt_trace *trace, uint64_t id) -{ - BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE(!stream_class->assigns_automatic_stream_id, - "Stream class automatically assigns stream IDs: " - "%![sc-]+S", stream_class); - return create_stream_with_id(stream_class, trace, id); -} - -struct bt_stream_class *bt_stream_borrow_class(struct bt_stream *stream) -{ - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - return stream->class; -} - -const struct bt_stream_class *bt_stream_borrow_class_const( - const struct bt_stream *stream) -{ - return bt_stream_borrow_class((void *) stream); -} - -struct bt_trace *bt_stream_borrow_trace(struct bt_stream *stream) -{ - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - return bt_stream_borrow_trace_inline(stream); -} - -const struct bt_trace *bt_stream_borrow_trace_const( - const struct bt_stream *stream) -{ - return bt_stream_borrow_trace((void *) stream); -} - -const char *bt_stream_get_name(const struct bt_stream *stream) -{ - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - return stream->name.value; -} - -enum bt_stream_status bt_stream_set_name(struct bt_stream *stream, - const char *name) -{ - BT_ASSERT_PRE_NON_NULL(stream, "Stream"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_STREAM_HOT(stream); - g_string_assign(stream->name.str, name); - stream->name.value = stream->name.str->str; - BT_LIB_LOGV("Set stream's name: %!+s", stream); - return BT_STREAM_STATUS_OK; -} - -uint64_t bt_stream_get_id(const struct bt_stream *stream) -{ - BT_ASSERT_PRE_NON_NULL(stream, "Stream class"); - return stream->id; -} - -BT_HIDDEN -void _bt_stream_freeze(const struct bt_stream *stream) -{ - BT_ASSERT(stream); - BT_LIB_LOGD("Freezing stream: %!+s", stream); - ((struct bt_stream *) stream)->frozen = true; -} - -void bt_stream_get_ref(const struct bt_stream *stream) -{ - bt_object_get_ref(stream); -} - -void bt_stream_put_ref(const struct bt_stream *stream) -{ - bt_object_put_ref(stream); -} diff --git a/lib/trace-ir/trace-class.c b/lib/trace-ir/trace-class.c deleted file mode 100644 index 51cc6691..00000000 --- a/lib/trace-ir/trace-class.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "TRACE" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_trace_class_destruction_listener_elem { - bt_trace_class_destruction_listener_func func; - void *data; -}; - -#define BT_ASSERT_PRE_TRACE_CLASS_HOT(_tc) \ - BT_ASSERT_PRE_HOT((_tc), "Trace class", ": %!+T", (_tc)) - -static -void destroy_trace_class(struct bt_object *obj) -{ - struct bt_trace_class *tc = (void *) obj; - - BT_LIB_LOGD("Destroying trace class object: %!+T", tc); - /* - * Call destruction listener functions so that everything else - * still exists in the trace class. - */ - if (tc->destruction_listeners) { - uint64_t i; - BT_LIB_LOGV("Calling trace class destruction listener(s): %!+T", tc); - - /* - * The trace class' reference count is 0 if we're here. Increment - * it to avoid a double-destroy (possibly infinitely recursive). - * This could happen for example if a destruction listener did - * bt_object_get_ref() (or anything that causes - * bt_object_get_ref() to be called) on the trace class (ref. - * count goes from 0 to 1), and then bt_object_put_ref(): the - * reference count would go from 1 to 0 again and this function - * would be called again. - */ - tc->base.ref_count++; - - /* Call all the trace class destruction listeners */ - for (i = 0; i < tc->destruction_listeners->len; i++) { - struct bt_trace_class_destruction_listener_elem elem = - g_array_index(tc->destruction_listeners, - struct bt_trace_class_destruction_listener_elem, i); - - if (elem.func) { - elem.func(tc, elem.data); - } - - /* - * The destruction listener should not have kept a - * reference to the trace class. - */ - BT_ASSERT_PRE(tc->base.ref_count == 1, "Destruction listener kept a reference to the trace class being destroyed: %![tc-]+T", tc); - } - g_array_free(tc->destruction_listeners, TRUE); - tc->destruction_listeners = NULL; - } - - if (tc->environment) { - BT_LOGD_STR("Destroying environment attributes."); - bt_attributes_destroy(tc->environment); - tc->environment = NULL; - } - - if (tc->name.str) { - g_string_free(tc->name.str, TRUE); - tc->name.str = NULL; - tc->name.value = NULL; - } - - if (tc->stream_classes) { - BT_LOGD_STR("Destroying stream classes."); - g_ptr_array_free(tc->stream_classes, TRUE); - tc->stream_classes = NULL; - } - - g_free(tc); -} - -struct bt_trace_class *bt_trace_class_create(bt_self_component *self_comp) -{ - struct bt_trace_class *tc = NULL; - - BT_ASSERT_PRE_NON_NULL(self_comp, "Self component"); - BT_LOGD_STR("Creating default trace class object."); - tc = g_new0(struct bt_trace_class, 1); - if (!tc) { - BT_LOGE_STR("Failed to allocate one trace class."); - goto error; - } - - bt_object_init_shared_with_parent(&tc->base, destroy_trace_class); - - tc->stream_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!tc->stream_classes) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - - tc->name.str = g_string_new(NULL); - if (!tc->name.str) { - BT_LOGE_STR("Failed to allocate one GString."); - goto error; - } - - tc->environment = bt_attributes_create(); - if (!tc->environment) { - BT_LOGE_STR("Cannot create empty attributes object."); - goto error; - } - - tc->destruction_listeners = g_array_new(FALSE, TRUE, - sizeof(struct bt_trace_class_destruction_listener_elem)); - if (!tc->destruction_listeners) { - BT_LOGE_STR("Failed to allocate one GArray."); - goto error; - } - - tc->assigns_automatic_stream_class_id = true; - BT_LIB_LOGD("Created trace class object: %!+T", tc); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(tc); - -end: - return tc; -} - -const char *bt_trace_class_get_name(const struct bt_trace_class *tc) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - return tc->name.value; -} - -enum bt_trace_class_status bt_trace_class_set_name( - struct bt_trace_class *tc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); - g_string_assign(tc->name.str, name); - tc->name.value = tc->name.str->str; - BT_LIB_LOGV("Set trace class's name: %!+T", tc); - return BT_TRACE_CLASS_STATUS_OK; -} - -bt_uuid bt_trace_class_get_uuid(const struct bt_trace_class *tc) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - return tc->uuid.value; -} - -void bt_trace_class_set_uuid(struct bt_trace_class *tc, bt_uuid uuid) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(uuid, "UUID"); - BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); - memcpy(tc->uuid.uuid, uuid, BABELTRACE_UUID_LEN); - tc->uuid.value = tc->uuid.uuid; - BT_LIB_LOGV("Set trace class's UUID: %!+T", tc); -} - -enum bt_trace_class_status bt_trace_class_add_destruction_listener( - const struct bt_trace_class *_tc, - bt_trace_class_destruction_listener_func listener, - void *data, uint64_t *listener_id) -{ - struct bt_trace_class *tc = (void *) _tc; - uint64_t i; - struct bt_trace_class_destruction_listener_elem new_elem = { - .func = listener, - .data = data, - }; - - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(listener, "Listener"); - - /* Find the next available spot */ - for (i = 0; i < tc->destruction_listeners->len; i++) { - struct bt_trace_class_destruction_listener_elem elem = - g_array_index(tc->destruction_listeners, - struct bt_trace_class_destruction_listener_elem, i); - - if (!elem.func) { - break; - } - } - - if (i == tc->destruction_listeners->len) { - g_array_append_val(tc->destruction_listeners, new_elem); - } else { - g_array_insert_val(tc->destruction_listeners, i, new_elem); - } - - if (listener_id) { - *listener_id = i; - } - - BT_LIB_LOGV("Added trace class destruction listener: %![tc-]+T, " - "listener-id=%" PRIu64, tc, i); - return BT_TRACE_CLASS_STATUS_OK; -} - -BT_ASSERT_PRE_FUNC -static -bool has_listener_id(const struct bt_trace_class *tc, uint64_t listener_id) -{ - BT_ASSERT(listener_id < tc->destruction_listeners->len); - return (&g_array_index(tc->destruction_listeners, - struct bt_trace_class_destruction_listener_elem, - listener_id))->func != NULL; -} - -enum bt_trace_class_status bt_trace_class_remove_destruction_listener( - const struct bt_trace_class *_tc, uint64_t listener_id) -{ - struct bt_trace_class *tc = (void *) _tc; - struct bt_trace_class_destruction_listener_elem *elem; - - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE(has_listener_id(tc, listener_id), - "Trace class has no such trace class destruction listener ID: " - "%![tc-]+T, %" PRIu64, tc, listener_id); - elem = &g_array_index(tc->destruction_listeners, - struct bt_trace_class_destruction_listener_elem, - listener_id); - BT_ASSERT(elem->func); - - elem->func = NULL; - elem->data = NULL; - BT_LIB_LOGV("Removed trace class destruction listener: " - "%![tc-]+T, listener-id=%" PRIu64, - tc, listener_id); - return BT_TRACE_CLASS_STATUS_OK; -} - -BT_ASSERT_FUNC -static -bool trace_has_environment_entry(const struct bt_trace_class *tc, const char *name) -{ - BT_ASSERT(tc); - - return bt_attributes_borrow_field_value_by_name( - tc->environment, name) != NULL; -} - -static -enum bt_trace_class_status set_environment_entry(struct bt_trace_class *tc, - const char *name, struct bt_value *value) -{ - int ret; - - BT_ASSERT(tc); - BT_ASSERT(name); - BT_ASSERT(value); - BT_ASSERT_PRE(!tc->frozen || - !trace_has_environment_entry(tc, name), - "Trace class is frozen: cannot replace environment entry: " - "%![tc-]+T, entry-name=\"%s\"", tc, name); - ret = bt_attributes_set_field_value(tc->environment, name, - value); - if (ret) { - ret = BT_TRACE_CLASS_STATUS_NOMEM; - BT_LIB_LOGE("Cannot set trace class's environment entry: " - "%![tc-]+T, entry-name=\"%s\"", tc, name); - } else { - bt_value_freeze(value); - BT_LIB_LOGV("Set trace class's environment entry: " - "%![tc-]+T, entry-name=\"%s\"", tc, name); - } - - return ret; -} - -enum bt_trace_class_status bt_trace_class_set_environment_entry_string( - struct bt_trace_class *tc, const char *name, const char *value) -{ - int ret; - struct bt_value *value_obj; - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(value, "Value"); - value_obj = bt_value_string_create_init(value); - if (!value_obj) { - BT_LOGE_STR("Cannot create a string value object."); - ret = -1; - goto end; - } - - /* set_environment_entry() logs errors */ - ret = set_environment_entry(tc, name, value_obj); - -end: - bt_object_put_ref(value_obj); - return ret; -} - -enum bt_trace_class_status bt_trace_class_set_environment_entry_integer( - struct bt_trace_class *tc, const char *name, int64_t value) -{ - int ret; - struct bt_value *value_obj; - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - value_obj = bt_value_signed_integer_create_init(value); - if (!value_obj) { - BT_LOGE_STR("Cannot create an integer value object."); - ret = BT_TRACE_CLASS_STATUS_NOMEM; - goto end; - } - - /* set_environment_entry() logs errors */ - ret = set_environment_entry(tc, name, value_obj); - -end: - bt_object_put_ref(value_obj); - return ret; -} - -uint64_t bt_trace_class_get_environment_entry_count(const struct bt_trace_class *tc) -{ - int64_t ret; - - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - ret = bt_attributes_get_count(tc->environment); - BT_ASSERT(ret >= 0); - return (uint64_t) ret; -} - -void bt_trace_class_borrow_environment_entry_by_index_const( - const struct bt_trace_class *tc, uint64_t index, - const char **name, const struct bt_value **value) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_NON_NULL(value, "Value"); - BT_ASSERT_PRE_VALID_INDEX(index, - bt_attributes_get_count(tc->environment)); - *value = bt_attributes_borrow_field_value(tc->environment, index); - BT_ASSERT(*value); - *name = bt_attributes_get_field_name(tc->environment, index); - BT_ASSERT(*name); -} - -const struct bt_value *bt_trace_class_borrow_environment_entry_value_by_name_const( - const struct bt_trace_class *tc, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - return bt_attributes_borrow_field_value_by_name(tc->environment, - name); -} - -uint64_t bt_trace_class_get_stream_class_count(const struct bt_trace_class *tc) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - return (uint64_t) tc->stream_classes->len; -} - -struct bt_stream_class *bt_trace_class_borrow_stream_class_by_index( - struct bt_trace_class *tc, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_VALID_INDEX(index, tc->stream_classes->len); - return g_ptr_array_index(tc->stream_classes, index); -} - -const struct bt_stream_class * -bt_trace_class_borrow_stream_class_by_index_const( - const struct bt_trace_class *tc, uint64_t index) -{ - return bt_trace_class_borrow_stream_class_by_index( - (void *) tc, index); -} - -struct bt_stream_class *bt_trace_class_borrow_stream_class_by_id( - struct bt_trace_class *tc, uint64_t id) -{ - struct bt_stream_class *stream_class = NULL; - uint64_t i; - - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - - for (i = 0; i < tc->stream_classes->len; i++) { - struct bt_stream_class *stream_class_candidate = - g_ptr_array_index(tc->stream_classes, i); - - if (stream_class_candidate->id == id) { - stream_class = stream_class_candidate; - goto end; - } - } - -end: - return stream_class; -} - -const struct bt_stream_class * -bt_trace_class_borrow_stream_class_by_id_const( - const struct bt_trace_class *tc, uint64_t id) -{ - return bt_trace_class_borrow_stream_class_by_id((void *) tc, id); -} - -BT_HIDDEN -void _bt_trace_class_freeze(const struct bt_trace_class *tc) -{ - BT_ASSERT(tc); - BT_LIB_LOGD("Freezing trace class: %!+T", tc); - ((struct bt_trace_class *) tc)->frozen = true; -} - -bt_bool bt_trace_class_assigns_automatic_stream_class_id(const struct bt_trace_class *tc) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - return (bt_bool) tc->assigns_automatic_stream_class_id; -} - -void bt_trace_class_set_assigns_automatic_stream_class_id(struct bt_trace_class *tc, - bt_bool value) -{ - BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); - BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); - tc->assigns_automatic_stream_class_id = (bool) value; - BT_LIB_LOGV("Set trace class's automatic stream class ID " - "assignment property: %!+T", tc); -} - -void bt_trace_class_get_ref(const struct bt_trace_class *trace_class) -{ - bt_object_get_ref(trace_class); -} - -void bt_trace_class_put_ref(const struct bt_trace_class *trace_class) -{ - bt_object_put_ref(trace_class); -} diff --git a/lib/trace-ir/trace.c b/lib/trace-ir/trace.c deleted file mode 100644 index da8d7d75..00000000 --- a/lib/trace-ir/trace.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2014 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "TRACE" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bt_trace_destruction_listener_elem { - bt_trace_destruction_listener_func func; - void *data; -}; - -#define BT_ASSERT_PRE_TRACE_HOT(_trace) \ - BT_ASSERT_PRE_HOT((_trace), "Trace", ": %!+t", (_trace)) - -static -void destroy_trace(struct bt_object *obj) -{ - struct bt_trace *trace = (void *) obj; - - BT_LIB_LOGD("Destroying trace object: %!+t", trace); - - /* - * Call destruction listener functions so that everything else - * still exists in the trace. - */ - if (trace->destruction_listeners) { - uint64_t i; - BT_LIB_LOGV("Calling trace destruction listener(s): %!+t", trace); - - /* - * The trace's reference count is 0 if we're here. Increment - * it to avoid a double-destroy (possibly infinitely recursive). - * This could happen for example if a destruction listener did - * bt_object_get_ref() (or anything that causes - * bt_object_get_ref() to be called) on the trace (ref. - * count goes from 0 to 1), and then bt_object_put_ref(): the - * reference count would go from 1 to 0 again and this function - * would be called again. - */ - trace->base.ref_count++; - - /* Call all the trace destruction listeners */ - for (i = 0; i < trace->destruction_listeners->len; i++) { - struct bt_trace_destruction_listener_elem elem = - g_array_index(trace->destruction_listeners, - struct bt_trace_destruction_listener_elem, i); - - if (elem.func) { - elem.func(trace, elem.data); - } - - /* - * The destruction listener should not have kept a - * reference to the trace. - */ - BT_ASSERT_PRE(trace->base.ref_count == 1, "Destruction listener kept a reference to the trace being destroyed: %![trace-]+t", trace); - } - g_array_free(trace->destruction_listeners, TRUE); - trace->destruction_listeners = NULL; - } - - if (trace->name.str) { - g_string_free(trace->name.str, TRUE); - trace->name.str = NULL; - trace->name.value = NULL; - } - - if (trace->streams) { - BT_LOGD_STR("Destroying streams."); - g_ptr_array_free(trace->streams, TRUE); - trace->streams = NULL; - } - - if (trace->stream_classes_stream_count) { - g_hash_table_destroy(trace->stream_classes_stream_count); - trace->stream_classes_stream_count = NULL; - } - - BT_LOGD_STR("Putting trace's class."); - bt_object_put_ref(trace->class); - trace->class = NULL; - g_free(trace); -} - -struct bt_trace *bt_trace_create(struct bt_trace_class *tc) -{ - struct bt_trace *trace = NULL; - - BT_LIB_LOGD("Creating trace object: %![tc-]+T", tc); - trace = g_new0(struct bt_trace, 1); - if (!trace) { - BT_LOGE_STR("Failed to allocate one trace."); - goto error; - } - - bt_object_init_shared(&trace->base, destroy_trace); - trace->streams = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_try_spec_release); - if (!trace->streams) { - BT_LOGE_STR("Failed to allocate one GPtrArray."); - goto error; - } - - trace->stream_classes_stream_count = g_hash_table_new(g_direct_hash, - g_direct_equal); - if (!trace->stream_classes_stream_count) { - BT_LOGE_STR("Failed to allocate one GHashTable."); - goto error; - } - - trace->name.str = g_string_new(NULL); - if (!trace->name.str) { - BT_LOGE_STR("Failed to allocate one GString."); - goto error; - } - - trace->destruction_listeners = g_array_new(FALSE, TRUE, - sizeof(struct bt_trace_destruction_listener_elem)); - if (!trace->destruction_listeners) { - BT_LOGE_STR("Failed to allocate one GArray."); - goto error; - } - - trace->class = tc; - bt_object_get_no_null_check(trace->class); - BT_LIB_LOGD("Created trace object: %!+t", trace); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(trace); - -end: - return trace; -} - -const char *bt_trace_get_name(const struct bt_trace *trace) -{ - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->name.value; -} - -enum bt_trace_status bt_trace_set_name(struct bt_trace *trace, const char *name) -{ - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE_NON_NULL(name, "Name"); - BT_ASSERT_PRE_TRACE_HOT(trace); - g_string_assign(trace->name.str, name); - trace->name.value = trace->name.str->str; - BT_LIB_LOGV("Set trace's name: %!+t", trace); - return BT_TRACE_STATUS_OK; -} - -uint64_t bt_trace_get_stream_count(const struct bt_trace *trace) -{ - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - return (uint64_t) trace->streams->len; -} - -struct bt_stream *bt_trace_borrow_stream_by_index( - struct bt_trace *trace, uint64_t index) -{ - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE_VALID_INDEX(index, trace->streams->len); - return g_ptr_array_index(trace->streams, index); -} - -const struct bt_stream *bt_trace_borrow_stream_by_index_const( - const struct bt_trace *trace, uint64_t index) -{ - return bt_trace_borrow_stream_by_index((void *) trace, index); -} - -struct bt_stream *bt_trace_borrow_stream_by_id(struct bt_trace *trace, - uint64_t id) -{ - struct bt_stream *stream = NULL; - uint64_t i; - - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - - for (i = 0; i < trace->streams->len; i++) { - struct bt_stream *stream_candidate = - g_ptr_array_index(trace->streams, i); - - if (stream_candidate->id == id) { - stream = stream_candidate; - goto end; - } - } - -end: - return stream; -} - -const struct bt_stream *bt_trace_borrow_stream_by_id_const( - const struct bt_trace *trace, uint64_t id) -{ - return bt_trace_borrow_stream_by_id((void *) trace, id); -} - -enum bt_trace_status bt_trace_add_destruction_listener( - const struct bt_trace *c_trace, - bt_trace_destruction_listener_func listener, - void *data, uint64_t *listener_id) -{ - struct bt_trace *trace = (void *) c_trace; - uint64_t i; - struct bt_trace_destruction_listener_elem new_elem = { - .func = listener, - .data = data, - }; - - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE_NON_NULL(listener, "Listener"); - - /* Find the next available spot */ - for (i = 0; i < trace->destruction_listeners->len; i++) { - struct bt_trace_destruction_listener_elem elem = - g_array_index(trace->destruction_listeners, - struct bt_trace_destruction_listener_elem, i); - - if (!elem.func) { - break; - } - } - - if (i == trace->destruction_listeners->len) { - g_array_append_val(trace->destruction_listeners, new_elem); - } else { - g_array_insert_val(trace->destruction_listeners, i, new_elem); - } - - if (listener_id) { - *listener_id = i; - } - - BT_LIB_LOGV("Added destruction listener: " "%![trace-]+t, " - "listener-id=%" PRIu64, trace, i); - return BT_TRACE_STATUS_OK; -} - -BT_ASSERT_PRE_FUNC -static -bool has_listener_id(const struct bt_trace *trace, uint64_t listener_id) -{ - BT_ASSERT(listener_id < trace->destruction_listeners->len); - return (&g_array_index(trace->destruction_listeners, - struct bt_trace_destruction_listener_elem, - listener_id))->func != NULL; -} - -enum bt_trace_status bt_trace_remove_destruction_listener( - const struct bt_trace *c_trace, uint64_t listener_id) -{ - struct bt_trace *trace = (void *) c_trace; - struct bt_trace_destruction_listener_elem *elem; - - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - BT_ASSERT_PRE(has_listener_id(trace, listener_id), - "Trace has no such trace destruction listener ID: " - "%![trace-]+t, %" PRIu64, trace, listener_id); - elem = &g_array_index(trace->destruction_listeners, - struct bt_trace_destruction_listener_elem, - listener_id); - BT_ASSERT(elem->func); - - elem->func = NULL; - elem->data = NULL; - BT_LIB_LOGV("Removed \"trace destruction listener: " - "%![trace-]+t, listener-id=%" PRIu64, - trace, listener_id); - return BT_TRACE_STATUS_OK; -} - -BT_HIDDEN -void _bt_trace_freeze(const struct bt_trace *trace) -{ - BT_ASSERT(trace); - BT_LIB_LOGD("Freezing trace's class: %!+T", trace->class); - bt_trace_class_freeze(trace->class); - BT_LIB_LOGD("Freezing trace: %!+t", trace); - ((struct bt_trace *) trace)->frozen = true; -} - -BT_HIDDEN -void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream) -{ - guint count = 0; - - bt_object_set_parent(&stream->base, &trace->base); - g_ptr_array_add(trace->streams, stream); - bt_trace_freeze(trace); - - if (bt_g_hash_table_contains(trace->stream_classes_stream_count, - stream->class)) { - count = GPOINTER_TO_UINT(g_hash_table_lookup( - trace->stream_classes_stream_count, stream->class)); - } - - g_hash_table_insert(trace->stream_classes_stream_count, - stream->class, GUINT_TO_POINTER(count + 1)); -} - -BT_HIDDEN -uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace, - const struct bt_stream_class *stream_class) -{ - gpointer orig_key; - gpointer value; - uint64_t id = 0; - - BT_ASSERT(stream_class); - BT_ASSERT(trace); - if (g_hash_table_lookup_extended(trace->stream_classes_stream_count, - stream_class, &orig_key, &value)) { - id = (uint64_t) GPOINTER_TO_UINT(value); - } - - return id; -} - -struct bt_trace_class *bt_trace_borrow_class(struct bt_trace *trace) -{ - BT_ASSERT_PRE_NON_NULL(trace, "Trace"); - return trace->class; -} - -const struct bt_trace_class *bt_trace_borrow_class_const( - const struct bt_trace *trace) -{ - return bt_trace_borrow_class((void *) trace); -} - -void bt_trace_get_ref(const struct bt_trace *trace) -{ - bt_object_get_ref(trace); -} - -void bt_trace_put_ref(const struct bt_trace *trace) -{ - bt_object_put_ref(trace); -} diff --git a/lib/trace-ir/utils.c b/lib/trace-ir/utils.c deleted file mode 100644 index 355cb77d..00000000 --- a/lib/trace-ir/utils.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017-2018 Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "TRACE-IR-UTILS" -#include - -#include -#include -#include -#include -#include diff --git a/lib/util.c b/lib/util.c deleted file mode 100644 index 82456dc8..00000000 --- a/lib/util.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2015-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "UTIL" -#include - -#include -#include -#include -#include -#include -#include -#include - -bt_util_status bt_util_clock_cycles_to_ns_from_origin(uint64_t cycles, - uint64_t frequency, int64_t offset_seconds, - uint64_t offset_cycles, int64_t *ns) -{ - bool overflows; - int64_t base_offset_ns; - bt_util_status status = BT_UTIL_STATUS_OK; - int ret; - - BT_ASSERT_PRE_NON_NULL(ns, "Nanoseconds (output)"); - BT_ASSERT_PRE(frequency != UINT64_C(-1) && frequency != 0, - "Invalid frequency: freq=%" PRIu64, frequency); - BT_ASSERT_PRE(offset_cycles < frequency, - "Offset (cycles) is greater than frequency: " - "offset-cycles=%" PRIu64 ", freq=%" PRIu64, - offset_cycles, frequency); - - overflows = bt_util_get_base_offset_ns(offset_seconds, offset_cycles, - frequency, &base_offset_ns); - if (overflows) { - status = BT_UTIL_STATUS_OVERFLOW; - goto end; - } - - ret = bt_util_ns_from_origin_inline(base_offset_ns, - offset_seconds, offset_cycles, - frequency, cycles, ns); - if (ret) { - status = BT_UTIL_STATUS_OVERFLOW; - } - -end: - return status; -} diff --git a/lib/value.c b/lib/value.c deleted file mode 100644 index 6ec7497d..00000000 --- a/lib/value.c +++ /dev/null @@ -1,1369 +0,0 @@ -/* - * Copyright (c) 2015-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "VALUES" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_VALUE_TO_BOOL(_base) ((struct bt_value_bool *) (_base)) -#define BT_VALUE_TO_INTEGER(_base) ((struct bt_value_integer *) (_base)) -#define BT_VALUE_TO_REAL(_base) ((struct bt_value_real *) (_base)) -#define BT_VALUE_TO_STRING(_base) ((struct bt_value_string *) (_base)) -#define BT_VALUE_TO_ARRAY(_base) ((struct bt_value_array *) (_base)) -#define BT_VALUE_TO_MAP(_base) ((struct bt_value_map *) (_base)) - -#define BT_ASSERT_PRE_VALUE_IS_TYPE(_value, _type) \ - BT_ASSERT_PRE(((struct bt_value *) (_value))->type == (_type), \ - "Value has the wrong type ID: expected-type=%s, " \ - "%![value-]+v", bt_common_value_type_string(_type), \ - (_value)) - -#define BT_ASSERT_PRE_VALUE_HOT(_value, _name) \ - BT_ASSERT_PRE_HOT(((struct bt_value *) (_value)), (_name), \ - ": %!+v", (_value)) - -#define BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(_index, _count) \ - BT_ASSERT_PRE((_index) < (_count), \ - "Index is out of bound: " \ - "index=%" PRIu64 ", count=%u", (_index), (_count)); - -static -void bt_value_null_instance_release_func(struct bt_object *obj) -{ - BT_LOGW("Releasing the null value singleton: addr=%p", obj); -} - -static -struct bt_value bt_value_null_instance = { - .base = { - .is_shared = true, - .ref_count = 1, - .release_func = bt_value_null_instance_release_func, - .spec_release_func = NULL, - .parent_is_owner_listener_func = NULL, - .parent = NULL, - }, - .type = BT_VALUE_TYPE_NULL, - .frozen = BT_TRUE, -}; - -struct bt_value *const bt_value_null = &bt_value_null_instance; - -static -void bt_value_destroy(struct bt_object *obj); - -static -void bt_value_string_destroy(struct bt_value *object) -{ - g_string_free(BT_VALUE_TO_STRING(object)->gstr, TRUE); - BT_VALUE_TO_STRING(object)->gstr = NULL; -} - -static -void bt_value_array_destroy(struct bt_value *object) -{ - /* - * Pointer array's registered value destructor will take care - * of putting each contained object. - */ - g_ptr_array_free(BT_VALUE_TO_ARRAY(object)->garray, TRUE); - BT_VALUE_TO_ARRAY(object)->garray = NULL; -} - -static -void bt_value_map_destroy(struct bt_value *object) -{ - /* - * Hash table's registered value destructor will take care of - * putting each contained object. Keys are GQuarks and cannot - * be destroyed anyway. - */ - g_hash_table_destroy(BT_VALUE_TO_MAP(object)->ght); - BT_VALUE_TO_MAP(object)->ght = NULL; -} - -static -void (* const destroy_funcs[])(struct bt_value *) = { - [BT_VALUE_TYPE_NULL] = NULL, - [BT_VALUE_TYPE_BOOL] = NULL, - [BT_VALUE_TYPE_UNSIGNED_INTEGER] = NULL, - [BT_VALUE_TYPE_SIGNED_INTEGER] = NULL, - [BT_VALUE_TYPE_REAL] = NULL, - [BT_VALUE_TYPE_STRING] = bt_value_string_destroy, - [BT_VALUE_TYPE_ARRAY] = bt_value_array_destroy, - [BT_VALUE_TYPE_MAP] = bt_value_map_destroy, -}; - -static -struct bt_value *bt_value_null_copy(const struct bt_value *null_obj) -{ - return (void *) bt_value_null; -} - -static -struct bt_value *bt_value_bool_copy(const struct bt_value *bool_obj) -{ - return bt_value_bool_create_init( - BT_VALUE_TO_BOOL(bool_obj)->value); -} - -static inline -struct bt_value *bt_value_integer_create_init(enum bt_value_type type, - uint64_t uval); - -static -struct bt_value *bt_value_integer_copy( - const struct bt_value *integer_obj) -{ - return bt_value_integer_create_init(integer_obj->type, - BT_VALUE_TO_INTEGER(integer_obj)->value.u); -} - -static -struct bt_value *bt_value_real_copy(const struct bt_value *real_obj) -{ - return bt_value_real_create_init( - BT_VALUE_TO_REAL(real_obj)->value); -} - -static -struct bt_value *bt_value_string_copy(const struct bt_value *string_obj) -{ - return bt_value_string_create_init( - BT_VALUE_TO_STRING(string_obj)->gstr->str); -} - -static -struct bt_value *bt_value_array_copy(const struct bt_value *array_obj) -{ - int i; - int ret; - struct bt_value *copy_obj; - struct bt_value_array *typed_array_obj; - - BT_LOGD("Copying array value: addr=%p", array_obj); - typed_array_obj = BT_VALUE_TO_ARRAY(array_obj); - copy_obj = bt_value_array_create(); - if (!copy_obj) { - BT_LOGE_STR("Cannot create empty array value."); - goto end; - } - - for (i = 0; i < typed_array_obj->garray->len; ++i) { - struct bt_value *element_obj_copy = NULL; - const struct bt_value *element_obj = - bt_value_array_borrow_element_by_index_const( - array_obj, i); - - BT_ASSERT(element_obj); - BT_LOGD("Copying array value's element: element-addr=%p, " - "index=%d", element_obj, i); - ret = bt_value_copy(element_obj, &element_obj_copy); - if (ret) { - BT_LOGE("Cannot copy array value's element: " - "array-addr=%p, index=%d", - array_obj, i); - BT_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - - BT_ASSERT(element_obj_copy); - ret = bt_value_array_append_element(copy_obj, - (void *) element_obj_copy); - BT_OBJECT_PUT_REF_AND_RESET(element_obj_copy); - if (ret) { - BT_LOGE("Cannot append to array value: addr=%p", - array_obj); - BT_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - } - - BT_LOGD("Copied array value: original-addr=%p, copy-addr=%p", - array_obj, copy_obj); - -end: - return copy_obj; -} - -static -struct bt_value *bt_value_map_copy(const struct bt_value *map_obj) -{ - int ret; - GHashTableIter iter; - gpointer key, element_obj; - struct bt_value *copy_obj; - struct bt_value *element_obj_copy = NULL; - struct bt_value_map *typed_map_obj; - - BT_LOGD("Copying map value: addr=%p", map_obj); - typed_map_obj = BT_VALUE_TO_MAP(map_obj); - copy_obj = bt_value_map_create(); - if (!copy_obj) { - goto end; - } - - g_hash_table_iter_init(&iter, typed_map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - BT_ASSERT(key_str); - BT_LOGD("Copying map value's element: element-addr=%p, " - "key=\"%s\"", element_obj, key_str); - ret = bt_value_copy(element_obj, &element_obj_copy); - if (ret) { - BT_LOGE("Cannot copy map value's element: " - "map-addr=%p, key=\"%s\"", - map_obj, key_str); - BT_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - - BT_ASSERT(element_obj_copy); - ret = bt_value_map_insert_entry(copy_obj, key_str, - (void *) element_obj_copy); - BT_OBJECT_PUT_REF_AND_RESET(element_obj_copy); - if (ret) { - BT_LOGE("Cannot insert into map value: addr=%p, key=\"%s\"", - map_obj, key_str); - BT_OBJECT_PUT_REF_AND_RESET(copy_obj); - goto end; - } - } - - BT_LOGD("Copied map value: addr=%p", map_obj); - -end: - return copy_obj; -} - -static -struct bt_value *(* const copy_funcs[])(const struct bt_value *) = { - [BT_VALUE_TYPE_NULL] = bt_value_null_copy, - [BT_VALUE_TYPE_BOOL] = bt_value_bool_copy, - [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_integer_copy, - [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_integer_copy, - [BT_VALUE_TYPE_REAL] = bt_value_real_copy, - [BT_VALUE_TYPE_STRING] = bt_value_string_copy, - [BT_VALUE_TYPE_ARRAY] = bt_value_array_copy, - [BT_VALUE_TYPE_MAP] = bt_value_map_copy, -}; - -static -bt_bool bt_value_null_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - /* - * Always BT_TRUE since bt_value_compare() already checks if both - * object_a and object_b have the same type, and in the case of - * null value objects, they're always the same if it is so. - */ - return BT_TRUE; -} - -static -bt_bool bt_value_bool_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - if (BT_VALUE_TO_BOOL(object_a)->value != - BT_VALUE_TO_BOOL(object_b)->value) { - BT_LOGV("Boolean value objects are different: " - "bool-a-val=%d, bool-b-val=%d", - BT_VALUE_TO_BOOL(object_a)->value, - BT_VALUE_TO_BOOL(object_b)->value); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_value_integer_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - if (BT_VALUE_TO_INTEGER(object_a)->value.u != - BT_VALUE_TO_INTEGER(object_b)->value.u) { - if (object_a->type == BT_VALUE_TYPE_UNSIGNED_INTEGER) { - BT_LOGV("Unsigned integer value objects are different: " - "int-a-val=%" PRIu64 ", int-b-val=%" PRIu64, - BT_VALUE_TO_INTEGER(object_a)->value.u, - BT_VALUE_TO_INTEGER(object_b)->value.u); - } else { - BT_LOGV("Signed integer value objects are different: " - "int-a-val=%" PRId64 ", int-b-val=%" PRId64, - BT_VALUE_TO_INTEGER(object_a)->value.i, - BT_VALUE_TO_INTEGER(object_b)->value.i); - } - - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_value_real_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - if (BT_VALUE_TO_REAL(object_a)->value != - BT_VALUE_TO_REAL(object_b)->value) { - BT_LOGV("Real number value objects are different: " - "real-a-val=%f, real-b-val=%f", - BT_VALUE_TO_REAL(object_a)->value, - BT_VALUE_TO_REAL(object_b)->value); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_value_string_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - if (strcmp(BT_VALUE_TO_STRING(object_a)->gstr->str, - BT_VALUE_TO_STRING(object_b)->gstr->str) != 0) { - BT_LOGV("String value objects are different: " - "string-a-val=\"%s\", string-b-val=\"%s\"", - BT_VALUE_TO_STRING(object_a)->gstr->str, - BT_VALUE_TO_STRING(object_b)->gstr->str); - return BT_FALSE; - } - - return BT_TRUE; -} - -static -bt_bool bt_value_array_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - int i; - bt_bool ret = BT_TRUE; - const struct bt_value_array *array_obj_a = - BT_VALUE_TO_ARRAY(object_a); - - if (bt_value_array_get_size(object_a) != - bt_value_array_get_size(object_b)) { - BT_LOGV("Array values are different: size mismatch " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-size=%" PRId64 ", value-b-size=%" PRId64, - object_a, object_b, - bt_value_array_get_size(object_a), - bt_value_array_get_size(object_b)); - ret = BT_FALSE; - goto end; - } - - for (i = 0; i < array_obj_a->garray->len; ++i) { - const struct bt_value *element_obj_a; - const struct bt_value *element_obj_b; - - element_obj_a = bt_value_array_borrow_element_by_index_const( - object_a, i); - element_obj_b = bt_value_array_borrow_element_by_index_const( - object_b, i); - - if (!bt_value_compare(element_obj_a, element_obj_b)) { - BT_LOGV("Array values's elements are different: " - "value-a-addr=%p, value-b-addr=%p, index=%d", - element_obj_a, element_obj_b, i); - ret = BT_FALSE; - goto end; - } - } - -end: - return ret; -} - -static -bt_bool bt_value_map_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - bt_bool ret = BT_TRUE; - GHashTableIter iter; - gpointer key, element_obj_a; - const struct bt_value_map *map_obj_a = BT_VALUE_TO_MAP(object_a); - - if (bt_value_map_get_size(object_a) != - bt_value_map_get_size(object_b)) { - BT_LOGV("Map values are different: size mismatch " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-size=%" PRId64 ", value-b-size=%" PRId64, - object_a, object_b, - bt_value_map_get_size(object_a), - bt_value_map_get_size(object_b)); - ret = BT_FALSE; - goto end; - } - - g_hash_table_iter_init(&iter, map_obj_a->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj_a)) { - const struct bt_value *element_obj_b; - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - element_obj_b = bt_value_map_borrow_entry_value_const(object_b, - key_str); - - if (!bt_value_compare(element_obj_a, element_obj_b)) { - BT_LOGV("Map values's elements are different: " - "value-a-addr=%p, value-b-addr=%p, key=\"%s\"", - element_obj_a, element_obj_b, key_str); - ret = BT_FALSE; - goto end; - } - } - -end: - return ret; -} - -static -bt_bool (* const compare_funcs[])(const struct bt_value *, - const struct bt_value *) = { - [BT_VALUE_TYPE_NULL] = bt_value_null_compare, - [BT_VALUE_TYPE_BOOL] = bt_value_bool_compare, - [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_integer_compare, - [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_integer_compare, - [BT_VALUE_TYPE_REAL] = bt_value_real_compare, - [BT_VALUE_TYPE_STRING] = bt_value_string_compare, - [BT_VALUE_TYPE_ARRAY] = bt_value_array_compare, - [BT_VALUE_TYPE_MAP] = bt_value_map_compare, -}; - -static -void bt_value_null_freeze(struct bt_value *object) -{ -} - -static -void bt_value_generic_freeze(struct bt_value *object) -{ - object->frozen = BT_TRUE; -} - -static -void bt_value_array_freeze(struct bt_value *object) -{ - int i; - struct bt_value_array *typed_array_obj = - BT_VALUE_TO_ARRAY(object); - - for (i = 0; i < typed_array_obj->garray->len; ++i) { - bt_value_freeze(g_ptr_array_index(typed_array_obj->garray, i)); - } - - bt_value_generic_freeze(object); -} - -static -void bt_value_map_freeze(struct bt_value *object) -{ - GHashTableIter iter; - gpointer key, element_obj; - const struct bt_value_map *map_obj = BT_VALUE_TO_MAP(object); - - g_hash_table_iter_init(&iter, map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - bt_value_freeze(element_obj); - } - - bt_value_generic_freeze(object); -} - -static -void (* const freeze_funcs[])(struct bt_value *) = { - [BT_VALUE_TYPE_NULL] = bt_value_null_freeze, - [BT_VALUE_TYPE_BOOL] = bt_value_generic_freeze, - [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_generic_freeze, - [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_generic_freeze, - [BT_VALUE_TYPE_REAL] = bt_value_generic_freeze, - [BT_VALUE_TYPE_STRING] = bt_value_generic_freeze, - [BT_VALUE_TYPE_ARRAY] = bt_value_array_freeze, - [BT_VALUE_TYPE_MAP] = bt_value_map_freeze, -}; - -static -void bt_value_destroy(struct bt_object *obj) -{ - struct bt_value *value; - - value = container_of(obj, struct bt_value, base); - BT_LOGD("Destroying value: addr=%p", value); - - if (bt_value_is_null(value)) { - BT_LOGD_STR("Not destroying the null value singleton."); - return; - } - - if (destroy_funcs[value->type]) { - destroy_funcs[value->type](value); - } - - g_free(value); -} - -BT_HIDDEN -enum bt_value_status _bt_value_freeze(const struct bt_value *c_object) -{ - const struct bt_value *object = (void *) c_object; - enum bt_value_status ret = BT_VALUE_STATUS_OK; - - BT_ASSERT(object); - - if (object->frozen) { - goto end; - } - - BT_LOGD("Freezing value: addr=%p", object); - freeze_funcs[object->type]((void *) object); - -end: - return ret; -} - -enum bt_value_type bt_value_get_type(const struct bt_value *object) -{ - BT_ASSERT_PRE_NON_NULL(object, "Value object"); - return object->type; -} - -static -struct bt_value bt_value_create_base(enum bt_value_type type) -{ - struct bt_value value; - - value.type = type; - value.frozen = BT_FALSE; - bt_object_init_shared(&value.base, bt_value_destroy); - return value; -} - -struct bt_value *bt_value_bool_create_init(bt_bool val) -{ - struct bt_value_bool *bool_obj; - - BT_LOGD("Creating boolean value object: val=%d", val); - bool_obj = g_new0(struct bt_value_bool, 1); - if (!bool_obj) { - BT_LOGE_STR("Failed to allocate one boolean value object."); - goto end; - } - - bool_obj->base = bt_value_create_base(BT_VALUE_TYPE_BOOL); - bool_obj->value = val; - BT_LOGD("Created boolean value object: addr=%p", bool_obj); - -end: - return (void *) bool_obj; -} - -struct bt_value *bt_value_bool_create(void) -{ - return bt_value_bool_create_init(BT_FALSE); -} - -static inline -struct bt_value *bt_value_integer_create_init(enum bt_value_type type, - uint64_t uval) -{ - struct bt_value_integer *integer_obj; - - BT_ASSERT(type == BT_VALUE_TYPE_UNSIGNED_INTEGER || - type == BT_VALUE_TYPE_SIGNED_INTEGER); - - if (type == BT_VALUE_TYPE_UNSIGNED_INTEGER) { - BT_LOGD("Creating unsigned integer value object: val=%" PRIu64, - uval); - } else { - BT_LOGD("Creating signed integer value object: val=%" PRId64, - (int64_t) uval); - } - - integer_obj = g_new0(struct bt_value_integer, 1); - if (!integer_obj) { - BT_LOGE_STR("Failed to allocate one integer value object."); - goto end; - } - - integer_obj->base = bt_value_create_base(type); - integer_obj->value.u = uval; - BT_LOGD("Created %ssigned integer value object: addr=%p", - type == BT_VALUE_TYPE_UNSIGNED_INTEGER ? "un" : "", - integer_obj); - -end: - return (void *) integer_obj; -} - -struct bt_value *bt_value_unsigned_integer_create_init(uint64_t val) -{ - return bt_value_integer_create_init(BT_VALUE_TYPE_UNSIGNED_INTEGER, - val); -} - -struct bt_value *bt_value_unsigned_integer_create(void) -{ - return bt_value_unsigned_integer_create_init(0); -} - -struct bt_value *bt_value_signed_integer_create_init(int64_t val) -{ - return bt_value_integer_create_init(BT_VALUE_TYPE_SIGNED_INTEGER, - (uint64_t) val); -} - -struct bt_value *bt_value_signed_integer_create(void) -{ - return bt_value_signed_integer_create_init(0); -} - -struct bt_value *bt_value_real_create_init(double val) -{ - struct bt_value_real *real_obj; - - BT_LOGD("Creating real number value object: val=%f", val); - real_obj = g_new0(struct bt_value_real, 1); - if (!real_obj) { - BT_LOGE_STR("Failed to allocate one real number value object."); - goto end; - } - - real_obj->base = bt_value_create_base(BT_VALUE_TYPE_REAL); - real_obj->value = val; - BT_LOGD("Created real number value object: addr=%p", - real_obj); - -end: - return (void *) real_obj; -} - -struct bt_value *bt_value_real_create(void) -{ - return bt_value_real_create_init(0.); -} - -struct bt_value *bt_value_string_create_init(const char *val) -{ - struct bt_value_string *string_obj = NULL; - - if (!val) { - BT_LOGW_STR("Invalid parameter: value is NULL."); - goto end; - } - - BT_LOGD("Creating string value object: val-len=%zu", strlen(val)); - string_obj = g_new0(struct bt_value_string, 1); - if (!string_obj) { - BT_LOGE_STR("Failed to allocate one string object."); - goto end; - } - - string_obj->base = bt_value_create_base(BT_VALUE_TYPE_STRING); - string_obj->gstr = g_string_new(val); - if (!string_obj->gstr) { - BT_LOGE_STR("Failed to allocate a GString."); - g_free(string_obj); - string_obj = NULL; - goto end; - } - - BT_LOGD("Created string value object: addr=%p", - string_obj); - -end: - return (void *) string_obj; -} - -struct bt_value *bt_value_string_create(void) -{ - return bt_value_string_create_init(""); -} - -struct bt_value *bt_value_array_create(void) -{ - struct bt_value_array *array_obj; - - BT_LOGD_STR("Creating empty array value object."); - array_obj = g_new0(struct bt_value_array, 1); - if (!array_obj) { - BT_LOGE_STR("Failed to allocate one array object."); - goto end; - } - - array_obj->base = bt_value_create_base(BT_VALUE_TYPE_ARRAY); - array_obj->garray = bt_g_ptr_array_new_full(0, - (GDestroyNotify) bt_object_put_ref); - if (!array_obj->garray) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - g_free(array_obj); - array_obj = NULL; - goto end; - } - - BT_LOGD("Created array value object: addr=%p", - array_obj); - -end: - return (void *) array_obj; -} - -struct bt_value *bt_value_map_create(void) -{ - struct bt_value_map *map_obj; - - BT_LOGD_STR("Creating empty map value object."); - map_obj = g_new0(struct bt_value_map, 1); - if (!map_obj) { - BT_LOGE_STR("Failed to allocate one map object."); - goto end; - } - - map_obj->base = bt_value_create_base(BT_VALUE_TYPE_MAP); - map_obj->ght = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) bt_object_put_ref); - if (!map_obj->ght) { - BT_LOGE_STR("Failed to allocate a GHashTable."); - g_free(map_obj); - map_obj = NULL; - goto end; - } - - BT_LOGD("Created map value object: addr=%p", - map_obj); - -end: - return (void *) map_obj; -} - -bt_bool bt_value_bool_get(const struct bt_value *bool_obj) -{ - BT_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_VALUE_TYPE_BOOL); - return BT_VALUE_TO_BOOL(bool_obj)->value; -} - -void bt_value_bool_set(struct bt_value *bool_obj, bt_bool val) -{ - BT_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_VALUE_TYPE_BOOL); - BT_ASSERT_PRE_VALUE_HOT(bool_obj, "Value object"); - BT_VALUE_TO_BOOL(bool_obj)->value = val; - BT_LOGV("Set boolean value's raw value: value-addr=%p, value=%d", - bool_obj, val); -} - -uint64_t bt_value_unsigned_integer_get(const struct bt_value *integer_obj) -{ - BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, - BT_VALUE_TYPE_UNSIGNED_INTEGER); - return BT_VALUE_TO_INTEGER(integer_obj)->value.u; -} - -int64_t bt_value_signed_integer_get(const struct bt_value *integer_obj) -{ - BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, - BT_VALUE_TYPE_SIGNED_INTEGER); - return BT_VALUE_TO_INTEGER(integer_obj)->value.i; -} - -static inline -void bt_value_integer_set(struct bt_value *integer_obj, - enum bt_value_type expected_type, uint64_t uval) -{ - BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, expected_type); - BT_ASSERT_PRE_VALUE_HOT(integer_obj, "Value object"); - BT_VALUE_TO_INTEGER(integer_obj)->value.u = uval; -} - -void bt_value_unsigned_integer_set(struct bt_value *integer_obj, - uint64_t val) -{ - bt_value_integer_set(integer_obj, BT_VALUE_TYPE_UNSIGNED_INTEGER, val); - BT_LOGV("Set unsigned integer value's raw value: " - "value-addr=%p, value=%" PRIu64, integer_obj, val); -} - -void bt_value_signed_integer_set(struct bt_value *integer_obj, - int64_t val) -{ - bt_value_integer_set(integer_obj, BT_VALUE_TYPE_SIGNED_INTEGER, - (uint64_t) val); - BT_LOGV("Set signed integer value's raw value: " - "value-addr=%p, value=%" PRId64, integer_obj, val); -} - -double bt_value_real_get(const struct bt_value *real_obj) -{ - BT_ASSERT_PRE_NON_NULL(real_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_VALUE_TYPE_REAL); - return BT_VALUE_TO_REAL(real_obj)->value; -} - -void bt_value_real_set(struct bt_value *real_obj, double val) -{ - BT_ASSERT_PRE_NON_NULL(real_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_VALUE_TYPE_REAL); - BT_ASSERT_PRE_VALUE_HOT(real_obj, "Value object"); - BT_VALUE_TO_REAL(real_obj)->value = val; - BT_LOGV("Set real number value's raw value: value-addr=%p, value=%f", - real_obj, val); -} - -const char *bt_value_string_get(const struct bt_value *string_obj) -{ - BT_ASSERT_PRE_NON_NULL(string_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_VALUE_TYPE_STRING); - return BT_VALUE_TO_STRING(string_obj)->gstr->str; -} - -enum bt_value_status bt_value_string_set( - struct bt_value *string_obj, const char *val) -{ - BT_ASSERT_PRE_NON_NULL(string_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_VALUE_TYPE_STRING); - BT_ASSERT_PRE_VALUE_HOT(string_obj, "Value object"); - g_string_assign(BT_VALUE_TO_STRING(string_obj)->gstr, val); - BT_LOGV("Set string value's raw value: value-addr=%p, raw-value-addr=%p", - string_obj, val); - return BT_VALUE_STATUS_OK; -} - -uint64_t bt_value_array_get_size(const struct bt_value *array_obj) -{ - BT_ASSERT_PRE_NON_NULL(array_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); - return (uint64_t) BT_VALUE_TO_ARRAY(array_obj)->garray->len; -} - -struct bt_value *bt_value_array_borrow_element_by_index( - struct bt_value *array_obj, uint64_t index) -{ - struct bt_value_array *typed_array_obj = - BT_VALUE_TO_ARRAY(array_obj); - - BT_ASSERT_PRE_NON_NULL(array_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); - BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, - typed_array_obj->garray->len); - return g_ptr_array_index(typed_array_obj->garray, index); -} - -const struct bt_value *bt_value_array_borrow_element_by_index_const( - const struct bt_value *array_obj, - uint64_t index) -{ - return bt_value_array_borrow_element_by_index( - (void *) array_obj, index); -} - -enum bt_value_status bt_value_array_append_element( - struct bt_value *array_obj, - struct bt_value *element_obj) -{ - struct bt_value_array *typed_array_obj = - BT_VALUE_TO_ARRAY(array_obj); - - BT_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); - BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); - BT_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); - g_ptr_array_add(typed_array_obj->garray, element_obj); - bt_object_get_ref(element_obj); - BT_LOGV("Appended element to array value: array-value-addr=%p, " - "element-value-addr=%p, new-size=%u", - array_obj, element_obj, typed_array_obj->garray->len); - return BT_VALUE_STATUS_OK; -} - -enum bt_value_status bt_value_array_append_bool_element( - struct bt_value *array_obj, bt_bool val) -{ - enum bt_value_status ret; - struct bt_value *bool_obj = NULL; - - bool_obj = bt_value_bool_create_init(val); - ret = bt_value_array_append_element(array_obj, - (void *) bool_obj); - bt_object_put_ref(bool_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_unsigned_integer_element( - struct bt_value *array_obj, uint64_t val) -{ - enum bt_value_status ret; - struct bt_value *integer_obj = NULL; - - integer_obj = bt_value_unsigned_integer_create_init(val); - ret = bt_value_array_append_element(array_obj, - (void *) integer_obj); - bt_object_put_ref(integer_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_signed_integer_element( - struct bt_value *array_obj, int64_t val) -{ - enum bt_value_status ret; - struct bt_value *integer_obj = NULL; - - integer_obj = bt_value_signed_integer_create_init(val); - ret = bt_value_array_append_element(array_obj, - (void *) integer_obj); - bt_object_put_ref(integer_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_real_element( - struct bt_value *array_obj, double val) -{ - enum bt_value_status ret; - struct bt_value *real_obj = NULL; - - real_obj = bt_value_real_create_init(val); - ret = bt_value_array_append_element(array_obj, - (void *) real_obj); - bt_object_put_ref(real_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_string_element( - struct bt_value *array_obj, const char *val) -{ - enum bt_value_status ret; - struct bt_value *string_obj = NULL; - - string_obj = bt_value_string_create_init(val); - ret = bt_value_array_append_element(array_obj, - (void *) string_obj); - bt_object_put_ref(string_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_empty_array_element( - struct bt_value *array_obj) -{ - enum bt_value_status ret; - struct bt_value *empty_array_obj = NULL; - - empty_array_obj = bt_value_array_create(); - ret = bt_value_array_append_element(array_obj, - (void *) empty_array_obj); - bt_object_put_ref(empty_array_obj); - return ret; -} - -enum bt_value_status bt_value_array_append_empty_map_element( - struct bt_value *array_obj) -{ - enum bt_value_status ret; - struct bt_value *map_obj = NULL; - - map_obj = bt_value_map_create(); - ret = bt_value_array_append_element(array_obj, - (void *) map_obj); - bt_object_put_ref(map_obj); - return ret; -} - -enum bt_value_status bt_value_array_set_element_by_index( - struct bt_value *array_obj, uint64_t index, - struct bt_value *element_obj) -{ - struct bt_value_array *typed_array_obj = - BT_VALUE_TO_ARRAY(array_obj); - - BT_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); - BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); - BT_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); - BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, - typed_array_obj->garray->len); - bt_object_put_ref(g_ptr_array_index(typed_array_obj->garray, index)); - g_ptr_array_index(typed_array_obj->garray, index) = element_obj; - bt_object_get_ref(element_obj); - BT_LOGV("Set array value's element: array-value-addr=%p, " - "index=%" PRIu64 ", element-value-addr=%p", - array_obj, index, element_obj); - return BT_VALUE_STATUS_OK; -} - -uint64_t bt_value_map_get_size(const struct bt_value *map_obj) -{ - BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); - return (uint64_t) g_hash_table_size(BT_VALUE_TO_MAP(map_obj)->ght); -} - -struct bt_value *bt_value_map_borrow_entry_value(struct bt_value *map_obj, - const char *key) -{ - BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_ASSERT_PRE_NON_NULL(key, "Key"); - BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); - return g_hash_table_lookup(BT_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key))); -} - -const struct bt_value *bt_value_map_borrow_entry_value_const( - const struct bt_value *map_obj, const char *key) -{ - return bt_value_map_borrow_entry_value((void *) map_obj, key); -} - -bt_bool bt_value_map_has_entry(const struct bt_value *map_obj, const char *key) -{ - BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_ASSERT_PRE_NON_NULL(key, "Key"); - BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); - return bt_g_hash_table_contains(BT_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key))); -} - -enum bt_value_status bt_value_map_insert_entry( - struct bt_value *map_obj, - const char *key, struct bt_value *element_obj) -{ - BT_ASSERT_PRE_NON_NULL(map_obj, "Map value object"); - BT_ASSERT_PRE_NON_NULL(key, "Key"); - BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); - BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); - BT_ASSERT_PRE_VALUE_HOT(map_obj, "Map value object"); - g_hash_table_insert(BT_VALUE_TO_MAP(map_obj)->ght, - GUINT_TO_POINTER(g_quark_from_string(key)), element_obj); - bt_object_get_ref(element_obj); - BT_LOGV("Inserted value into map value: map-value-addr=%p, " - "key=\"%s\", element-value-addr=%p", - map_obj, key, element_obj); - return BT_VALUE_STATUS_OK; -} - -enum bt_value_status bt_value_map_insert_bool_entry( - struct bt_value *map_obj, const char *key, bt_bool val) -{ - enum bt_value_status ret; - struct bt_value *bool_obj = NULL; - - bool_obj = bt_value_bool_create_init(val); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) bool_obj); - bt_object_put_ref(bool_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_unsigned_integer_entry( - struct bt_value *map_obj, const char *key, uint64_t val) -{ - enum bt_value_status ret; - struct bt_value *integer_obj = NULL; - - integer_obj = bt_value_unsigned_integer_create_init(val); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) integer_obj); - bt_object_put_ref(integer_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_signed_integer_entry( - struct bt_value *map_obj, const char *key, int64_t val) -{ - enum bt_value_status ret; - struct bt_value *integer_obj = NULL; - - integer_obj = bt_value_signed_integer_create_init(val); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) integer_obj); - bt_object_put_ref(integer_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_real_entry( - struct bt_value *map_obj, const char *key, double val) -{ - enum bt_value_status ret; - struct bt_value *real_obj = NULL; - - real_obj = bt_value_real_create_init(val); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) real_obj); - bt_object_put_ref(real_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_string_entry( - struct bt_value *map_obj, const char *key, - const char *val) -{ - enum bt_value_status ret; - struct bt_value *string_obj = NULL; - - string_obj = bt_value_string_create_init(val); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) string_obj); - bt_object_put_ref(string_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_empty_array_entry( - struct bt_value *map_obj, const char *key) -{ - enum bt_value_status ret; - struct bt_value *array_obj = NULL; - - array_obj = bt_value_array_create(); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) array_obj); - bt_object_put_ref(array_obj); - return ret; -} - -enum bt_value_status bt_value_map_insert_empty_map_entry( - struct bt_value *map_obj, const char *key) -{ - enum bt_value_status ret; - struct bt_value *empty_map_obj = NULL; - - empty_map_obj = bt_value_map_create(); - ret = bt_value_map_insert_entry(map_obj, key, - (void *) empty_map_obj); - bt_object_put_ref(empty_map_obj); - return ret; -} - -enum bt_value_status bt_value_map_foreach_entry(struct bt_value *map_obj, - bt_value_map_foreach_entry_func func, void *data) -{ - enum bt_value_status ret = BT_VALUE_STATUS_OK; - gpointer key, element_obj; - GHashTableIter iter; - struct bt_value_map *typed_map_obj = BT_VALUE_TO_MAP(map_obj); - - BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); - BT_ASSERT_PRE_NON_NULL(func, "Callback"); - BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); - g_hash_table_iter_init(&iter, typed_map_obj->ght); - - while (g_hash_table_iter_next(&iter, &key, &element_obj)) { - const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); - - if (!func(key_str, element_obj, data)) { - BT_LOGV("User canceled the loop: key=\"%s\", " - "value-addr=%p, data=%p", - key_str, element_obj, data); - ret = BT_VALUE_STATUS_CANCELED; - break; - } - } - - return ret; -} - -enum bt_value_status bt_value_map_foreach_entry_const( - const struct bt_value *map_obj, - bt_value_map_foreach_entry_const_func func, void *data) -{ - return bt_value_map_foreach_entry((void *) map_obj, - (bt_value_map_foreach_entry_func) func, data); -} - -struct extend_map_element_data { - struct bt_value *extended_obj; - enum bt_value_status status; -}; - -static -bt_bool extend_map_element(const char *key, - const struct bt_value *extension_obj_elem, void *data) -{ - bt_bool ret = BT_TRUE; - struct extend_map_element_data *extend_data = data; - struct bt_value *extension_obj_elem_copy = NULL; - - /* Copy object which is to replace the current one */ - extend_data->status = bt_value_copy(extension_obj_elem, - &extension_obj_elem_copy); - if (extend_data->status) { - BT_LOGE("Cannot copy map element: addr=%p", - extension_obj_elem); - goto error; - } - - BT_ASSERT(extension_obj_elem_copy); - - /* Replace in extended object */ - extend_data->status = bt_value_map_insert_entry( - extend_data->extended_obj, key, - (void *) extension_obj_elem_copy); - if (extend_data->status) { - BT_LOGE("Cannot replace value in extended value: key=\"%s\", " - "extended-value-addr=%p, element-value-addr=%p", - key, extend_data->extended_obj, - extension_obj_elem_copy); - goto error; - } - - goto end; - -error: - BT_ASSERT(extend_data->status != BT_VALUE_STATUS_OK); - ret = BT_FALSE; - -end: - BT_OBJECT_PUT_REF_AND_RESET(extension_obj_elem_copy); - return ret; -} - -enum bt_value_status bt_value_map_extend( - const struct bt_value *base_map_obj, - const struct bt_value *extension_obj, - struct bt_value **extended_map_obj) -{ - struct extend_map_element_data extend_data = { - .extended_obj = NULL, - .status = BT_VALUE_STATUS_OK, - }; - - BT_ASSERT_PRE_NON_NULL(base_map_obj, "Base value object"); - BT_ASSERT_PRE_NON_NULL(extension_obj, "Extension value object"); - BT_ASSERT_PRE_NON_NULL(extended_map_obj, - "Extended value object (output)"); - BT_ASSERT_PRE_VALUE_IS_TYPE(base_map_obj, BT_VALUE_TYPE_MAP); - BT_ASSERT_PRE_VALUE_IS_TYPE(extension_obj, BT_VALUE_TYPE_MAP); - BT_LOGD("Extending map value: base-value-addr=%p, extension-value-addr=%p", - base_map_obj, extension_obj); - *extended_map_obj = NULL; - - /* Create copy of base map object to start with */ - extend_data.status = bt_value_copy(base_map_obj, extended_map_obj); - if (extend_data.status) { - BT_LOGE("Cannot copy base value: base-value-addr=%p", - base_map_obj); - goto error; - } - - BT_ASSERT(extended_map_obj); - - /* - * For each key in the extension map object, replace this key - * in the copied map object. - */ - extend_data.extended_obj = *extended_map_obj; - - if (bt_value_map_foreach_entry_const(extension_obj, extend_map_element, - &extend_data)) { - BT_LOGE("Cannot iterate on the extension object's elements: " - "extension-value-addr=%p", extension_obj); - goto error; - } - - if (extend_data.status) { - BT_LOGE("Failed to successfully iterate on the extension object's elements: " - "extension-value-addr=%p", extension_obj); - goto error; - } - - BT_LOGD("Extended map value: extended-value-addr=%p", - *extended_map_obj); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(*extended_map_obj); - *extended_map_obj = NULL; - -end: - return extend_data.status; -} - -enum bt_value_status bt_value_copy(const struct bt_value *object, - struct bt_value **copy_obj) -{ - enum bt_value_status status = BT_VALUE_STATUS_OK; - - BT_ASSERT_PRE_NON_NULL(object, "Value object"); - BT_ASSERT_PRE_NON_NULL(copy_obj, "Value object copy (output)"); - BT_LOGD("Copying value object: addr=%p", object); - *copy_obj = copy_funcs[object->type](object); - if (*copy_obj) { - BT_LOGD("Copied value object: copy-value-addr=%p", - copy_obj); - } else { - status = BT_VALUE_STATUS_NOMEM; - *copy_obj = NULL; - BT_LOGE_STR("Failed to copy value object."); - } - - return status; -} - -bt_bool bt_value_compare(const struct bt_value *object_a, - const struct bt_value *object_b) -{ - bt_bool ret = BT_FALSE; - - BT_ASSERT_PRE_NON_NULL(object_a, "Value object A"); - BT_ASSERT_PRE_NON_NULL(object_b, "Value object B"); - - if (object_a->type != object_b->type) { - BT_LOGV("Values are different: type mismatch: " - "value-a-addr=%p, value-b-addr=%p, " - "value-a-type=%s, value-b-type=%s", - object_a, object_b, - bt_common_value_type_string(object_a->type), - bt_common_value_type_string(object_b->type)); - goto end; - } - - ret = compare_funcs[object_a->type](object_a, object_b); - -end: - return ret; -} - -void bt_value_get_ref(const struct bt_value *value) -{ - bt_object_get_ref(value); -} - -void bt_value_put_ref(const struct bt_value *value) -{ - bt_object_put_ref(value); -} diff --git a/logging/LICENSE b/logging/LICENSE deleted file mode 100644 index 5569c1d5..00000000 --- a/logging/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 wonder-mice - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/logging/Makefile.am b/logging/Makefile.am deleted file mode 100644 index 559ea22f..00000000 --- a/logging/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -noinst_LTLIBRARIES = libbabeltrace2-logging.la - -libbabeltrace2_logging_la_SOURCES = log.c diff --git a/logging/log.c b/logging/log.c deleted file mode 100644 index a7946ca6..00000000 --- a/logging/log.c +++ /dev/null @@ -1,1419 +0,0 @@ -/* - * This is zf_log.c, modified with Babeltrace prefixes. - * See . - * See LICENSE. - */ - -#include -#include -#include -#include - -#ifdef __CYGWIN__ -extern unsigned long pthread_getsequence_np(pthread_t *); -#endif - -/* When defined, Android log (android/log.h) will be used by default instead of - * stderr (ignored on non-Android platforms). Date, time, pid and tid (context) - * will be provided by Android log. Android log features will be used to output - * log level and tag. - */ -#ifdef BT_LOG_USE_ANDROID_LOG - #undef BT_LOG_USE_ANDROID_LOG - #if defined(__ANDROID__) - #define BT_LOG_USE_ANDROID_LOG 1 - #else - #define BT_LOG_USE_ANDROID_LOG 0 - #endif -#else - #define BT_LOG_USE_ANDROID_LOG 0 -#endif -/* When defined, NSLog (uses Apple System Log) will be used instead of stderr - * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be - * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on - * non-public CFLog() function. Both use Apple System Log internally, but it's - * easier to call CFLog() from C than NSLog(). Current implementation doesn't - * support "%@" format specifier. - */ -#ifdef BT_LOG_USE_NSLOG - #undef BT_LOG_USE_NSLOG - #if defined(__APPLE__) && defined(__MACH__) - #define BT_LOG_USE_NSLOG 1 - #else - #define BT_LOG_USE_NSLOG 0 - #endif -#else - #define BT_LOG_USE_NSLOG 0 -#endif -/* When defined, OutputDebugString() will be used instead of stderr (ignored on - * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with - * UTF-8 data. - */ -#ifdef BT_LOG_USE_DEBUGSTRING - #undef BT_LOG_USE_DEBUGSTRING - #if defined(_WIN32) || defined(_WIN64) - #define BT_LOG_USE_DEBUGSTRING 1 - #else - #define BT_LOG_USE_DEBUGSTRING 0 - #endif -#else - #define BT_LOG_USE_DEBUGSTRING 0 -#endif -/* When defined, bt_log library will not contain definition of tag prefix - * variable. In that case it must be defined elsewhere using - * BT_LOG_DEFINE_TAG_PREFIX macro, for example: - * - * BT_LOG_DEFINE_TAG_PREFIX = "ProcessName"; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef BT_LOG_EXTERN_TAG_PREFIX - #undef BT_LOG_EXTERN_TAG_PREFIX - #define BT_LOG_EXTERN_TAG_PREFIX 1 -#else - #define BT_LOG_EXTERN_TAG_PREFIX 0 -#endif -/* When defined, bt_log library will not contain definition of global format - * variable. In that case it must be defined elsewhere using - * BT_LOG_DEFINE_GLOBAL_FORMAT macro, for example: - * - * BT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH}; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef BT_LOG_EXTERN_GLOBAL_FORMAT - #undef BT_LOG_EXTERN_GLOBAL_FORMAT - #define BT_LOG_EXTERN_GLOBAL_FORMAT 1 -#else - #define BT_LOG_EXTERN_GLOBAL_FORMAT 0 -#endif -/* When defined, bt_log library will not contain definition of global output - * variable. In that case it must be defined elsewhere using - * BT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example: - * - * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback}; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT - #undef BT_LOG_EXTERN_GLOBAL_OUTPUT - #define BT_LOG_EXTERN_GLOBAL_OUTPUT 1 -#else - #define BT_LOG_EXTERN_GLOBAL_OUTPUT 0 -#endif -/* When defined, bt_log library will not contain definition of global output - * level variable. In that case it must be defined elsewhere using - * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example: - * - * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_WARN; - * - * This allows to specify custom value for static initialization and avoid - * overhead of setting this value in runtime. - */ -#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - #undef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1 -#else - #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0 -#endif -/* When defined, implementation will prefer smaller code size over speed. - * Very rough estimate is that code will be up to 2x smaller and up to 2x - * slower. Disabled by default. - */ -#ifdef BT_LOG_OPTIMIZE_SIZE - #undef BT_LOG_OPTIMIZE_SIZE - #define BT_LOG_OPTIMIZE_SIZE 1 -#else - #define BT_LOG_OPTIMIZE_SIZE 0 -#endif -/* Size of the log line buffer. The buffer is a globally allocated per thread. - */ -#ifndef BT_LOG_BUF_SZ - #define BT_LOG_BUF_SZ (4 * 4096) -#endif -/* Default number of bytes in one line of memory output. For large values - * BT_LOG_BUF_SZ also must be increased. - */ -#ifndef BT_LOG_MEM_WIDTH - #define BT_LOG_MEM_WIDTH 32 -#endif -/* String to put in the end of each log line (can be empty). Its value used by - * stderr output callback. Its size used as a default value for BT_LOG_EOL_SZ. - */ -#ifndef BT_LOG_EOL - #define BT_LOG_EOL "\n" -#endif -/* Default delimiter that separates parts of log message. Can NOT contain '%' - * or '\0'. - * - * Log message format specifications can override (or ignore) this value. For - * more details see BT_LOG_MESSAGE_CTX_FORMAT, BT_LOG_MESSAGE_SRC_FORMAT and - * BT_LOG_MESSAGE_TAG_FORMAT. - */ -#ifndef BT_LOG_DEF_DELIMITER - #define BT_LOG_DEF_DELIMITER " " -#endif -/* Specifies log message context format. Log message context includes date, - * time, process id, thread id and message's log level. Custom information can - * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, - * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements), - * F_UINT(width, value). - * - * Must be defined as a tuple, for example: - * - * #define BT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, S(" > ")) - * - * In that case, resulting log message will be: - * - * 2016.12.22 > TAG function@filename.c:line Message text - * - * Note, that tag, source location and message text are not impacted by - * this setting. See BT_LOG_MESSAGE_TAG_FORMAT and BT_LOG_MESSAGE_SRC_FORMAT. - * - * If message context must be visually separated from the rest of the message, - * it must be reflected in context format (notice trailing S(" > ") in the - * example above). - * - * S(str) adds constant string str. String can NOT contain '%' or '\0'. - * - * F_INIT(statements) adds initialization statement(s) that will be evaluated - * once for each log message. All statements are evaluated in specified order. - * Several F_INIT() fields can be used in every log message format - * specification. Fields, like F_UINT(width, value), are allowed to use results - * of initialization statements. If statement introduces variables (or other - * names, like structures) they must be prefixed with "f_". Statements must be - * enclosed into additional "()". Example: - * - * #define BT_LOG_MESSAGE_CTX_FORMAT \ - * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \ - * YEAR, S("."), MONTH, S("."), DAY, S(" "), \ - * F_UINT(5, f_ru.ru_nsignals), \ - * S(" ")) - * - * F_UINT(width, value) adds unsigned integer value extended with up to width - * spaces (for alignment purposes). Value can be any expression that evaluates - * to unsigned integer. If expression contains non-standard functions, they - * must be declared with F_INIT(). Example: - * - * #define BT_LOG_MESSAGE_CTX_FORMAT \ - * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \ - * F_INIT(( unsigned tickcount(); )), \ - * F_UINT(5, tickcount()), \ - * S(" ")) - * - * Other log message format specifications follow same rules, but have a - * different set of supported fields. - */ -#ifndef BT_LOG_MESSAGE_CTX_FORMAT - #define BT_LOG_MESSAGE_CTX_FORMAT \ - (MONTH, S("-"), DAY, S(BT_LOG_DEF_DELIMITER), \ - HOUR, S(":"), MINUTE, S(":"), SECOND, S("."), MILLISECOND, S(BT_LOG_DEF_DELIMITER), \ - PID, S(BT_LOG_DEF_DELIMITER), TID, S(BT_LOG_DEF_DELIMITER), \ - LEVEL, S(BT_LOG_DEF_DELIMITER)) -#endif -/* Example: - */ -/* Specifies log message tag format. It includes tag prefix and tag. Custom - * information can be added as well. Supported fields: - * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements), - * F_UINT(width, value). - * - * TAG(prefix_delimiter, tag_delimiter) adds following string to log message: - * - * PREFIXTAG - * - * Prefix delimiter will be used only when prefix is not empty. Tag delimiter - * will be used only when prefixed tag is not empty. Example: - * - * #define BT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] ")) - * - * See BT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#ifndef BT_LOG_MESSAGE_TAG_FORMAT - #define BT_LOG_MESSAGE_TAG_FORMAT \ - (TAG(".", BT_LOG_DEF_DELIMITER)) -#endif -/* Specifies log message source location format. It includes function name, - * file name and file line. Custom information can be added as well. Supported - * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements), - * F_UINT(width, value). - * - * See BT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#ifndef BT_LOG_MESSAGE_SRC_FORMAT - #define BT_LOG_MESSAGE_SRC_FORMAT \ - (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(BT_LOG_DEF_DELIMITER)) -#endif -/* Fields that can be used in log message format specifications (see above). - * Mentioning them here explicitly, so we know that nobody else defined them - * before us. See BT_LOG_MESSAGE_CTX_FORMAT for details. - */ -#define YEAR YEAR -#define MONTH MONTH -#define DAY DAY -#define MINUTE MINUTE -#define SECOND SECOND -#define MILLISECOND MILLISECOND -#define PID PID -#define TID TID -#define LEVEL LEVEL -#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim) -#define FUNCTION FUNCTION -#define FILENAME FILENAME -#define FILELINE FILELINE -#define S(str) S(str) -#define F_INIT(statements) F_INIT(statements) -#define F_UINT(width, value) F_UINT(width, value) -/* Number of bytes to reserve for EOL in the log line buffer (must be >0). - * Must be larger than or equal to length of BT_LOG_EOL with terminating null. - */ -#ifndef BT_LOG_EOL_SZ - #define BT_LOG_EOL_SZ sizeof(BT_LOG_EOL) -#endif -/* Compile instrumented version of the library to facilitate unit testing. - */ -#ifndef BT_LOG_INSTRUMENTED - #define BT_LOG_INSTRUMENTED 0 -#endif - -#if defined(__linux__) - #if !defined(__ANDROID__) && !defined(_GNU_SOURCE) - #define _GNU_SOURCE - #endif -#endif -#if defined(__MINGW32__) - #ifdef __STRICT_ANSI__ - #undef __STRICT_ANSI__ - #endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#define BT_LOG_OUTPUT_LEVEL dummy - -#include -#include - -#if defined(_WIN32) || defined(_WIN64) - #include -#else - #include - #include - #if defined(__linux__) - #include - #elif (defined(__sun__) || defined(__CYGWIN__)) - /* Solaris and Cygwin have no sys/syslimits.h */ - #else - #include - #endif -#endif - -#if defined(__linux__) - #include - #include - #if !defined(__ANDROID__) - #include - #endif -#endif -#if defined(__MACH__) - #include -#endif - -#define INLINE _BT_LOG_INLINE -#define VAR_UNUSED(var) (void)var -#define RETVAL_UNUSED(expr) do { while(expr) break; } while(0) -#define STATIC_ASSERT(name, cond) \ - typedef char assert_##name[(cond)? 1: -1] -#define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) -#ifndef _countof - #define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) -#endif - -#if BT_LOG_INSTRUMENTED - #define INSTRUMENTED_CONST -#else - #define INSTRUMENTED_CONST const -#endif - -#define _PP_PASTE_2(a, b) a ## b -#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) - -#define _PP_PASTE_3(a, b, c) a ## b ## c -#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) - -/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ - * as a single token and requires additional expansion to realize that it's - * actually a list. If not for it, there would be no need in this extra - * expansion. - */ -#define _PP_ID(x) x -#define _PP_NARGS_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,...) _24 -#define _PP_NARGS(...) _PP_ID(_PP_NARGS_N(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) - -/* There is a more efficient way to implement this, but it requires - * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't - * have one. - */ -#define _PP_HEAD__(x, ...) x -#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) -#define _PP_HEAD(xs) _PP_HEAD_ xs -#define _PP_TAIL_(x, ...) (__VA_ARGS__) -#define _PP_TAIL(xs) _PP_TAIL_ xs -#define _PP_UNTUPLE_(...) __VA_ARGS__ -#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs - -/* Apply function macro to each element in tuple. Output is not - * enforced to be a tuple. - */ -#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) -#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) -#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) -#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) -#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) -#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) -#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) -#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) -#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) -#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) -#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) -#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) -#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) -#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) -#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) -#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) -#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) -#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) -#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) -#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) -#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) -#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) -#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) -#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) -#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs) - -/* Apply function macro to each element in tuple in reverse order. - * Output is not enforced to be a tuple. - */ -#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) -#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) -#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs) - -/* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible - * fields must be mentioned here. Not counting F_INIT() here because it's - * somewhat special and is handled spearatly (at least for now). - */ -#define _BT_LOG_MESSAGE_FORMAT_MASK__ (0<<0) -#define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR (1<<1) -#define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH (1<<2) -#define _BT_LOG_MESSAGE_FORMAT_MASK__DAY (1<<3) -#define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR (1<<4) -#define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1<<5) -#define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND (1<<6) -#define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1<<7) -#define _BT_LOG_MESSAGE_FORMAT_MASK__PID (1<<8) -#define _BT_LOG_MESSAGE_FORMAT_MASK__TID (1<<9) -#define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1<<10) -#define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1<<11) -#define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1<<12) -#define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1<<13) -#define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1<<14) -#define _BT_LOG_MESSAGE_FORMAT_MASK__S(s) (1<<15) -#define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16) -#define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17) -#define _BT_LOG_MESSAGE_FORMAT_MASK(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field) - -/* Logical "or" of masks of fields used in specified format specification. - */ -#define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \ - (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format)) - -/* Expands to expressions that evaluates to true if field is used in - * specified format specification. Example: - * - * #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT) - * ... - * #endif - */ -#define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ - (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format)) - -/* Same, but checks all supported format specifications. - */ -#define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ - (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT)) - -#define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \ - (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT)) - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ - #define memccpy _memccpy -#endif - -#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || \ - (defined(__MINGW64__) && !defined(__USE_MINGW_ANSI_STDIO)) - #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) - static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) - { - const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); - return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */ - } - #if BT_LOG_OPTIMIZE_SIZE - #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) - static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) - { - va_list va; - va_start(va, fmt); - const int n = fake_vsnprintf(s, sz, fmt, va); - va_end(va); - return n; - } - #endif -#endif - -typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); -typedef void (*pid_cb)(int *const pid, int *const tid); -typedef void (*buffer_cb)(bt_log_message *msg, char *buf); - -typedef struct src_location -{ - const char *const func; - const char *const file; - const unsigned line; -} -src_location; - -typedef struct mem_block -{ - const void *const d; - const unsigned d_sz; -} -mem_block; - -static void time_callback(struct tm *const tm, unsigned *const usec); -static void pid_callback(int *const pid, int *const tid); -static void buffer_callback(bt_log_message *msg, char *buf); - -STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ); -STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ); -STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ); -static const char c_hex[] = "0123456789abcdef"; - -static __thread char logging_buf[4 * 4096]; - -static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ; -static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; -static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; -static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; - -#if BT_LOG_USE_ANDROID_LOG - #include - - static INLINE int android_lvl(const int lvl) - { - switch (lvl) - { - case BT_LOG_VERBOSE: - return ANDROID_LOG_VERBOSE; - case BT_LOG_DEBUG: - return ANDROID_LOG_DEBUG; - case BT_LOG_INFO: - return ANDROID_LOG_INFO; - case BT_LOG_WARN: - return ANDROID_LOG_WARN; - case BT_LOG_ERROR: - return ANDROID_LOG_ERROR; - case BT_LOG_FATAL: - return ANDROID_LOG_FATAL; - default: - ASSERT_UNREACHABLE("Bad log level"); - return ANDROID_LOG_UNKNOWN; - } - } - - static void out_android_callback(const bt_log_message *const msg, void *arg) - { - VAR_UNUSED(arg); - *msg->p = 0; - const char *tag = msg->p; - if (msg->tag_e != msg->tag_b) - { - tag = msg->tag_b; - *msg->tag_e = 0; - } - __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b); - } - - enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; - #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback -#endif - -#if BT_LOG_USE_NSLOG - #include - CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); - - static INLINE int apple_lvl(const int lvl) - { - switch (lvl) - { - case BT_LOG_VERBOSE: - return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; - case BT_LOG_DEBUG: - return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; - case BT_LOG_INFO: - return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */; - case BT_LOG_WARN: - return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */; - case BT_LOG_ERROR: - return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */; - case BT_LOG_FATAL: - return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; - default: - ASSERT_UNREACHABLE("Bad log level"); - return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; - } - } - - static void out_nslog_callback(const bt_log_message *const msg, void *arg) - { - VAR_UNUSED(arg); - *msg->p = 0; - CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); - } - - enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; - #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback -#endif - -#if BT_LOG_USE_DEBUGSTRING - #include - - static void out_debugstring_callback(const bt_log_message *const msg, void *arg) - { - VAR_UNUSED(arg); - msg->p[0] = '\n'; - msg->p[1] = '\0'; - OutputDebugStringA(msg->buf); - } - - enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD }; - #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback -#endif - -BT_HIDDEN -void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg) -{ - VAR_UNUSED(arg); - const size_t eol_len = sizeof(BT_LOG_EOL) - 1; - memcpy(msg->p, BT_LOG_EOL, eol_len); -#if defined(_WIN32) || defined(_WIN64) - /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and - without FILE_WRITE_DATA */ - WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, - (DWORD)(msg->p - msg->buf + eol_len), 0, 0); -#else - /* write() is atomic for buffers less than or equal to PIPE_BUF. */ - RETVAL_UNUSED(write(STDERR_FILENO, msg->buf, - (size_t)(msg->p - msg->buf) + eol_len)); -#endif -} - -static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR}; - -#if !BT_LOG_EXTERN_TAG_PREFIX - BT_LOG_DEFINE_TAG_PREFIX = 0; -#endif - -#if !BT_LOG_EXTERN_GLOBAL_FORMAT - BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH}; -#endif - -#if !BT_LOG_EXTERN_GLOBAL_OUTPUT - #if BT_LOG_USE_ANDROID_LOG - BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; - #elif BT_LOG_USE_NSLOG - BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; - #elif BT_LOG_USE_DEBUGSTRING - BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; - #else - BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR}; - #endif -#endif - -#if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; -#endif - -BT_HIDDEN -const bt_log_spec _bt_log_stderr_spec = -{ - BT_LOG_GLOBAL_FORMAT, - &out_stderr, -}; - -static const bt_log_spec global_spec = -{ - BT_LOG_GLOBAL_FORMAT, - BT_LOG_GLOBAL_OUTPUT, -}; - -#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT) -static char lvl_char(const int lvl) -{ - switch (lvl) - { - case BT_LOG_VERBOSE: - return 'V'; - case BT_LOG_DEBUG: - return 'D'; - case BT_LOG_INFO: - return 'I'; - case BT_LOG_WARN: - return 'W'; - case BT_LOG_ERROR: - return 'E'; - case BT_LOG_FATAL: - return 'F'; - default: - ASSERT_UNREACHABLE("Bad log level"); - return '?'; - } -} -#endif - -#define GCCVER_LESS(MAJOR, MINOR, PATCH) \ - (__GNUC__ < MAJOR || \ - (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ - (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH)))) - -#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0) - #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) - #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) - #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) - #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) - #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) - /* Note: will not store old value of *vp in *ep (non-standard behaviour) */ - #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ - __sync_bool_compare_and_swap(vp, *(ep), d) -#endif - -#if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) -#define TCACHE -#define TCACHE_STALE (0x40000000) -#define TCACHE_FLUID (0x40000000 | 0x80000000) -static unsigned g_tcache_mode = TCACHE_STALE; -static struct timeval g_tcache_tv = {0, 0}; -static struct tm g_tcache_tm = {0}; - -static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm) -{ - unsigned mode; - mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); - if (0 == (mode & TCACHE_FLUID)) - { - mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); - if (0 == (mode & TCACHE_FLUID)) - { - if (g_tcache_tv.tv_sec == tv->tv_sec) - { - *tm = g_tcache_tm; - __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); - return !0; - } - __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); - } - __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); - } - return 0; -} - -static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm) -{ - unsigned stale = TCACHE_STALE; - if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, - 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) - { - g_tcache_tv = *tv; - g_tcache_tm = *tm; - __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); - } -} -#endif - -static void time_callback(struct tm *const tm, unsigned *const msec) -{ -#if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED - VAR_UNUSED(tm); - VAR_UNUSED(msec); -#else - #if defined(_WIN32) || defined(_WIN64) - SYSTEMTIME st; - GetLocalTime(&st); - tm->tm_year = st.wYear; - tm->tm_mon = st.wMonth; - tm->tm_mday = st.wDay; - tm->tm_wday = st.wDayOfWeek; - tm->tm_hour = st.wHour; - tm->tm_min = st.wMinute; - tm->tm_sec = st.wSecond; - *msec = st.wMilliseconds; - #else - struct timeval tv; - gettimeofday(&tv, 0); - #ifndef TCACHE - localtime_r(&tv.tv_sec, tm); - #else - if (!tcache_get(&tv, tm)) - { - localtime_r(&tv.tv_sec, tm); - tcache_set(&tv, tm); - } - #endif - *msec = (unsigned)tv.tv_usec / 1000; - #endif -#endif -} - -static void pid_callback(int *const pid, int *const tid) -{ -#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(pid); -#else - #if defined(_WIN32) || defined(_WIN64) - *pid = GetCurrentProcessId(); - #else - *pid = getpid(); - #endif -#endif - -#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(tid); -#else - #if defined(_WIN32) || defined(_WIN64) - *tid = GetCurrentThreadId(); - #elif defined(__CYGWIN__) - pthread_t thr = pthread_self(); - *tid = (int)pthread_getsequence_np(&thr); - #elif defined(__sun__) - *tid = (int)pthread_self(); - #elif defined(__ANDROID__) - *tid = gettid(); - #elif defined(__linux__) - *tid = syscall(SYS_gettid); - #elif defined(__MACH__) - *tid = (int)pthread_mach_thread_np(pthread_self()); - #else - #define Platform not supported - #endif -#endif -} - -static void buffer_callback(bt_log_message *msg, char *buf) -{ - msg->e = (msg->p = msg->buf = buf) + g_buf_sz; -} - -#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) -static const char *funcname(const char *func) -{ - return func? func: ""; -} -#endif - -#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) -static const char *filename(const char *file) -{ - const char *f = file; - for (const char *p = file; 0 != *p; ++p) - { - if ('/' == *p || '\\' == *p) - { - f = p + 1; - } - } - return f; -} -#endif - -static INLINE size_t nprintf_size(bt_log_message *const msg) -{ - // *nprintf() always puts 0 in the end when input buffer is not empty. This - // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which - // leaves space for one more character. Some put_xxx() functions don't use - // *nprintf() and could use that last character. In that case log line will - // have multiple (two) half-written parts which is confusing. To workaround - // that we allow *nprintf() to write its 0 in the eol area (which is always - // not empty). - return (size_t)(msg->e - msg->p + 1); -} - -static INLINE void put_nprintf(bt_log_message *const msg, const int n) -{ - if (0 < n) - { - msg->p = n < msg->e - msg->p? msg->p + n: msg->e; - } -} - -static INLINE char *put_padding_r(const unsigned w, const char wc, - char *p, char *e) -{ - for (char *const b = e - w; b < p; *--p = wc) {} - return p; -} - -static char *put_integer_r(unsigned v, const int sign, - const unsigned w, const char wc, char *const e) -{ - static const char _signs[] = {'-', '0', '+'}; - const char *const signs = _signs + 1; - char *p = e; - do { *--p = '0' + v % 10; } while (0 != (v /= 10)); - if (0 == sign) return put_padding_r(w, wc, p, e); - if ('0' != wc) - { - *--p = signs[sign]; - return put_padding_r(w, wc, p, e); - } - p = put_padding_r(w, wc, p, e + 1); - *--p = signs[sign]; - return p; -} - -static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc, - char *const e) -{ - return put_integer_r(v, 0, w, wc, e); -} - -static INLINE char *put_int_r(const int v, const unsigned w, const char wc, - char *const e) -{ - return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e) - : put_integer_r((unsigned)-v, -1, w, wc, e); -} - -static INLINE char *put_stringn(const char *const s_p, const char *const s_e, - char *const p, char *const e) -{ - const ptrdiff_t m = e - p; - ptrdiff_t n = s_e - s_p; - if (n > m) - { - n = m; - } - memcpy(p, s_p, n); - return p + n; -} - -static INLINE char *put_string(const char *s, char *p, char *const e) -{ - const ptrdiff_t n = e - p; - char *const c = (char *)memccpy(p, s, '\0', n); - return 0 != c? c - 1: e; -} - -static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, - char *const p, char *const e) -{ - char buf[16]; - char *const se = buf + _countof(buf); - char *sp = put_uint_r(v, w, wc, se); - return put_stringn(sp, se, p, e); -} - -#define PUT_CSTR_R(p, STR) \ - do { \ - for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ - *--(p) = (STR)[i]; \ - } \ - } _BT_LOG_ONCE - -#define PUT_CSTR_CHECKED(p, e, STR) \ - do { \ - for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ - *(p)++ = (STR)[i]; \ - } \ - } _BT_LOG_ONCE - -/* F_INIT field support. - */ -#define _BT_LOG_MESSAGE_FORMAT_INIT__ -#define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR -#define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH -#define _BT_LOG_MESSAGE_FORMAT_INIT__DAY -#define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR -#define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE -#define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND -#define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND -#define _BT_LOG_MESSAGE_FORMAT_INIT__PID -#define _BT_LOG_MESSAGE_FORMAT_INIT__TID -#define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL -#define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) -#define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION -#define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME -#define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE -#define _BT_LOG_MESSAGE_FORMAT_INIT__S(s) -#define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); -#define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) -#define _BT_LOG_MESSAGE_FORMAT_INIT(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field) - -/* Implements generation of printf-like format string for log message - * format specification. - */ -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) - -/* Implements generation of printf-like format parameters for log message - * format specification. - */ -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR ,(unsigned)(tm.tm_year + 1900) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH ,(unsigned)(tm.tm_mon + 1) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY ,(unsigned)tm.tm_mday -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR ,(unsigned)tm.tm_hour -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE ,(unsigned)tm.tm_min -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND ,(unsigned)tm.tm_sec -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND ,(unsigned)msec -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID ,pid -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID ,tid -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL ,(char)lvl_char(msg->lvl) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION ,funcname(src->func) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME ,filename(src->file) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE ,src->line -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v -#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) - -/* Implements generation of put_xxx_t statements for log message specification. - */ -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__ -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR p = put_uint_r(tm.tm_year + 1900, 4, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND p = put_uint_r(msec, 3, '0', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) -#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p); -#define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) - -static void put_ctx(bt_log_message *const msg) -{ - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT) -#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT) - VAR_UNUSED(msg); -#else - #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED - struct tm tm; - unsigned msec; - g_time_cb(&tm, &msec); - #endif - #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \ - _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) - int pid, tid; - g_pid_cb(&pid, &tid); - #endif - - #if BT_LOG_OPTIMIZE_SIZE - int n; - n = snprintf(msg->p, nprintf_size(msg), - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT) - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT)); - put_nprintf(msg, n); - #else - char buf[64]; - char *const e = buf + sizeof(buf); - char *p = e; - _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT) - msg->p = put_stringn(p, e, msg->p, msg->e); - #endif -#endif -} - -#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ - do { \ - const char *ch; \ - msg->tag_b = msg->p; \ - if (0 != (ch = _bt_log_tag_prefix)) { \ - for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ - } \ - if (0 != (ch = tag) && 0 != tag[0]) { \ - if (msg->tag_b != msg->p) { \ - PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ - } \ - for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ - } \ - msg->tag_e = msg->p; \ - if (msg->tag_b != msg->p) { \ - PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ - } \ - } _BT_LOG_ONCE - -/* Implements simple put statements for log message specification. - */ -#define _BT_LOG_MESSAGE_FORMAT_PUT__ -#define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED -#define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) PUT_TAG(msg, tag, pd, td); -#define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION msg->p = put_string(funcname(src->func), msg->p, msg->e); -#define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME msg->p = put_string(filename(src->file), msg->p, msg->e); -#define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); -#define _BT_LOG_MESSAGE_FORMAT_PUT__S(s) PUT_CSTR_CHECKED(msg->p, msg->e, s); -#define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) -#define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e); -#define _BT_LOG_MESSAGE_FORMAT_PUT(field) \ - _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field) - -static void put_tag(bt_log_message *const msg, const char *const tag) -{ - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT) -#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT) - VAR_UNUSED(tag); -#endif -#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT) - VAR_UNUSED(msg); -#else - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT) -#endif -} - -static void put_src(bt_log_message *const msg, const src_location *const src) -{ - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT) -#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \ - !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \ - !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT) - VAR_UNUSED(src); -#endif -#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT) - VAR_UNUSED(msg); -#else - #if BT_LOG_OPTIMIZE_SIZE - int n; - n = snprintf(msg->p, nprintf_size(msg), - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT) - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT)); - put_nprintf(msg, n); - #else - _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT) - #endif -#endif -} - -static void put_msg(bt_log_message *const msg, - const char *const fmt, va_list va) -{ - int n; - msg->msg_b = msg->p; - n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); - put_nprintf(msg, n); -} - -static void output_mem(const bt_log_spec *log, bt_log_message *const msg, - const mem_block *const mem) -{ - if (0 == mem->d || 0 == mem->d_sz) - { - return; - } - const unsigned char *mem_p = (const unsigned char *)mem->d; - const unsigned char *const mem_e = mem_p + mem->d_sz; - const unsigned char *mem_cut; - const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; - char *const hex_b = msg->msg_b; - char *const ascii_b = hex_b + 2 * mem_width + 2; - char *const ascii_e = ascii_b + mem_width; - if (msg->e < ascii_e) - { - return; - } - while (mem_p != mem_e) - { - char *hex = hex_b; - char *ascii = ascii_b; - for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e; - mem_cut != mem_p; ++mem_p) - { - const unsigned char ch = *mem_p; - *hex++ = c_hex[(0xf0 & ch) >> 4]; - *hex++ = c_hex[(0x0f & ch)]; - *ascii++ = isprint(ch)? (char)ch: '?'; - } - while (hex != ascii_b) - { - *hex++ = ' '; - } - msg->p = ascii; - log->output->callback(msg, log->output->arg); - } -} - -BT_HIDDEN -void bt_log_set_tag_prefix(const char *const prefix) -{ - _bt_log_tag_prefix = prefix; -} - -BT_HIDDEN -void bt_log_set_mem_width(const unsigned w) -{ - _bt_log_global_format.mem_width = w; -} - -BT_HIDDEN -void bt_log_set_output_level(const int lvl) -{ - _bt_log_global_output_lvl = lvl; -} - -BT_HIDDEN -void bt_log_set_output_v(const unsigned mask, void *const arg, - const bt_log_output_cb callback) -{ - _bt_log_global_output.mask = mask; - _bt_log_global_output.arg = arg; - _bt_log_global_output.callback = callback; -} - -static void _bt_log_write_imp( - const bt_log_spec *log, - const src_location *const src, const mem_block *const mem, - const int lvl, const char *const tag, const char *const fmt, va_list va) -{ - bt_log_message msg; - char *buf = logging_buf; - const unsigned mask = log->output->mask; - msg.lvl = lvl; - msg.tag = tag; - g_buffer_cb(&msg, buf); - const char *rst_color_p = bt_common_color_reset(); - const char *rst_color_e = rst_color_p + strlen(rst_color_p); - const char *color_p = ""; - const char *color_e = color_p; - - switch (lvl) { - case BT_LOG_INFO: - color_p = bt_common_color_fg_blue(); - color_e = color_p + strlen(color_p); - break; - case BT_LOG_WARN: - color_p = bt_common_color_fg_yellow(); - color_e = color_p + strlen(color_p); - break; - case BT_LOG_ERROR: - case BT_LOG_FATAL: - color_p = bt_common_color_fg_red(); - color_e = color_p + strlen(color_p); - break; - default: - break; - } - - msg.p = put_stringn(color_p, color_e, msg.p, msg.e); - - if (BT_LOG_PUT_CTX & mask) - { - put_ctx(&msg); - } - if (BT_LOG_PUT_TAG & mask) - { - put_tag(&msg, tag); - } - if (0 != src && BT_LOG_PUT_SRC & mask) - { - put_src(&msg, src); - } - if (BT_LOG_PUT_MSG & mask) - { - put_msg(&msg, fmt, va); - } - msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e); - log->output->callback(&msg, log->output->arg); - if (0 != mem && BT_LOG_PUT_MSG & mask) - { - output_mem(log, &msg, mem); - } -} - -BT_HIDDEN -void _bt_log_write_d( - const char *const func, const char *const file, const unsigned line, - const int lvl, const char *const tag, - const char *const fmt, ...) -{ - const src_location src = {func, file, line}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_aux_d( - const char *const func, const char *const file, const unsigned line, - const bt_log_spec *const log, const int lvl, const char *const tag, - const char *const fmt, ...) -{ - const src_location src = {func, file, line}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write(const int lvl, const char *const tag, - const char *const fmt, ...) -{ - va_list va; - va_start(va, fmt); - _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_aux( - const bt_log_spec *const log, const int lvl, const char *const tag, - const char *const fmt, ...) -{ - va_list va; - va_start(va, fmt); - _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_mem_d( - const char *const func, const char *const file, const unsigned line, - const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) -{ - const src_location src = {func, file, line}; - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_mem_aux_d( - const char *const func, const char *const file, const unsigned line, - const bt_log_spec *const log, const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) -{ - const src_location src = {func, file, line}; - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_mem(const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) -{ - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); - va_end(va); -} - -BT_HIDDEN -void _bt_log_write_mem_aux( - const bt_log_spec *const log, const int lvl, const char *const tag, - const void *const d, const unsigned d_sz, - const char *const fmt, ...) -{ - const mem_block mem = {d, d_sz}; - va_list va; - va_start(va, fmt); - _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); - va_end(va); -} diff --git a/plugins/Makefile.am b/plugins/Makefile.am deleted file mode 100644 index 9b23e3c4..00000000 --- a/plugins/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -SUBDIRS = utils text ctf - -if ENABLE_DEBUG_INFO -SUBDIRS += lttng-utils -endif - -noinst_HEADERS = plugins-common.h diff --git a/plugins/ctf/Makefile.am b/plugins/ctf/Makefile.am deleted file mode 100644 index 5f5c859d..00000000 --- a/plugins/ctf/Makefile.am +++ /dev/null @@ -1,30 +0,0 @@ -SUBDIRS = common \ - fs-src \ - fs-sink \ - lttng-live - -noinst_HEADERS = print.h - -plugindir = "$(PLUGINSDIR)" -plugin_LTLIBRARIES = babeltrace-plugin-ctf.la - -# ctf plugin -babeltrace_plugin_ctf_la_SOURCES = plugin.c - -babeltrace_plugin_ctf_la_LDFLAGS = \ - $(LT_NO_UNDEFINED) \ - -avoid-version -module - -babeltrace_plugin_ctf_la_LIBADD = \ - common/libbabeltrace2-plugin-ctf-common.la \ - fs-sink/libbabeltrace2-plugin-ctf-fs-sink.la \ - fs-src/libbabeltrace2-plugin-ctf-fs-src.la \ - lttng-live/libbabeltrace2-plugin-ctf-lttng-live.la - -if !ENABLE_BUILT_IN_PLUGINS -babeltrace_plugin_ctf_la_LIBADD += \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/ctfser/libbabeltrace2-ctfser.la -endif diff --git a/plugins/ctf/common/Makefile.am b/plugins/ctf/common/Makefile.am deleted file mode 100644 index 055355b1..00000000 --- a/plugins/ctf/common/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -SUBDIRS = metadata bfcr msg-iter utils - -noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-common.la -libbabeltrace2_plugin_ctf_common_la_SOURCES = print.h -libbabeltrace2_plugin_ctf_common_la_LIBADD = \ - $(builddir)/metadata/libctf-parser.la \ - $(builddir)/metadata/libctf-ast.la \ - $(builddir)/bfcr/libctf-bfcr.la \ - $(builddir)/msg-iter/libctf-msg-iter.la \ - $(builddir)/utils/libctf-utils.la diff --git a/plugins/ctf/common/bfcr/Makefile.am b/plugins/ctf/common/bfcr/Makefile.am deleted file mode 100644 index d0dd8c6f..00000000 --- a/plugins/ctf/common/bfcr/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LTLIBRARIES = libctf-bfcr.la -libctf_bfcr_la_SOURCES = \ - bfcr.c \ - bfcr.h \ - logging.c \ - logging.h diff --git a/plugins/ctf/common/bfcr/bfcr.c b/plugins/ctf/common/bfcr/bfcr.c deleted file mode 100644 index 91d6788a..00000000 --- a/plugins/ctf/common/bfcr/bfcr.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* - * Babeltrace - CTF binary field class reader (BFCR) - * - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-BFCR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bfcr.h" -#include "../metadata/ctf-meta.h" - -#define DIV8(_x) ((_x) >> 3) -#define BYTES_TO_BITS(_x) ((_x) * 8) -#define BITS_TO_BYTES_FLOOR(_x) DIV8(_x) -#define BITS_TO_BYTES_CEIL(_x) DIV8((_x) + 7) -#define IN_BYTE_OFFSET(_at) ((_at) & 7) - -/* A visit stack entry */ -struct stack_entry { - /* - * Current class of base field, one of: - * - * * Structure - * * Array - * * Sequence - * * Variant - */ - struct ctf_field_class *base_class; - - /* Length of base field (always 1 for a variant class) */ - int64_t base_len; - - /* Index of next field to read */ - int64_t index; -}; - -/* Visit stack */ -struct stack { - /* Entries (struct stack_entry) */ - GArray *entries; - - /* Number of active entries */ - size_t size; -}; - -/* Reading states */ -enum bfcr_state { - BFCR_STATE_NEXT_FIELD, - BFCR_STATE_ALIGN_BASIC, - BFCR_STATE_ALIGN_COMPOUND, - BFCR_STATE_READ_BASIC_BEGIN, - BFCR_STATE_READ_BASIC_CONTINUE, - BFCR_STATE_DONE, -}; - -/* Binary class reader */ -struct bt_bfcr { - /* Bisit stack */ - struct stack *stack; - - /* Current basic field class */ - struct ctf_field_class *cur_basic_field_class; - - /* Current state */ - enum bfcr_state state; - - /* - * Last basic field class's byte order. - * - * This is used to detect errors since two contiguous basic - * classes for which the common boundary is not the boundary of - * a byte cannot have different byte orders. - * - * This is set to -1 on reset and when the last basic field class - * was a string class. - */ - enum ctf_byte_order last_bo; - - /* Current byte order (copied to last_bo after a successful read) */ - enum ctf_byte_order cur_bo; - - /* Stitch buffer infos */ - struct { - /* Stitch buffer */ - uint8_t buf[16]; - - /* Offset, within stitch buffer, of first bit */ - size_t offset; - - /* Length (bits) of data in stitch buffer from offset */ - size_t at; - } stitch; - - /* User buffer infos */ - struct { - /* Address */ - const uint8_t *addr; - - /* Offset of data from address (bits) */ - size_t offset; - - /* Current position from offset (bits) */ - size_t at; - - /* Offset of offset within whole packet (bits) */ - size_t packet_offset; - - /* Data size in buffer (bits) */ - size_t sz; - - /* Buffer size (bytes) */ - size_t buf_sz; - } buf; - - /* User stuff */ - struct { - /* Callback functions */ - struct bt_bfcr_cbs cbs; - - /* Private data */ - void *data; - } user; -}; - -static inline -const char *bfcr_state_string(enum bfcr_state state) -{ - switch (state) { - case BFCR_STATE_NEXT_FIELD: - return "BFCR_STATE_NEXT_FIELD"; - case BFCR_STATE_ALIGN_BASIC: - return "BFCR_STATE_ALIGN_BASIC"; - case BFCR_STATE_ALIGN_COMPOUND: - return "BFCR_STATE_ALIGN_COMPOUND"; - case BFCR_STATE_READ_BASIC_BEGIN: - return "BFCR_STATE_READ_BASIC_BEGIN"; - case BFCR_STATE_READ_BASIC_CONTINUE: - return "BFCR_STATE_READ_BASIC_CONTINUE"; - case BFCR_STATE_DONE: - return "BFCR_STATE_DONE"; - default: - return "(unknown)"; - } -} - -static -struct stack *stack_new(void) -{ - struct stack *stack = NULL; - - stack = g_new0(struct stack, 1); - if (!stack) { - BT_LOGE_STR("Failed to allocate one stack."); - goto error; - } - - stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry)); - if (!stack->entries) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - BT_LOGD("Created stack: addr=%p", stack); - return stack; - -error: - g_free(stack); - return NULL; -} - -static -void stack_destroy(struct stack *stack) -{ - if (!stack) { - return; - } - - BT_LOGD("Destroying stack: addr=%p", stack); - - if (stack->entries) { - g_array_free(stack->entries, TRUE); - } - - g_free(stack); -} - -static -int stack_push(struct stack *stack, struct ctf_field_class *base_class, - size_t base_len) -{ - struct stack_entry *entry; - - BT_ASSERT(stack); - BT_ASSERT(base_class); - BT_LOGV("Pushing field class on stack: stack-addr=%p, " - "fc-addr=%p, fc-type=%d, base-length=%zu, " - "stack-size-before=%zu, stack-size-after=%zu", - stack, base_class, base_class->type, - base_len, stack->size, stack->size + 1); - - if (stack->entries->len == stack->size) { - g_array_set_size(stack->entries, stack->size + 1); - } - - entry = &g_array_index(stack->entries, struct stack_entry, stack->size); - entry->base_class = base_class; - entry->base_len = base_len; - entry->index = 0; - stack->size++; - return 0; -} - -static inline -int64_t get_compound_field_class_length(struct bt_bfcr *bfcr, - struct ctf_field_class *fc) -{ - int64_t length; - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - length = (int64_t) struct_fc->members->len; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - /* Variant field classes always "contain" a single class */ - length = 1; - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - { - struct ctf_field_class_array *array_fc = (void *) fc; - - length = (int64_t) array_fc->length; - break; - } - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - length = bfcr->user.cbs.query.get_sequence_length(fc, - bfcr->user.data); - break; - default: - abort(); - } - - return length; -} - -static -int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class) -{ - int ret; - int64_t length = get_compound_field_class_length(bfcr, base_class); - - if (length < 0) { - BT_LOGW("Cannot get compound field class's field count: " - "bfcr-addr=%p, fc-addr=%p, fc-type=%d", - bfcr, base_class, base_class->type); - ret = BT_BFCR_STATUS_ERROR; - goto end; - } - - ret = stack_push(bfcr->stack, base_class, (size_t) length); - -end: - return ret; -} - -static inline -unsigned int stack_size(struct stack *stack) -{ - BT_ASSERT(stack); - return stack->size; -} - -static -void stack_pop(struct stack *stack) -{ - BT_ASSERT(stack); - BT_ASSERT(stack_size(stack)); - BT_LOGV("Popping from stack: " - "stack-addr=%p, stack-size-before=%u, stack-size-after=%u", - stack, stack->entries->len, stack->entries->len - 1); - stack->size--; -} - -static inline -bool stack_empty(struct stack *stack) -{ - return stack_size(stack) == 0; -} - -static -void stack_clear(struct stack *stack) -{ - BT_ASSERT(stack); - stack->size = 0; -} - -static inline -struct stack_entry *stack_top(struct stack *stack) -{ - BT_ASSERT(stack); - BT_ASSERT(stack_size(stack)); - return &g_array_index(stack->entries, struct stack_entry, - stack->size - 1); -} - -static inline -size_t available_bits(struct bt_bfcr *bfcr) -{ - return bfcr->buf.sz - bfcr->buf.at; -} - -static inline -void consume_bits(struct bt_bfcr *bfcr, size_t incr) -{ - BT_LOGV("Advancing cursor: bfcr-addr=%p, cur-before=%zu, cur-after=%zu", - bfcr, bfcr->buf.at, bfcr->buf.at + incr); - bfcr->buf.at += incr; -} - -static inline -bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz) -{ - return available_bits(bfcr) >= sz; -} - -static inline -bool at_least_one_bit_left(struct bt_bfcr *bfcr) -{ - return has_enough_bits(bfcr, 1); -} - -static inline -size_t packet_at(struct bt_bfcr *bfcr) -{ - return bfcr->buf.packet_offset + bfcr->buf.at; -} - -static inline -size_t buf_at_from_addr(struct bt_bfcr *bfcr) -{ - /* - * Considering this: - * - * ====== offset ===== (17) - * - * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - * ^ - * addr (0) ==== at ==== (12) - * - * We want this: - * - * =============================== (29) - */ - return bfcr->buf.offset + bfcr->buf.at; -} - -static -void stitch_reset(struct bt_bfcr *bfcr) -{ - bfcr->stitch.offset = 0; - bfcr->stitch.at = 0; -} - -static inline -size_t stitch_at_from_addr(struct bt_bfcr *bfcr) -{ - return bfcr->stitch.offset + bfcr->stitch.at; -} - -static -void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz) -{ - size_t stitch_byte_at; - size_t buf_byte_at; - size_t nb_bytes; - - if (sz == 0) { - return; - } - - stitch_byte_at = - BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr)); - buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr)); - nb_bytes = BITS_TO_BYTES_CEIL(sz); - BT_ASSERT(nb_bytes > 0); - BT_ASSERT(bfcr->buf.addr); - memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at], - nb_bytes); - bfcr->stitch.at += sz; - consume_bits(bfcr, sz); -} - -static -void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr) -{ - stitch_append_from_buf(bfcr, available_bits(bfcr)); -} - -static -void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr) -{ - stitch_reset(bfcr); - bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr)); - stitch_append_from_remaining_buf(bfcr); -} - -static inline -void read_unsigned_bitfield(const uint8_t *buf, size_t at, - unsigned int field_size, enum ctf_byte_order bo, - uint64_t *v) -{ - switch (bo) { - case CTF_BYTE_ORDER_BIG: - bt_bitfield_read_be(buf, uint8_t, at, field_size, v); - break; - case CTF_BYTE_ORDER_LITTLE: - bt_bitfield_read_le(buf, uint8_t, at, field_size, v); - break; - default: - abort(); - } - - BT_LOGV("Read unsigned bit array: cur=%zu, size=%u, " - "bo=%d, val=%" PRIu64, at, field_size, bo, *v); -} - -static inline -void read_signed_bitfield(const uint8_t *buf, size_t at, - unsigned int field_size, enum ctf_byte_order bo, int64_t *v) -{ - switch (bo) { - case CTF_BYTE_ORDER_BIG: - bt_bitfield_read_be(buf, uint8_t, at, field_size, v); - break; - case CTF_BYTE_ORDER_LITTLE: - bt_bitfield_read_le(buf, uint8_t, at, field_size, v); - break; - default: - abort(); - } - - BT_LOGV("Read signed bit array: cur=%zu, size=%u, " - "bo=%d, val=%" PRId64, at, field_size, bo, *v); -} - -typedef enum bt_bfcr_status (* read_basic_and_call_cb_t)(struct bt_bfcr *, - const uint8_t *, size_t); - -static inline -enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr, - enum ctf_byte_order next_bo) -{ - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - - /* Always valid when at a byte boundary */ - if (packet_at(bfcr) % 8 == 0) { - goto end; - } - - /* Always valid if last byte order is unknown */ - if (bfcr->last_bo == -1) { - goto end; - } - - /* Always valid if next byte order is unknown */ - if (next_bo == -1) { - goto end; - } - - /* Make sure last byte order is compatible with the next byte order */ - switch (bfcr->last_bo) { - case CTF_BYTE_ORDER_BIG: - if (next_bo != CTF_BYTE_ORDER_BIG) { - status = BT_BFCR_STATUS_ERROR; - } - break; - case CTF_BYTE_ORDER_LITTLE: - if (next_bo != CTF_BYTE_ORDER_LITTLE) { - status = BT_BFCR_STATUS_ERROR; - } - break; - default: - status = BT_BFCR_STATUS_ERROR; - } - -end: - if (status < 0) { - BT_LOGW("Cannot read bit array: two different byte orders not at a byte boundary: " - "bfcr-addr=%p, last-bo=%d, next-bo=%d", - bfcr, bfcr->last_bo, next_bo); - } - - return status; -} - -static -enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr, - const uint8_t *buf, size_t at) -{ - double dblval; - unsigned int field_size; - enum ctf_byte_order bo; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - struct ctf_field_class_float *fc = (void *) bfcr->cur_basic_field_class; - - BT_ASSERT(fc); - field_size = fc->base.size; - bo = fc->base.byte_order; - bfcr->cur_bo = bo; - - switch (field_size) { - case 32: - { - uint64_t v; - union { - uint32_t u; - float f; - } f32; - - read_unsigned_bitfield(buf, at, field_size, bo, &v); - f32.u = (uint32_t) v; - dblval = (double) f32.f; - break; - } - case 64: - { - union { - uint64_t u; - double d; - } f64; - - read_unsigned_bitfield(buf, at, field_size, bo, &f64.u); - dblval = f64.d; - break; - } - default: - /* Only 32-bit and 64-bit fields are supported currently */ - abort(); - } - - BT_LOGV("Read floating point number value: bfcr=%p, cur=%zu, val=%f", - bfcr, at, dblval); - - if (bfcr->user.cbs.classes.floating_point) { - BT_LOGV("Calling user function (floating point number)."); - status = bfcr->user.cbs.classes.floating_point(dblval, - bfcr->cur_basic_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - } - } - - return status; -} - -static inline -enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr, - const uint8_t *buf, size_t at) -{ - unsigned int field_size; - enum ctf_byte_order bo; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - struct ctf_field_class_int *fc = (void *) bfcr->cur_basic_field_class; - - field_size = fc->base.size; - bo = fc->base.byte_order; - - /* - * Update current byte order now because we could be reading - * the integer value of an enumeration class, and thus we know - * here the actual supporting integer class's byte order. - */ - bfcr->cur_bo = bo; - - if (fc->is_signed) { - int64_t v; - - read_signed_bitfield(buf, at, field_size, bo, &v); - - if (bfcr->user.cbs.classes.signed_int) { - BT_LOGV("Calling user function (signed integer)."); - status = bfcr->user.cbs.classes.signed_int(v, - bfcr->cur_basic_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: " - "bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - } - } - } else { - uint64_t v; - - read_unsigned_bitfield(buf, at, field_size, bo, &v); - - if (bfcr->user.cbs.classes.unsigned_int) { - BT_LOGV("Calling user function (unsigned integer)."); - status = bfcr->user.cbs.classes.unsigned_int(v, - bfcr->cur_basic_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: " - "bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - } - } - } - - return status; -} - -static inline -enum bt_bfcr_status read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr, - read_basic_and_call_cb_t read_basic_and_call_cb) -{ - size_t available; - size_t needed_bits; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - struct ctf_field_class_bit_array *fc = - (void *) bfcr->cur_basic_field_class; - - if (!at_least_one_bit_left(bfcr)) { - BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); - status = BT_BFCR_STATUS_EOF; - goto end; - } - - available = available_bits(bfcr); - needed_bits = fc->size - bfcr->stitch.at; - BT_LOGV("Continuing basic field decoding: " - "bfcr-addr=%p, field-size=%u, needed-size=%zu, " - "available-size=%zu", - bfcr, fc->size, needed_bits, available); - if (needed_bits <= available) { - /* We have all the bits; append to stitch, then decode */ - stitch_append_from_buf(bfcr, needed_bits); - status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf, - bfcr->stitch.offset); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("Cannot read basic field: " - "bfcr-addr=%p, fc-addr=%p, status=%s", - bfcr, bfcr->cur_basic_field_class, - bt_bfcr_status_string(status)); - goto end; - } - - if (stack_empty(bfcr->stack)) { - /* Root is a basic class */ - bfcr->state = BFCR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(bfcr->stack)->index++; - bfcr->state = BFCR_STATE_NEXT_FIELD; - bfcr->last_bo = bfcr->cur_bo; - } - goto end; - } - - /* We are here; it means we don't have enough data to decode this */ - BT_LOGV_STR("Not enough data to read the next basic field: appending to stitch buffer."); - stitch_append_from_remaining_buf(bfcr); - status = BT_BFCR_STATUS_EOF; - -end: - return status; -} - -static inline -enum bt_bfcr_status read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr, - read_basic_and_call_cb_t read_basic_and_call_cb) -{ - size_t available; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - struct ctf_field_class_bit_array *fc = - (void *) bfcr->cur_basic_field_class; - - if (!at_least_one_bit_left(bfcr)) { - BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); - status = BT_BFCR_STATUS_EOF; - goto end; - } - - status = validate_contiguous_bo(bfcr, fc->byte_order); - if (status != BT_BFCR_STATUS_OK) { - /* validate_contiguous_bo() logs errors */ - goto end; - } - - available = available_bits(bfcr); - - if (fc->size <= available) { - /* We have all the bits; decode and set now */ - BT_ASSERT(bfcr->buf.addr); - status = read_basic_and_call_cb(bfcr, bfcr->buf.addr, - buf_at_from_addr(bfcr)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("Cannot read basic field: " - "bfcr-addr=%p, fc-addr=%p, status=%s", - bfcr, bfcr->cur_basic_field_class, - bt_bfcr_status_string(status)); - goto end; - } - - consume_bits(bfcr, fc->size); - - if (stack_empty(bfcr->stack)) { - /* Root is a basic class */ - bfcr->state = BFCR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(bfcr->stack)->index++; - bfcr->state = BFCR_STATE_NEXT_FIELD; - bfcr->last_bo = bfcr->cur_bo; - } - - goto end; - } - - /* We are here; it means we don't have enough data to decode this */ - BT_LOGV_STR("Not enough data to read the next basic field: setting stitch buffer."); - stitch_set_from_remaining_buf(bfcr); - bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE; - status = BT_BFCR_STATUS_EOF; - -end: - return status; -} - -static inline -enum bt_bfcr_status read_basic_int_class_and_call_begin( - struct bt_bfcr *bfcr) -{ - return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb); -} - -static inline -enum bt_bfcr_status read_basic_int_class_and_call_continue( - struct bt_bfcr *bfcr) -{ - return read_bit_array_class_and_call_continue(bfcr, - read_basic_int_and_call_cb); -} - -static inline -enum bt_bfcr_status read_basic_float_class_and_call_begin( - struct bt_bfcr *bfcr) -{ - return read_bit_array_class_and_call_begin(bfcr, - read_basic_float_and_call_cb); -} - -static inline -enum bt_bfcr_status read_basic_float_class_and_call_continue( - struct bt_bfcr *bfcr) -{ - return read_bit_array_class_and_call_continue(bfcr, - read_basic_float_and_call_cb); -} - -static inline -enum bt_bfcr_status read_basic_string_class_and_call( - struct bt_bfcr *bfcr, bool begin) -{ - size_t buf_at_bytes; - const uint8_t *result; - size_t available_bytes; - const uint8_t *first_chr; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - - if (!at_least_one_bit_left(bfcr)) { - BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); - status = BT_BFCR_STATUS_EOF; - goto end; - } - - BT_ASSERT(buf_at_from_addr(bfcr) % 8 == 0); - available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr)); - buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr)); - BT_ASSERT(bfcr->buf.addr); - first_chr = &bfcr->buf.addr[buf_at_bytes]; - result = memchr(first_chr, '\0', available_bytes); - - if (begin && bfcr->user.cbs.classes.string_begin) { - BT_LOGV("Calling user function (string, beginning)."); - status = bfcr->user.cbs.classes.string_begin( - bfcr->cur_basic_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - if (!result) { - /* No null character yet */ - if (bfcr->user.cbs.classes.string) { - BT_LOGV("Calling user function (substring)."); - status = bfcr->user.cbs.classes.string( - (const char *) first_chr, - available_bytes, bfcr->cur_basic_field_class, - bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: " - "bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - consume_bits(bfcr, BYTES_TO_BITS(available_bytes)); - bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE; - status = BT_BFCR_STATUS_EOF; - } else { - /* Found the null character */ - size_t result_len = (size_t) (result - first_chr); - - if (bfcr->user.cbs.classes.string && result_len) { - BT_LOGV("Calling user function (substring)."); - status = bfcr->user.cbs.classes.string( - (const char *) first_chr, - result_len, bfcr->cur_basic_field_class, - bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: " - "bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - if (bfcr->user.cbs.classes.string_end) { - BT_LOGV("Calling user function (string, end)."); - status = bfcr->user.cbs.classes.string_end( - bfcr->cur_basic_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: " - "bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - consume_bits(bfcr, BYTES_TO_BITS(result_len + 1)); - - if (stack_empty(bfcr->stack)) { - /* Root is a basic class */ - bfcr->state = BFCR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(bfcr->stack)->index++; - bfcr->state = BFCR_STATE_NEXT_FIELD; - bfcr->last_bo = bfcr->cur_bo; - } - } - -end: - return status; -} - -static inline -enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr) -{ - enum bt_bfcr_status status; - - BT_ASSERT(bfcr->cur_basic_field_class); - - switch (bfcr->cur_basic_field_class->type) { - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - status = read_basic_int_class_and_call_begin(bfcr); - break; - case CTF_FIELD_CLASS_TYPE_FLOAT: - status = read_basic_float_class_and_call_begin(bfcr); - break; - case CTF_FIELD_CLASS_TYPE_STRING: - status = read_basic_string_class_and_call(bfcr, true); - break; - default: - abort(); - } - - return status; -} - -static inline -enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr) -{ - enum bt_bfcr_status status; - - BT_ASSERT(bfcr->cur_basic_field_class); - - switch (bfcr->cur_basic_field_class->type) { - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - status = read_basic_int_class_and_call_continue(bfcr); - break; - case CTF_FIELD_CLASS_TYPE_FLOAT: - status = read_basic_float_class_and_call_continue(bfcr); - break; - case CTF_FIELD_CLASS_TYPE_STRING: - status = read_basic_string_class_and_call(bfcr, false); - break; - default: - abort(); - } - - return status; -} - -static inline -size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align) -{ - size_t aligned_packet_at; - - aligned_packet_at = ALIGN(packet_at(bfcr), align); - return aligned_packet_at - packet_at(bfcr); -} - -static inline -enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr, - struct ctf_field_class *field_class, enum bfcr_state next_state) -{ - unsigned int field_alignment; - size_t skip_bits; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - - /* Get field's alignment */ - field_alignment = field_class->alignment; - - /* - * 0 means "undefined" for variants; what we really want is 1 - * (always aligned) - */ - BT_ASSERT(field_alignment >= 1); - - /* Compute how many bits we need to skip */ - skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment); - - /* Nothing to skip? aligned */ - if (skip_bits == 0) { - bfcr->state = next_state; - goto end; - } - - /* Make sure there's at least one bit left */ - if (!at_least_one_bit_left(bfcr)) { - status = BT_BFCR_STATUS_EOF; - goto end; - } - - /* Consume as many bits as possible in what's left */ - consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits)); - - /* Are we done now? */ - skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment); - if (skip_bits == 0) { - /* Yes: go to next state */ - bfcr->state = next_state; - goto end; - } else { - /* No: need more data */ - BT_LOGV("Reached end of data when aligning: bfcr-addr=%p", bfcr); - status = BT_BFCR_STATUS_EOF; - } - -end: - return status; -} - -static inline -enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr) -{ - int ret; - struct stack_entry *top; - struct ctf_field_class *next_field_class = NULL; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - - if (stack_empty(bfcr->stack)) { - goto end; - } - - top = stack_top(bfcr->stack); - - /* Are we done with this base class? */ - while (top->index == top->base_len) { - if (bfcr->user.cbs.classes.compound_end) { - BT_LOGV("Calling user function (compound, end)."); - status = bfcr->user.cbs.classes.compound_end( - top->base_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - stack_pop(bfcr->stack); - - /* Are we done with the root class? */ - if (stack_empty(bfcr->stack)) { - bfcr->state = BFCR_STATE_DONE; - goto end; - } - - top = stack_top(bfcr->stack); - top->index++; - } - - /* Get next field's class */ - switch (top->base_class->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - next_field_class = ctf_field_class_struct_borrow_member_by_index( - (void *) top->base_class, (uint64_t) top->index)->fc; - break; - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = - (void *) top->base_class; - - next_field_class = array_fc->elem_fc; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - /* Variant classes are dynamic: the user should know! */ - next_field_class = - bfcr->user.cbs.query.borrow_variant_selected_field_class( - top->base_class, bfcr->user.data); - break; - default: - break; - } - - if (!next_field_class) { - BT_LOGW("Cannot get the field class of the next field: " - "bfcr-addr=%p, base-fc-addr=%p, base-fc-type=%d, " - "index=%" PRId64, - bfcr, top->base_class, top->base_class->type, - top->index); - status = BT_BFCR_STATUS_ERROR; - goto end; - } - - if (next_field_class->is_compound) { - if (bfcr->user.cbs.classes.compound_begin) { - BT_LOGV("Calling user function (compound, begin)."); - status = bfcr->user.cbs.classes.compound_begin( - next_field_class, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(status)); - if (status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - goto end; - } - } - - ret = stack_push_with_len(bfcr, next_field_class); - if (ret) { - /* stack_push_with_len() logs errors */ - status = BT_BFCR_STATUS_ERROR; - goto end; - } - - /* Next state: align a compound class */ - bfcr->state = BFCR_STATE_ALIGN_COMPOUND; - } else { - /* Replace current basic field class */ - BT_LOGV("Replacing current basic field class: " - "bfcr-addr=%p, cur-basic-fc-addr=%p, " - "next-basic-fc-addr=%p", - bfcr, bfcr->cur_basic_field_class, next_field_class); - bfcr->cur_basic_field_class = next_field_class; - - /* Next state: align a basic class */ - bfcr->state = BFCR_STATE_ALIGN_BASIC; - } - -end: - return status; -} - -static inline -enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr) -{ - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - - BT_LOGV("Handling state: bfcr-addr=%p, state=%s", - bfcr, bfcr_state_string(bfcr->state)); - - switch (bfcr->state) { - case BFCR_STATE_NEXT_FIELD: - status = next_field_state(bfcr); - break; - case BFCR_STATE_ALIGN_BASIC: - status = align_class_state(bfcr, bfcr->cur_basic_field_class, - BFCR_STATE_READ_BASIC_BEGIN); - break; - case BFCR_STATE_ALIGN_COMPOUND: - status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class, - BFCR_STATE_NEXT_FIELD); - break; - case BFCR_STATE_READ_BASIC_BEGIN: - status = read_basic_begin_state(bfcr); - break; - case BFCR_STATE_READ_BASIC_CONTINUE: - status = read_basic_continue_state(bfcr); - break; - case BFCR_STATE_DONE: - break; - } - - BT_LOGV("Handled state: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(status)); - return status; -} - -BT_HIDDEN -struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data) -{ - struct bt_bfcr *bfcr; - - BT_LOGD_STR("Creating binary class reader (BFCR)."); - bfcr = g_new0(struct bt_bfcr, 1); - if (!bfcr) { - BT_LOGE_STR("Failed to allocate one binary class reader."); - goto end; - } - - bfcr->stack = stack_new(); - if (!bfcr->stack) { - BT_LOGE_STR("Cannot create BFCR's stack."); - bt_bfcr_destroy(bfcr); - bfcr = NULL; - goto end; - } - - bfcr->state = BFCR_STATE_NEXT_FIELD; - bfcr->user.cbs = cbs; - bfcr->user.data = data; - BT_LOGD("Created BFCR: addr=%p", bfcr); - -end: - return bfcr; -} - -BT_HIDDEN -void bt_bfcr_destroy(struct bt_bfcr *bfcr) -{ - if (bfcr->stack) { - stack_destroy(bfcr->stack); - } - - BT_LOGD("Destroying BFCR: addr=%p", bfcr); - g_free(bfcr); -} - -static -void reset(struct bt_bfcr *bfcr) -{ - BT_LOGD("Resetting BFCR: addr=%p", bfcr); - stack_clear(bfcr->stack); - stitch_reset(bfcr); - bfcr->buf.addr = NULL; - bfcr->last_bo = -1; -} - -static -void update_packet_offset(struct bt_bfcr *bfcr) -{ - BT_LOGV("Updating packet offset for next call: " - "bfcr-addr=%p, cur-packet-offset=%zu, next-packet-offset=%zu", - bfcr, bfcr->buf.packet_offset, - bfcr->buf.packet_offset + bfcr->buf.at); - bfcr->buf.packet_offset += bfcr->buf.at; -} - -BT_HIDDEN -size_t bt_bfcr_start(struct bt_bfcr *bfcr, - struct ctf_field_class *cls, const uint8_t *buf, - size_t offset, size_t packet_offset, size_t sz, - enum bt_bfcr_status *status) -{ - BT_ASSERT(bfcr); - BT_ASSERT(BYTES_TO_BITS(sz) >= offset); - reset(bfcr); - bfcr->buf.addr = buf; - bfcr->buf.offset = offset; - bfcr->buf.at = 0; - bfcr->buf.packet_offset = packet_offset; - bfcr->buf.buf_sz = sz; - bfcr->buf.sz = BYTES_TO_BITS(sz) - offset; - *status = BT_BFCR_STATUS_OK; - - BT_LOGV("Starting decoding: bfcr-addr=%p, fc-addr=%p, " - "buf-addr=%p, buf-size=%zu, offset=%zu, " - "packet-offset=%zu", - bfcr, cls, buf, sz, offset, packet_offset); - - /* Set root class */ - if (cls->is_compound) { - /* Compound class: push on visit stack */ - int stack_ret; - - if (bfcr->user.cbs.classes.compound_begin) { - BT_LOGV("Calling user function (compound, begin)."); - *status = bfcr->user.cbs.classes.compound_begin( - cls, bfcr->user.data); - BT_LOGV("User function returned: status=%s", - bt_bfcr_status_string(*status)); - if (*status != BT_BFCR_STATUS_OK) { - BT_LOGW("User function failed: bfcr-addr=%p, status=%s", - bfcr, bt_bfcr_status_string(*status)); - goto end; - } - } - - stack_ret = stack_push_with_len(bfcr, cls); - if (stack_ret) { - /* stack_push_with_len() logs errors */ - *status = BT_BFCR_STATUS_ERROR; - goto end; - } - - bfcr->state = BFCR_STATE_ALIGN_COMPOUND; - } else { - /* Basic class: set as current basic class */ - bfcr->cur_basic_field_class = cls; - bfcr->state = BFCR_STATE_ALIGN_BASIC; - } - - /* Run the machine! */ - BT_LOGV_STR("Running the state machine."); - - while (true) { - *status = handle_state(bfcr); - if (*status != BT_BFCR_STATUS_OK || - bfcr->state == BFCR_STATE_DONE) { - break; - } - } - - /* Update packet offset for next time */ - update_packet_offset(bfcr); - -end: - return bfcr->buf.at; -} - -BT_HIDDEN -size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz, - enum bt_bfcr_status *status) -{ - BT_ASSERT(bfcr); - BT_ASSERT(buf); - BT_ASSERT(sz > 0); - bfcr->buf.addr = buf; - bfcr->buf.offset = 0; - bfcr->buf.at = 0; - bfcr->buf.buf_sz = sz; - bfcr->buf.sz = BYTES_TO_BITS(sz); - *status = BT_BFCR_STATUS_OK; - - BT_LOGV("Continuing decoding: bfcr-addr=%p, buf-addr=%p, buf-size=%zu", - bfcr, buf, sz); - - /* Continue running the machine */ - BT_LOGV_STR("Running the state machine."); - - while (true) { - *status = handle_state(bfcr); - if (*status != BT_BFCR_STATUS_OK || - bfcr->state == BFCR_STATE_DONE) { - break; - } - } - - /* Update packet offset for next time */ - update_packet_offset(bfcr); - return bfcr->buf.at; -} - -BT_HIDDEN -void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, - bt_bfcr_unsigned_int_cb_func cb) -{ - BT_ASSERT(bfcr); - BT_ASSERT(cb); - bfcr->user.cbs.classes.unsigned_int = cb; -} diff --git a/plugins/ctf/common/bfcr/bfcr.h b/plugins/ctf/common/bfcr/bfcr.h deleted file mode 100644 index 66a7ef71..00000000 --- a/plugins/ctf/common/bfcr/bfcr.h +++ /dev/null @@ -1,378 +0,0 @@ -#ifndef CTF_BFCR_H -#define CTF_BFCR_H - -/* - * Babeltrace - CTF binary field class reader (BFCR) - * - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "../metadata/ctf-meta.h" - -/** - * @file bfcr.h - * - * Event-driven CTF binary field class reader (BFCR). - * - * This is a common, internal API used by CTF source plugins. It allows - * a binary CTF IR field class to be decoded from user-provided buffers. - * As the class is decoded (and, possibly, its nested classes), - * registered user callback functions are called. - * - * This API is only concerned with reading one CTF class at a time from - * one or more buffer of bytes. It does not know CTF dynamic scopes, - * events, or streams. Sequence lengths and selected variant classes are - * requested to the user when needed. - */ - -/** - * Binary class reader API status codes. - */ -enum bt_bfcr_status { - /** Out of memory. */ - BT_BFCR_STATUS_ENOMEM = -5, - /** - * The binary stream reader reached the end of the user-provided - * buffer, but data is still needed to finish decoding the - * requested class. - * - * The user needs to call bt_bfcr_continue() as long as - * #BT_BFCR_STATUS_EOF is returned to complete the decoding - * process of a given class. - */ - BT_BFCR_STATUS_EOF = 1, - - /** Invalid argument. */ - BT_BFCR_STATUS_INVAL = -3, - - /** General error. */ - BT_BFCR_STATUS_ERROR = -1, - - /** Everything okay. */ - BT_BFCR_STATUS_OK = 0, -}; - -/** Field class reader. */ -struct bt_bfcr; - -typedef enum bt_bfcr_status (* bt_bfcr_unsigned_int_cb_func)(uint64_t, - struct ctf_field_class *, void *); - -/* - * Field class reader user callback functions. - */ -struct bt_bfcr_cbs { - /** - * Field class callback functions. - * - * This CTF binary class reader is event-driven. The following - * functions are called during the decoding process, either when - * a compound class begins/ends, or when a basic class is - * completely decoded (along with its value). - * - * Each function also receives the CTF field class associated - * with the call, and user data (registered to the class reader - * calling them). - * - * Actual trace IR fields are \em not created here; this would - * be the responsibility of a class reader's user (the provider - * of those callback functions). - * - * All the class callback functions return one of the following - * values: - * - * - #BT_BFCR_STATUS_OK: Everything is okay; - * continue the decoding process. - * - #BT_BFCR_STATUS_ERROR: General error (reported - * to class reader's user). - * - * Any member of this structure may be set to \c NULL, should - * a specific message be not needed. - */ - struct { - /** - * Called when a signed integer class is completely - * decoded. This could also be the supporting signed - * integer class of an enumeration class (\p class will - * indicate this). - * - * @param value Signed integer value - * @param class Integer or enumeration class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* signed_int)(int64_t value, - struct ctf_field_class *cls, void *data); - - /** - * Called when an unsigned integer class is completely - * decoded. This could also be the supporting signed - * integer class of an enumeration class (\p class will - * indicate this). - * - * @param value Unsigned integer value - * @param class Integer or enumeration class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - bt_bfcr_unsigned_int_cb_func unsigned_int; - - /** - * Called when a floating point number class is - * completely decoded. - * - * @param value Floating point number value - * @param class Floating point number class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* floating_point)(double value, - struct ctf_field_class *cls, void *data); - - /** - * Called when a string class begins. - * - * All the following user callback function calls will - * be made to bt_bfcr_cbs::classes::string(), each of - * them providing one substring of the complete string - * class's value. - * - * @param class Beginning string class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* string_begin)( - struct ctf_field_class *cls, void *data); - - /** - * Called when a string class's substring is decoded - * (between a call to bt_bfcr_cbs::classes::string_begin() - * and a call to bt_bfcr_cbs::classes::string_end()). - * - * @param value String value (\em not null-terminated) - * @param len String value length - * @param class String class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* string)(const char *value, - size_t len, struct ctf_field_class *cls, - void *data); - - /** - * Called when a string class ends. - * - * @param class Ending string class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* string_end)( - struct ctf_field_class *cls, void *data); - - /** - * Called when a compound class begins. - * - * All the following class callback function calls will - * signal sequential elements of this compound class, - * until the next corresponding - * bt_bfcr_cbs::classes::compound_end() is called. - * - * If \p class is a variant class, then only one class - * callback function call will follow before the call to - * bt_bfcr_cbs::classes::compound_end(). This single - * call indicates the selected class of this variant - * class. - * - * @param class Beginning compound class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* compound_begin)( - struct ctf_field_class *cls, void *data); - - /** - * Called when a compound class ends. - * - * @param class Ending compound class - * @param data User data - * @returns #BT_BFCR_STATUS_OK or - * #BT_BFCR_STATUS_ERROR - */ - enum bt_bfcr_status (* compound_end)( - struct ctf_field_class *cls, void *data); - } classes; - - /** - * Query callback functions are used when the class reader needs - * dynamic information, i.e. a sequence class's current length - * or a variant class's current selected class. - * - * Both functions need to be set unless it is known that no - * sequences or variants will have to be decoded. - */ - struct { - /** - * Called to query the current length of a given sequence - * class. - * - * @param class Sequence class - * @param data User data - * @returns Sequence length or - * #BT_BFCR_STATUS_ERROR on error - */ - int64_t (* get_sequence_length)(struct ctf_field_class *cls, - void *data); - - /** - * Called to query the current selected class of a given - * variant class. - * - * @param class Variant class - * @param data User data - * @returns Current selected class (owned by - * this) or \c NULL on error - */ - struct ctf_field_class * (* borrow_variant_selected_field_class)( - struct ctf_field_class *cls, void *data); - } query; -}; - -/** - * Creates a CTF binary class reader. - * - * @param cbs User callback functions - * @param data User data (passed to user callback functions) - * @returns New binary class reader on success, or \c NULL on error - */ -BT_HIDDEN -struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data); - -/** - * Destroys a CTF binary class reader, freeing all internal resources. - * - * @param bfcr Binary class reader - */ -BT_HIDDEN -void bt_bfcr_destroy(struct bt_bfcr *bfcr); - -/** - * Decodes a given CTF class from a buffer of bytes. - * - * The number of \em bits consumed by this function is returned. - * - * The \p status output parameter is where a status is written, amongst - * the following: - * - * - #BT_BFCR_STATUS_OK: Decoding is done. - * - #BT_BFCR_STATUS_EOF: The end of the buffer was reached, - * but more data is needed to finish the decoding process of the - * requested class. The user needs to call bt_bfcr_continue() - * as long as #BT_BFCR_STATUS_EOF is returned to complete the - * decoding process of the original class. - * - #BT_BFCR_STATUS_INVAL: Invalid argument. - * - #BT_BFCR_STATUS_ERROR: General error. - * - * Calling this function resets the class reader's internal state. If - * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to - * be called next, \em not bt_bfcr_decode(). - * - * @param bfcr Binary class reader - * @param class Field class to decode - * @param buf Buffer - * @param offset Offset of first bit from \p buf (bits) - * @param packet_offset Offset of \p offset within the CTF - * binary packet containing \p class (bits) - * @param sz Size of buffer in bytes (from \p buf) - * @param status Returned status (see description above) - * @returns Number of consumed bits - */ -BT_HIDDEN -size_t bt_bfcr_start(struct bt_bfcr *bfcr, - struct ctf_field_class *cls, const uint8_t *buf, - size_t offset, size_t packet_offset, size_t sz, - enum bt_bfcr_status *status); - -/** - * Continues the decoding process a given CTF class. - * - * The number of bits consumed by this function is returned. - * - * The \p status output parameter is where a status is placed, amongst - * the following: - * - * - #BT_BFCR_STATUS_OK: decoding is done. - * - #BT_BFCR_STATUS_EOF: the end of the buffer was reached, - * but more data is needed to finish the decoding process of the - * requested class. The user needs to call bt_bfcr_continue() - * as long as #BT_BFCR_STATUS_EOF is returned to complete the - * decoding process of the original class. - * - #BT_BFCR_STATUS_INVAL: invalid argument. - * - #BT_BFCR_STATUS_ERROR: general error. - * - * @param bfcr Binary class reader - * @param buf Buffer - * @param sz Size of buffer in bytes (from \p offset) - * @param status Returned status (see description above) - * @returns Number of consumed bits - */ -BT_HIDDEN -size_t bt_bfcr_continue(struct bt_bfcr *bfcr, - const uint8_t *buf, size_t sz, - enum bt_bfcr_status *status); - -BT_HIDDEN -void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, - bt_bfcr_unsigned_int_cb_func cb); - -static inline -const char *bt_bfcr_status_string(enum bt_bfcr_status status) -{ - switch (status) { - case BT_BFCR_STATUS_ENOMEM: - return "BT_BFCR_STATUS_ENOMEM"; - case BT_BFCR_STATUS_EOF: - return "BT_BFCR_STATUS_EOF"; - case BT_BFCR_STATUS_INVAL: - return "BT_BFCR_STATUS_INVAL"; - case BT_BFCR_STATUS_ERROR: - return "BT_BFCR_STATUS_ERROR"; - case BT_BFCR_STATUS_OK: - return "BT_BFCR_STATUS_OK"; - default: - return "(unknown)"; - } -} - -#endif /* CTF_BFCR_H */ diff --git a/plugins/ctf/common/bfcr/btr.gdb b/plugins/ctf/common/bfcr/btr.gdb deleted file mode 100644 index 71cc0f6e..00000000 --- a/plugins/ctf/common/bfcr/btr.gdb +++ /dev/null @@ -1,24 +0,0 @@ -define ctf-btr-show-stack - if (stack_empty($arg0)) - printf "stack is empty!\n" - else - set $stack_size = stack_size($arg0) - set $stack_at = (int) ($stack_size - 1) - printf "%3s %10s %4s %3s\n", "pos", "base addr", "blen", "idx" - - while ($stack_at >= 0) - set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at) - - if ($stack_at == $stack_size - 1) - printf "%3d %10p %3d %3d <-- top\n", $stack_at, \ - $stack_entry->base_class, $stack_entry->base_len, \ - $stack_entry->index - else - printf "%3d %10p %3d %3d\n", $stack_at, \ - $stack_entry->base_class, $stack_entry->base_len, \ - $stack_entry->index - end - set $stack_at = $stack_at - 1 - end - end -end diff --git a/plugins/ctf/common/bfcr/logging.c b/plugins/ctf/common/bfcr/logging.c deleted file mode 100644 index 2dd5a5b1..00000000 --- a/plugins/ctf/common/bfcr/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bfcr_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bfcr_log_level, "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL"); diff --git a/plugins/ctf/common/bfcr/logging.h b/plugins/ctf/common/bfcr/logging.h deleted file mode 100644 index cd6fb683..00000000 --- a/plugins/ctf/common/bfcr/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CTF_BFCR_LOGGING_H -#define CTF_BFCR_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bfcr_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bfcr_log_level); - -#endif /* CTF_BFCR_LOGGING_H */ diff --git a/plugins/ctf/common/metadata/Makefile.am b/plugins/ctf/common/metadata/Makefile.am deleted file mode 100644 index fa5b6f8e..00000000 --- a/plugins/ctf/common/metadata/Makefile.am +++ /dev/null @@ -1,80 +0,0 @@ -AM_CPPFLAGS += -I$(builddir) -I$(srcdir) -AM_YFLAGS = -t -d -v - -noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la - -BUILT_SOURCES = parser.h - -libctf_parser_la_SOURCES = lexer.l parser.y objstack.c -# scanner-symbols.h is included to prefix generated yy_* symbols -# with bt_. -libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) \ - -include $(srcdir)/scanner-symbols.h -libctf_parser_la_CFLAGS = $(AM_CFLAGS) -Wno-unused-function - -libctf_ast_la_SOURCES = \ - visitor-generate-ir.c \ - visitor-semantic-validator.c \ - visitor-parent-links.c \ - ast.h \ - objstack.h \ - parser.h \ - scanner.h \ - scanner-symbols.h \ - decoder.c \ - decoder.h \ - logging.c \ - logging.h \ - ctf-meta.h \ - ctf-meta-visitors.h \ - ctf-meta-validate.c \ - ctf-meta-update-meanings.c \ - ctf-meta-update-in-ir.c \ - ctf-meta-update-default-clock-classes.c \ - ctf-meta-update-text-array-sequence.c \ - ctf-meta-update-value-storing-indexes.c \ - ctf-meta-update-stream-class-config.c \ - ctf-meta-warn-meaningless-header-fields.c \ - ctf-meta-translate.c \ - ctf-meta-resolve.c - -libctf_ast_la_LIBADD = $(UUID_LIBS) - -if BABELTRACE_BUILD_WITH_MINGW -libctf_ast_la_LIBADD += -lrpcrt4 -lintl -liconv -lole32 $(POPT_LIBS) -endif - -# start with empty files to clean -CLEANFILES = - -if HAVE_BISON -# we have bison: we can clean the generated parser files -CLEANFILES += parser.c parser.h parser.output -else # HAVE_BISON -# create target used to stop the build if we want to build the parser, -# but we don't have the necessary tool to do so -ERR_MSG = "Error: Cannot build target because bison is missing." -ERR_MSG += "Make sure bison is installed and run the configure script again." - -parser.c parser.h: parser.y - @echo $(ERR_MSG) - @false - -all-local: parser.c parser.h -endif # HAVE_BISON - -if HAVE_FLEX -# we have flex: we can clean the generated lexer files -CLEANFILES += lexer.c -else # HAVE_FLEX -# create target used to stop the build if we want to build the lexer, -# but we don't have the necessary tool to do so -ERR_MSG = "Error: Cannot build target because flex is missing." -ERR_MSG += "Make sure flex is installed and run the configure script again." - -filter-lexer.c: lexer.l - @echo $(ERR_MSG) - @false - -all-local: lexer.c -endif # HAVE_FLEX diff --git a/plugins/ctf/common/metadata/ast.h b/plugins/ctf/common/metadata/ast.h deleted file mode 100644 index b2eec360..00000000 --- a/plugins/ctf/common/metadata/ast.h +++ /dev/null @@ -1,344 +0,0 @@ -#ifndef _CTF_AST_H -#define _CTF_AST_H - -/* - * ctf-ast.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include -#include -#include -#include -#include - -#include "decoder.h" -#include "ctf-meta.h" - -// the parameter name (of the reentrant 'yyparse' function) -// data is a pointer to a 'SParserParam' structure -//#define YYPARSE_PARAM scanner - -struct ctf_node; -struct ctf_parser; -struct ctf_visitor_generate_ir; - -#define EINCOMPLETE 1000 - -#define FOREACH_CTF_NODES(F) \ - F(NODE_UNKNOWN) \ - F(NODE_ROOT) \ - F(NODE_ERROR) \ - F(NODE_EVENT) \ - F(NODE_STREAM) \ - F(NODE_ENV) \ - F(NODE_TRACE) \ - F(NODE_CLOCK) \ - F(NODE_CALLSITE) \ - F(NODE_CTF_EXPRESSION) \ - F(NODE_UNARY_EXPRESSION) \ - F(NODE_TYPEDEF) \ - F(NODE_TYPEALIAS_TARGET) \ - F(NODE_TYPEALIAS_ALIAS) \ - F(NODE_TYPEALIAS) \ - F(NODE_TYPE_SPECIFIER) \ - F(NODE_TYPE_SPECIFIER_LIST) \ - F(NODE_POINTER) \ - F(NODE_TYPE_DECLARATOR) \ - F(NODE_FLOATING_POINT) \ - F(NODE_INTEGER) \ - F(NODE_STRING) \ - F(NODE_ENUMERATOR) \ - F(NODE_ENUM) \ - F(NODE_STRUCT_OR_VARIANT_DECLARATION) \ - F(NODE_VARIANT) \ - F(NODE_STRUCT) - -enum node_type { -#define ENTRY(S) S, - FOREACH_CTF_NODES(ENTRY) -#undef ENTRY - NR_NODE_TYPES, -}; - -struct ctf_node { - /* - * Parent node is only set on demand by specific visitor. - */ - struct ctf_node *parent; - struct bt_list_head siblings; - struct bt_list_head tmp_head; - unsigned int lineno; - /* - * We mark nodes visited in the generate-ir phase (last - * phase). We only mark the 1-depth level nodes as visited - * (never the root node, and not their sub-nodes). This allows - * skipping already visited nodes when doing incremental - * metadata append. - */ - int visited; - - enum node_type type; - union { - struct { - } unknown; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - struct bt_list_head trace; - struct bt_list_head env; - struct bt_list_head stream; - struct bt_list_head event; - struct bt_list_head clock; - struct bt_list_head callsite; - } root; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } event; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } stream; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } env; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } trace; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } clock; - struct { - /* - * Children nodes are ctf_expression, field_class_def, - * field_class_alias and field_class_specifier_list. - */ - struct bt_list_head declaration_list; - } callsite; - struct { - struct bt_list_head left; /* Should be string */ - struct bt_list_head right; /* Unary exp. or type */ - } ctf_expression; - struct { - enum { - UNARY_UNKNOWN = 0, - UNARY_STRING, - UNARY_SIGNED_CONSTANT, - UNARY_UNSIGNED_CONSTANT, - UNARY_SBRAC, - } type; - union { - /* - * string for identifier, id_type, keywords, - * string literals and character constants. - */ - char *string; - int64_t signed_constant; - uint64_t unsigned_constant; - struct ctf_node *sbrac_exp; - } u; - enum { - UNARY_LINK_UNKNOWN = 0, - UNARY_DOTLINK, - UNARY_ARROWLINK, - UNARY_DOTDOTDOT, - } link; - } unary_expression; - struct { - struct ctf_node *field_class_specifier_list; - struct bt_list_head field_class_declarators; - } field_class_def; - /* new type is "alias", existing type "target" */ - struct { - struct ctf_node *field_class_specifier_list; - struct bt_list_head field_class_declarators; - } field_class_alias_target; - struct { - struct ctf_node *field_class_specifier_list; - struct bt_list_head field_class_declarators; - } field_class_alias_name; - struct { - struct ctf_node *target; - struct ctf_node *alias; - } field_class_alias; - struct { - enum { - TYPESPEC_UNKNOWN = 0, - TYPESPEC_VOID, - TYPESPEC_CHAR, - TYPESPEC_SHORT, - TYPESPEC_INT, - TYPESPEC_LONG, - TYPESPEC_FLOAT, - TYPESPEC_DOUBLE, - TYPESPEC_SIGNED, - TYPESPEC_UNSIGNED, - TYPESPEC_BOOL, - TYPESPEC_COMPLEX, - TYPESPEC_IMAGINARY, - TYPESPEC_CONST, - TYPESPEC_ID_TYPE, - TYPESPEC_FLOATING_POINT, - TYPESPEC_INTEGER, - TYPESPEC_STRING, - TYPESPEC_STRUCT, - TYPESPEC_VARIANT, - TYPESPEC_ENUM, - } type; - /* For struct, variant and enum */ - struct ctf_node *node; - const char *id_type; - } field_class_specifier; - struct { - /* list of field_class_specifier */ - struct bt_list_head head; - } field_class_specifier_list; - struct { - unsigned int const_qualifier; - } pointer; - struct { - struct bt_list_head pointers; - enum { - TYPEDEC_UNKNOWN = 0, - TYPEDEC_ID, /* identifier */ - TYPEDEC_NESTED, /* (), array or sequence */ - } type; - union { - char *id; - struct { - /* typedec has no pointer list */ - struct ctf_node *field_class_declarator; - /* - * unary expression (value) or - * field_class_specifier_list. - */ - struct bt_list_head length; - /* for abstract type declarator */ - unsigned int abstract_array; - } nested; - } u; - struct ctf_node *bitfield_len; - } field_class_declarator; - struct { - /* Children nodes are ctf_expression. */ - struct bt_list_head expressions; - } floating_point; - struct { - /* Children nodes are ctf_expression. */ - struct bt_list_head expressions; - } integer; - struct { - /* Children nodes are ctf_expression. */ - struct bt_list_head expressions; - } string; - struct { - char *id; - /* - * Range list or single value node. Contains unary - * expressions. - */ - struct bt_list_head values; - } enumerator; - struct { - char *enum_id; - /* - * Either NULL, or points to unary expression or - * field_class_specifier_list. - */ - struct ctf_node *container_field_class; - struct bt_list_head enumerator_list; - int has_body; - } _enum; - struct { - struct ctf_node *field_class_specifier_list; - struct bt_list_head field_class_declarators; - } struct_or_variant_declaration; - struct { - char *name; - char *choice; - /* - * list of field_class_def, field_class_alias and - * declarations - */ - struct bt_list_head declaration_list; - int has_body; - } variant; - struct { - char *name; - /* - * list of field_class_def, field_class_alias and - * declarations - */ - struct bt_list_head declaration_list; - int has_body; - struct bt_list_head min_align; /* align() attribute */ - } _struct; - } u; -}; - -struct ctf_ast { - struct ctf_node root; -}; - -const char *node_type(struct ctf_node *node); - -BT_HIDDEN -struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create( - bt_self_component_source *self_comp, - const struct ctf_metadata_decoder_config *config); - -void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor); - -BT_HIDDEN -bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class( - struct ctf_visitor_generate_ir *visitor); - -BT_HIDDEN -struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class( - struct ctf_visitor_generate_ir *visitor); - -BT_HIDDEN -int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor, - struct ctf_node *node); - -BT_HIDDEN -int ctf_visitor_semantic_check(int depth, struct ctf_node *node); - -BT_HIDDEN -int ctf_visitor_parent_links(int depth, struct ctf_node *node); - -#endif /* _CTF_AST_H */ diff --git a/plugins/ctf/common/metadata/ctf-meta-resolve.c b/plugins/ctf/common/metadata/ctf-meta-resolve.c deleted file mode 100644 index 2d54d0fb..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-resolve.c +++ /dev/null @@ -1,1316 +0,0 @@ -/* - * Copyright 2016-2018 - Philippe Proulx - * Copyright 2015 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-RESOLVE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -typedef GPtrArray field_class_stack; - -/* - * A stack frame. - * - * `fc` contains a compound field class (structure, variant, array, - * or sequence) and `index` indicates the index of the field class in - * the upper frame (-1 for array and sequence field classes). `name` - * indicates the name of the field class in the upper frame (empty - * string for array and sequence field classes). - */ -struct field_class_stack_frame { - struct ctf_field_class *fc; - int64_t index; -}; - -/* - * The current context of the resolving engine. - */ -struct resolve_context { - struct ctf_trace_class *tc; - struct ctf_stream_class *sc; - struct ctf_event_class *ec; - - struct { - struct ctf_field_class *packet_header; - struct ctf_field_class *packet_context; - struct ctf_field_class *event_header; - struct ctf_field_class *event_common_context; - struct ctf_field_class *event_spec_context; - struct ctf_field_class *event_payload; - } scopes; - - /* Root scope being visited */ - enum ctf_scope root_scope; - field_class_stack *field_class_stack; - struct ctf_field_class *cur_fc; -}; - -/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */ -static const char * const absolute_path_prefixes[] = { - [CTF_SCOPE_PACKET_HEADER] = "trace.packet.header.", - [CTF_SCOPE_PACKET_CONTEXT] = "stream.packet.context.", - [CTF_SCOPE_EVENT_HEADER] = "stream.event.header.", - [CTF_SCOPE_EVENT_COMMON_CONTEXT] = "stream.event.context.", - [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT] = "event.context.", - [CTF_SCOPE_EVENT_PAYLOAD] = "event.fields.", -}; - -/* Number of path tokens used for the absolute prefixes */ -static const uint64_t absolute_path_prefix_ptoken_counts[] = { - [CTF_SCOPE_PACKET_HEADER] = 3, - [CTF_SCOPE_PACKET_CONTEXT] = 3, - [CTF_SCOPE_EVENT_HEADER] = 3, - [CTF_SCOPE_EVENT_COMMON_CONTEXT] = 3, - [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT] = 2, - [CTF_SCOPE_EVENT_PAYLOAD] = 2, -}; - -static -void destroy_field_class_stack_frame(struct field_class_stack_frame *frame) -{ - if (!frame) { - return; - } - - g_free(frame); -} - -/* - * Creates a class stack. - */ -static -field_class_stack *field_class_stack_create(void) -{ - return g_ptr_array_new_with_free_func( - (GDestroyNotify) destroy_field_class_stack_frame); -} - -/* - * Destroys a class stack. - */ -static -void field_class_stack_destroy(field_class_stack *stack) -{ - if (stack) { - g_ptr_array_free(stack, TRUE); - } -} - -/* - * Pushes a field class onto a class stack. - */ -static -int field_class_stack_push(field_class_stack *stack, struct ctf_field_class *fc) -{ - int ret = 0; - struct field_class_stack_frame *frame = NULL; - - if (!stack || !fc) { - BT_LOGE("Invalid parameter: stack or field class is NULL."); - ret = -1; - goto end; - } - - frame = g_new0(struct field_class_stack_frame, 1); - if (!frame) { - BT_LOGE_STR("Failed to allocate one field class stack frame."); - ret = -1; - goto end; - } - - BT_LOGV("Pushing field class on context's stack: " - "fc-addr=%p, stack-size-before=%u", fc, stack->len); - frame->fc = fc; - g_ptr_array_add(stack, frame); - -end: - return ret; -} - -/* - * Checks whether or not `stack` is empty. - */ -static -bool field_class_stack_empty(field_class_stack *stack) -{ - return stack->len == 0; -} - -/* - * Returns the number of frames in `stack`. - */ -static -size_t field_class_stack_size(field_class_stack *stack) -{ - return stack->len; -} - -/* - * Returns the top frame of `stack`. - */ -static -struct field_class_stack_frame *field_class_stack_peek(field_class_stack *stack) -{ - struct field_class_stack_frame *entry = NULL; - - if (!stack || field_class_stack_empty(stack)) { - goto end; - } - - entry = g_ptr_array_index(stack, stack->len - 1); -end: - return entry; -} - -/* - * Returns the frame at index `index` in `stack`. - */ -static -struct field_class_stack_frame *field_class_stack_at(field_class_stack *stack, - size_t index) -{ - struct field_class_stack_frame *entry = NULL; - - if (!stack || index >= stack->len) { - goto end; - } - - entry = g_ptr_array_index(stack, index); - -end: - return entry; -} - -/* - * Removes the top frame of `stack`. - */ -static -void field_class_stack_pop(field_class_stack *stack) -{ - if (!field_class_stack_empty(stack)) { - /* - * This will call the frame's destructor and free it, as - * well as put its contained field class. - */ - BT_LOGV("Popping context's stack: stack-size-before=%u", - stack->len); - g_ptr_array_set_size(stack, stack->len - 1); - } -} - -/* - * Returns the scope field class of `scope` in the context `ctx`. - */ -static -struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx, - enum ctf_scope scope) -{ - switch (scope) { - case CTF_SCOPE_PACKET_HEADER: - return ctx->scopes.packet_header; - case CTF_SCOPE_PACKET_CONTEXT: - return ctx->scopes.packet_context; - case CTF_SCOPE_EVENT_HEADER: - return ctx->scopes.event_header; - case CTF_SCOPE_EVENT_COMMON_CONTEXT: - return ctx->scopes.event_common_context; - case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: - return ctx->scopes.event_spec_context; - case CTF_SCOPE_EVENT_PAYLOAD: - return ctx->scopes.event_payload; - default: - abort(); - } - - return NULL; -} - -/* - * Returns the CTF scope from a path string. May return -1 if the path - * is found to be relative. - */ -static -enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr) -{ - enum ctf_scope scope; - enum ctf_scope ret = -1; - const size_t prefixes_count = sizeof(absolute_path_prefixes) / - sizeof(*absolute_path_prefixes); - - for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER + - prefixes_count; scope++) { - /* - * Chech if path string starts with a known absolute - * path prefix. - * - * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES. - */ - if (strncmp(pathstr, absolute_path_prefixes[scope], - strlen(absolute_path_prefixes[scope]))) { - /* Prefix does not match: try the next one */ - BT_LOGV("Prefix does not match: trying the next one: " - "path=\"%s\", path-prefix=\"%s\", scope=%s", - pathstr, absolute_path_prefixes[scope], - ctf_scope_string(scope)); - continue; - } - - /* Found it! */ - ret = scope; - BT_LOGV("Found root scope from absolute path: " - "path=\"%s\", scope=%s", pathstr, - ctf_scope_string(scope)); - goto end; - } - -end: - return ret; -} - -/* - * Destroys a path token. - */ -static -void ptokens_destroy_func(gpointer ptoken, gpointer data) -{ - g_string_free(ptoken, TRUE); -} - -/* - * Destroys a path token list. - */ -static -void ptokens_destroy(GList *ptokens) -{ - if (!ptokens) { - return; - } - - g_list_foreach(ptokens, ptokens_destroy_func, NULL); - g_list_free(ptokens); -} - -/* - * Returns the string contained in a path token. - */ -static -const char *ptoken_get_string(GList *ptoken) -{ - GString *tokenstr = (GString *) ptoken->data; - - return tokenstr->str; -} - -/* - * Converts a path string to a path token list, that is, splits the - * individual words of a path string into a list of individual - * strings. - */ -static -GList *pathstr_to_ptokens(const char *pathstr) -{ - const char *at = pathstr; - const char *last = at; - GList *ptokens = NULL; - - for (;;) { - if (*at == '.' || *at == '\0') { - GString *tokenstr; - - if (at == last) { - /* Error: empty token */ - BT_LOGE("Empty path token: path=\"%s\", pos=%u", - pathstr, (unsigned int) (at - pathstr)); - goto error; - } - - tokenstr = g_string_new(NULL); - g_string_append_len(tokenstr, last, at - last); - ptokens = g_list_append(ptokens, tokenstr); - last = at + 1; - } - - if (*at == '\0') { - break; - } - - at++; - } - - return ptokens; - -error: - ptokens_destroy(ptokens); - return NULL; -} - -/* - * Converts a path token list to a field path object. The path token - * list is relative from `fc`. The index of the source looking for its - * target within `fc` is indicated by `src_index`. This can be `INT64_MAX` - * if the source is contained in `fc`. - * - * `field_path` is an output parameter owned by the caller that must be - * filled here. - */ -static -int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path, - struct ctf_field_class *fc, int64_t src_index) -{ - int ret = 0; - GList *cur_ptoken = ptokens; - bool first_level_done = false; - - /* Locate target */ - while (cur_ptoken) { - int64_t child_index; - struct ctf_field_class *child_fc; - const char *ft_name = ptoken_get_string(cur_ptoken); - - BT_LOGV("Current path token: token=\"%s\"", ft_name); - - /* Find to which index corresponds the current path token */ - if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || - fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { - child_index = -1; - } else { - child_index = - ctf_field_class_compound_get_field_class_index_from_name( - fc, ft_name); - if (child_index < 0) { - /* - * Error: field name does not exist or - * wrong current class. - */ - BT_LOGV("Cannot get index of field class: " - "field-name=\"%s\", " - "src-index=%" PRId64 ", " - "child-index=%" PRId64 ", " - "first-level-done=%d", - ft_name, src_index, child_index, - first_level_done); - ret = -1; - goto end; - } else if (child_index > src_index && - !first_level_done) { - BT_LOGV("Child field class is located after source field class: " - "field-name=\"%s\", " - "src-index=%" PRId64 ", " - "child-index=%" PRId64 ", " - "first-level-done=%d", - ft_name, src_index, child_index, - first_level_done); - ret = -1; - goto end; - } - - /* Next path token */ - cur_ptoken = g_list_next(cur_ptoken); - first_level_done = true; - } - - /* Create new field path entry */ - ctf_field_path_append_index(field_path, child_index); - - /* Get child field class */ - child_fc = ctf_field_class_compound_borrow_field_class_by_index( - fc, child_index); - BT_ASSERT(child_fc); - - /* Move child class to current class */ - fc = child_fc; - } - -end: - return ret; -} - -/* - * Converts a known absolute path token list to a field path object - * within the resolving context `ctx`. - * - * `field_path` is an output parameter owned by the caller that must be - * filled here. - */ -static -int absolute_ptokens_to_field_path(GList *ptokens, - struct ctf_field_path *field_path, - struct resolve_context *ctx) -{ - int ret = 0; - GList *cur_ptoken; - struct ctf_field_class *fc; - - /* - * Make sure we're not referring to a scope within a translated - * object. - */ - switch (field_path->root) { - case CTF_SCOPE_PACKET_HEADER: - if (ctx->tc->is_translated) { - BT_LOGE("Trace class is already translated: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - break; - case CTF_SCOPE_PACKET_CONTEXT: - case CTF_SCOPE_EVENT_HEADER: - case CTF_SCOPE_EVENT_COMMON_CONTEXT: - if (!ctx->sc) { - BT_LOGE("No current stream class: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - if (ctx->sc->is_translated) { - BT_LOGE("Stream class is already translated: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - break; - case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: - case CTF_SCOPE_EVENT_PAYLOAD: - if (!ctx->ec) { - BT_LOGE("No current event class: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - if (ctx->ec->is_translated) { - BT_LOGE("Event class is already translated: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - break; - - default: - abort(); - } - - /* Skip absolute path tokens */ - cur_ptoken = g_list_nth(ptokens, - absolute_path_prefix_ptoken_counts[field_path->root]); - - /* Start with root class */ - fc = borrow_class_from_ctx(ctx, field_path->root); - if (!fc) { - /* Error: root class is not available */ - BT_LOGE("Root field class is not available: " - "root-scope=%s", - ctf_scope_string(field_path->root)); - ret = -1; - goto end; - } - - /* Locate target */ - ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX); - -end: - return ret; -} - -/* - * Converts a known relative path token list to a field path object - * within the resolving context `ctx`. - * - * `field_path` is an output parameter owned by the caller that must be - * filled here. - */ -static -int relative_ptokens_to_field_path(GList *ptokens, - struct ctf_field_path *field_path, struct resolve_context *ctx) -{ - int ret = 0; - int64_t parent_pos_in_stack; - struct ctf_field_path tail_field_path; - - ctf_field_path_init(&tail_field_path); - parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1; - - while (parent_pos_in_stack >= 0) { - struct ctf_field_class *parent_class = - field_class_stack_at(ctx->field_class_stack, - parent_pos_in_stack)->fc; - int64_t cur_index = field_class_stack_at(ctx->field_class_stack, - parent_pos_in_stack)->index; - - BT_LOGV("Locating target field class from current parent field class: " - "parent-pos=%" PRId64 ", parent-fc-addr=%p, " - "cur-index=%" PRId64, - parent_pos_in_stack, parent_class, cur_index); - - /* Locate target from current parent class */ - ret = ptokens_to_field_path(ptokens, &tail_field_path, - parent_class, cur_index); - if (ret) { - /* Not found... yet */ - BT_LOGV_STR("Not found at this point."); - ctf_field_path_clear(&tail_field_path); - } else { - /* Found: stitch tail field path to head field path */ - uint64_t i = 0; - size_t tail_field_path_len = - tail_field_path.path->len; - - while (BT_TRUE) { - struct ctf_field_class *cur_class = - field_class_stack_at( - ctx->field_class_stack, i)->fc; - int64_t index = field_class_stack_at( - ctx->field_class_stack, i)->index; - - if (cur_class == parent_class) { - break; - } - - ctf_field_path_append_index(field_path, - index); - i++; - } - - for (i = 0; i < tail_field_path_len; i++) { - int64_t index = - ctf_field_path_borrow_index_by_index( - &tail_field_path, i); - - ctf_field_path_append_index(field_path, - (int64_t) index); - } - break; - } - - parent_pos_in_stack--; - } - - if (parent_pos_in_stack < 0) { - /* Not found */ - ret = -1; - } - - ctf_field_path_fini(&tail_field_path); - return ret; -} - -/* - * Converts a path string to a field path object within the resolving - * context `ctx`. - */ -static -int pathstr_to_field_path(const char *pathstr, - struct ctf_field_path *field_path, struct resolve_context *ctx) -{ - int ret = 0; - enum ctf_scope root_scope; - GList *ptokens = NULL; - - /* Convert path string to path tokens */ - ptokens = pathstr_to_ptokens(pathstr); - if (!ptokens) { - BT_LOGE("Cannot convert path string to path tokens: " - "path=\"%s\"", pathstr); - ret = -1; - goto end; - } - - /* Absolute or relative path? */ - root_scope = get_root_scope_from_absolute_pathstr(pathstr); - - if (root_scope == -1) { - /* Relative path: start with current root scope */ - field_path->root = ctx->root_scope; - BT_LOGV("Detected relative path: starting with current root scope: " - "scope=%s", ctf_scope_string(field_path->root)); - ret = relative_ptokens_to_field_path(ptokens, field_path, ctx); - if (ret) { - BT_LOGE("Cannot get relative field path of path string: " - "path=\"%s\", start-scope=%s, end-scope=%s", - pathstr, ctf_scope_string(ctx->root_scope), - ctf_scope_string(field_path->root)); - goto end; - } - } else { - /* Absolute path: use found root scope */ - field_path->root = root_scope; - BT_LOGV("Detected absolute path: using root scope: " - "scope=%s", ctf_scope_string(field_path->root)); - ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx); - if (ret) { - BT_LOGE("Cannot get absolute field path of path string: " - "path=\"%s\", root-scope=%s", - pathstr, ctf_scope_string(root_scope)); - goto end; - } - } - - if (BT_LOG_ON_VERBOSE && ret == 0) { - GString *field_path_pretty = ctf_field_path_string(field_path); - const char *field_path_pretty_str = - field_path_pretty ? field_path_pretty->str : NULL; - - BT_LOGV("Found field path: path=\"%s\", field-path=\"%s\"", - pathstr, field_path_pretty_str); - - if (field_path_pretty) { - g_string_free(field_path_pretty, TRUE); - } - } - -end: - ptokens_destroy(ptokens); - return ret; -} - -/* - * Retrieves a field class by following the field path `field_path` in - * the resolving context `ctx`. - */ -static -struct ctf_field_class *field_path_to_field_class( - struct ctf_field_path *field_path, struct resolve_context *ctx) -{ - uint64_t i; - struct ctf_field_class *fc; - - /* Start with root class */ - fc = borrow_class_from_ctx(ctx, field_path->root); - if (!fc) { - /* Error: root class is not available */ - BT_LOGE("Root field class is not available: root-scope=%s", - ctf_scope_string(field_path->root)); - goto end; - } - - /* Locate target */ - for (i = 0; i < field_path->path->len; i++) { - struct ctf_field_class *child_fc; - int64_t child_index = - ctf_field_path_borrow_index_by_index(field_path, i); - - /* Get child field class */ - child_fc = ctf_field_class_compound_borrow_field_class_by_index( - fc, child_index); - BT_ASSERT(child_fc); - - /* Move child class to current class */ - fc = child_fc; - } - -end: - return fc; -} - -/* - * Fills the equivalent field path object of the context class stack. - */ -static -void get_ctx_stack_field_path(struct resolve_context *ctx, - struct ctf_field_path *field_path) -{ - uint64_t i; - - BT_ASSERT(field_path); - field_path->root = ctx->root_scope; - ctf_field_path_clear(field_path); - - for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) { - struct field_class_stack_frame *frame = - field_class_stack_at(ctx->field_class_stack, i); - - ctf_field_path_append_index(field_path, frame->index); - } -} - -/* - * Returns the index of the lowest common ancestor of two field path - * objects having the same root scope. - */ -int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1, - struct ctf_field_path *field_path2) -{ - int64_t lca_index = 0; - uint64_t field_path1_len, field_path2_len; - - if (BT_LOG_ON_VERBOSE) { - GString *field_path1_pretty = - ctf_field_path_string(field_path1); - GString *field_path2_pretty = - ctf_field_path_string(field_path2); - const char *field_path1_pretty_str = - field_path1_pretty ? field_path1_pretty->str : NULL; - const char *field_path2_pretty_str = - field_path2_pretty ? field_path2_pretty->str : NULL; - - BT_LOGV("Finding lowest common ancestor (LCA) between two field paths: " - "field-path-1=\"%s\", field-path-2=\"%s\"", - field_path1_pretty_str, field_path2_pretty_str); - - if (field_path1_pretty) { - g_string_free(field_path1_pretty, TRUE); - } - - if (field_path2_pretty) { - g_string_free(field_path2_pretty, TRUE); - } - } - - /* - * Start from both roots and find the first mismatch. - */ - BT_ASSERT(field_path1->root == field_path2->root); - field_path1_len = field_path1->path->len; - field_path2_len = field_path2->path->len; - - while (true) { - int64_t target_index, ctx_index; - - if (lca_index == (int64_t) field_path2_len || - lca_index == (int64_t) field_path1_len) { - /* - * This means that both field paths never split. - * This is invalid because the target cannot be - * an ancestor of the source. - */ - BT_LOGE("Source field class is an ancestor of target field class or vice versa: " - "lca-index=%" PRId64 ", " - "field-path-1-len=%" PRIu64 ", " - "field-path-2-len=%" PRIu64, - lca_index, field_path1_len, field_path2_len); - lca_index = -1; - break; - } - - target_index = ctf_field_path_borrow_index_by_index(field_path1, - lca_index); - ctx_index = ctf_field_path_borrow_index_by_index(field_path2, - lca_index); - - if (target_index != ctx_index) { - /* LCA index is the previous */ - break; - } - - lca_index++; - } - - BT_LOGV("Found LCA: lca-index=%" PRId64, lca_index); - return lca_index; -} - -/* - * Validates a target field path. - */ -static -int validate_target_field_path(struct ctf_field_path *target_field_path, - struct ctf_field_class *target_fc, - struct resolve_context *ctx) -{ - int ret = 0; - struct ctf_field_path ctx_field_path; - uint64_t target_field_path_len = target_field_path->path->len; - int64_t lca_index; - - /* Get context field path */ - ctf_field_path_init(&ctx_field_path); - get_ctx_stack_field_path(ctx, &ctx_field_path); - - /* - * Make sure the target is not a root. - */ - if (target_field_path_len == 0) { - BT_LOGE_STR("Target field path's length is 0 (targeting the root)."); - ret = -1; - goto end; - } - - /* - * Make sure the root of the target field path is not located - * after the context field path's root. - */ - if (target_field_path->root > ctx_field_path.root) { - BT_LOGE("Target field class is located after source field class: " - "target-root=%s, source-root=%s", - ctf_scope_string(target_field_path->root), - ctf_scope_string(ctx_field_path.root)); - ret = -1; - goto end; - } - - if (target_field_path->root == ctx_field_path.root) { - int64_t target_index, ctx_index; - - /* - * Find the index of the lowest common ancestor of both field - * paths. - */ - lca_index = get_field_paths_lca_index(target_field_path, - &ctx_field_path); - if (lca_index < 0) { - BT_LOGE_STR("Cannot get least common ancestor."); - ret = -1; - goto end; - } - - /* - * Make sure the target field path is located before the - * context field path. - */ - target_index = ctf_field_path_borrow_index_by_index( - target_field_path, (uint64_t) lca_index); - ctx_index = ctf_field_path_borrow_index_by_index( - &ctx_field_path, (uint64_t) lca_index); - - if (target_index >= ctx_index) { - BT_LOGE("Target field class's index is greater than or equal to source field class's index in LCA: " - "lca-index=%" PRId64 ", " - "target-index=%" PRId64 ", " - "source-index=%" PRId64, - lca_index, target_index, ctx_index); - ret = -1; - goto end; - } - } - - /* - * Make sure the target class has the right class and properties. - */ - switch (ctx->cur_fc->type) { - case CTF_FIELD_CLASS_TYPE_VARIANT: - if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE("Variant field class's tag field class is not an enumeration field class: " - "tag-fc-addr=%p, tag-fc-id=%d", - target_fc, target_fc->type); - ret = -1; - goto end; - } - break; - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_int *int_fc = (void *) target_fc; - - if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT && - target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE("Sequence field class's length field class is not an unsigned integer field class: " - "length-fc-addr=%p, length-fc-id=%d", - target_fc, target_fc->type); - ret = -1; - goto end; - } - - if (int_fc->is_signed) { - BT_LOGE("Sequence field class's length field class is not an unsigned integer field class: " - "length-fc-addr=%p, length-fc-id=%d", - target_fc, target_fc->type); - ret = -1; - goto end; - } - break; - } - default: - abort(); - } - -end: - ctf_field_path_fini(&ctx_field_path); - return ret; -} - -/* - * Resolves a variant or sequence field class `fc`. - */ -static -int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc, - struct resolve_context *ctx) -{ - int ret = 0; - const char *pathstr; - struct ctf_field_path target_field_path; - struct ctf_field_class *target_fc = NULL; - GString *target_field_path_pretty = NULL; - const char *target_field_path_pretty_str; - - ctf_field_path_init(&target_field_path); - - /* Get path string */ - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_sequence *seq_fc = (void *) fc; - pathstr = seq_fc->length_ref->str; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - pathstr = var_fc->tag_ref->str; - break; - } - default: - abort(); - } - - if (!pathstr) { - BT_LOGE_STR("Cannot get path string."); - ret = -1; - goto end; - } - - /* Get target field path out of path string */ - ret = pathstr_to_field_path(pathstr, &target_field_path, ctx); - if (ret) { - BT_LOGE("Cannot get target field path for path string: " - "path=\"%s\"", pathstr); - goto end; - } - - target_field_path_pretty = ctf_field_path_string( - &target_field_path); - target_field_path_pretty_str = - target_field_path_pretty ? target_field_path_pretty->str : NULL; - - /* Get target field class */ - target_fc = field_path_to_field_class(&target_field_path, ctx); - if (!target_fc) { - BT_LOGE("Cannot get target field class for path string: " - "path=\"%s\", target-field-path=\"%s\"", - pathstr, target_field_path_pretty_str); - ret = -1; - goto end; - } - - ret = validate_target_field_path(&target_field_path, - target_fc, ctx); - if (ret) { - BT_LOGE("Invalid target field path for path string: " - "path=\"%s\", target-field-path=\"%s\"", - pathstr, target_field_path_pretty_str); - goto end; - } - - /* Set target field path and target field class */ - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_sequence *seq_fc = (void *) fc; - - ctf_field_path_copy_content(&seq_fc->length_path, - &target_field_path); - seq_fc->length_fc = (void *) target_fc; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - ctf_field_path_copy_content(&var_fc->tag_path, - &target_field_path); - ctf_field_class_variant_set_tag_field_class(var_fc, - (void *) target_fc); - break; - } - default: - abort(); - } - -end: - if (target_field_path_pretty) { - g_string_free(target_field_path_pretty, TRUE); - } - - ctf_field_path_fini(&target_field_path); - return ret; -} - -/* - * Resolves a field class `fc`. - */ -static -int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx) -{ - int ret = 0; - - if (!fc) { - /* Field class is not available; still valid */ - goto end; - } - - ctx->cur_fc = fc; - - /* Resolve sequence/variant field class */ - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - case CTF_FIELD_CLASS_TYPE_VARIANT: - ret = resolve_sequence_or_variant_field_class(fc, ctx); - if (ret) { - BT_LOGE("Cannot resolve sequence field class's length or variant field class's tag: " - "ret=%d, fc-addr=%p", ret, fc); - goto end; - } - - break; - default: - break; - } - - /* Recurse into compound classes */ - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - case CTF_FIELD_CLASS_TYPE_VARIANT: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - case CTF_FIELD_CLASS_TYPE_ARRAY: - { - uint64_t i; - uint64_t field_count = - ctf_field_class_compound_get_field_class_count(fc); - - ret = field_class_stack_push(ctx->field_class_stack, fc); - if (ret) { - BT_LOGE("Cannot push field class on context's stack: " - "fc-addr=%p", fc); - goto end; - } - - for (i = 0; i < field_count; i++) { - struct ctf_field_class *child_fc = - ctf_field_class_compound_borrow_field_class_by_index( - fc, i); - - BT_ASSERT(child_fc); - - if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY|| - fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { - field_class_stack_peek( - ctx->field_class_stack)->index = -1; - } else { - field_class_stack_peek( - ctx->field_class_stack)->index = - (int64_t) i; - } - - BT_LOGV("Resolving field class's child field class: " - "parent-fc-addr=%p, child-fc-addr=%p, " - "index=%" PRIu64 ", count=%" PRIu64, - fc, child_fc, i, field_count); - ret = resolve_field_class(child_fc, ctx); - if (ret) { - goto end; - } - } - - field_class_stack_pop(ctx->field_class_stack); - break; - } - default: - break; - } - -end: - return ret; -} - -/* - * Resolves the root field class corresponding to the scope `root_scope`. - */ -static -int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx) -{ - int ret; - - BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0); - ctx->root_scope = root_scope; - ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx); - ctx->root_scope = -1; - return ret; -} - -static -int resolve_event_class_field_classes(struct resolve_context *ctx, - struct ctf_event_class *ec) -{ - int ret = 0; - - BT_ASSERT(!ctx->scopes.event_spec_context); - BT_ASSERT(!ctx->scopes.event_payload); - - if (ec->is_translated) { - goto end; - } - - ctx->ec = ec; - ctx->scopes.event_spec_context = ec->spec_context_fc; - ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx); - if (ret) { - BT_LOGE("Cannot resolve event specific context field class: " - "ret=%d", ret); - goto end; - } - - ctx->scopes.event_payload = ec->payload_fc; - ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx); - if (ret) { - BT_LOGE("Cannot resolve event payload field class: " - "ret=%d", ret); - goto end; - } - -end: - ctx->scopes.event_spec_context = NULL; - ctx->scopes.event_payload = NULL; - ctx->ec = NULL; - return ret; -} - -static -int resolve_stream_class_field_classes(struct resolve_context *ctx, - struct ctf_stream_class *sc) -{ - int ret = 0; - uint64_t i; - - BT_ASSERT(!ctx->scopes.packet_context); - BT_ASSERT(!ctx->scopes.event_header); - BT_ASSERT(!ctx->scopes.event_common_context); - ctx->sc = sc; - - if (!sc->is_translated) { - ctx->scopes.packet_context = sc->packet_context_fc; - ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx); - if (ret) { - BT_LOGE("Cannot resolve packet context field class: " - "ret=%d", ret); - goto end; - } - - ctx->scopes.event_header = sc->event_header_fc; - ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx); - if (ret) { - BT_LOGE("Cannot resolve event header field class: " - "ret=%d", ret); - goto end; - } - - ctx->scopes.event_common_context = sc->event_common_context_fc; - ret = resolve_root_class(CTF_SCOPE_EVENT_SPECIFIC_CONTEXT, ctx); - if (ret) { - BT_LOGE("Cannot resolve event common context field class: " - "ret=%d", ret); - goto end; - } - } - - ctx->scopes.packet_context = sc->packet_context_fc; - ctx->scopes.event_header = sc->event_header_fc; - ctx->scopes.event_common_context = sc->event_common_context_fc; - - for (i = 0; i < sc->event_classes->len; i++) { - struct ctf_event_class *ec = sc->event_classes->pdata[i]; - - ret = resolve_event_class_field_classes(ctx, ec); - if (ret) { - BT_LOGE("Cannot resolve event class's field classes: " - "ec-id=%" PRIu64 ", ec-name=\"%s\"", - ec->id, ec->name->str); - goto end; - } - } - -end: - ctx->scopes.packet_context = NULL; - ctx->scopes.event_header = NULL; - ctx->scopes.event_common_context = NULL; - ctx->sc = NULL; - return ret; -} - -BT_HIDDEN -int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc) -{ - int ret = 0; - uint64_t i; - struct resolve_context ctx = { - .tc = tc, - .sc = NULL, - .ec = NULL, - .scopes = { - .packet_header = tc->packet_header_fc, - .packet_context = NULL, - .event_header = NULL, - .event_common_context = NULL, - .event_spec_context = NULL, - .event_payload = NULL, - }, - .root_scope = CTF_SCOPE_PACKET_HEADER, - .cur_fc = NULL, - }; - - /* Initialize class stack */ - ctx.field_class_stack = field_class_stack_create(); - if (!ctx.field_class_stack) { - BT_LOGE_STR("Cannot create field class stack."); - ret = -1; - goto end; - } - - if (!tc->is_translated) { - ctx.scopes.packet_header = tc->packet_header_fc; - ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, &ctx); - if (ret) { - BT_LOGE("Cannot resolve packet header field class: " - "ret=%d", ret); - goto end; - } - } - - ctx.scopes.packet_header = tc->packet_header_fc; - - for (i = 0; i < tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = tc->stream_classes->pdata[i]; - - ret = resolve_stream_class_field_classes(&ctx, sc); - if (ret) { - BT_LOGE("Cannot resolve stream class's field classes: " - "sc-id=%" PRIu64, sc->id); - goto end; - } - } - -end: - field_class_stack_destroy(ctx.field_class_stack); - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-translate.c b/plugins/ctf/common/metadata/ctf-meta-translate.c deleted file mode 100644 index ca3069ae..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-translate.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-TRANSLATE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -struct ctx { - bt_self_component_source *self_comp; - bt_trace_class *ir_tc; - bt_stream_class *ir_sc; - struct ctf_trace_class *tc; - struct ctf_stream_class *sc; - struct ctf_event_class *ec; - enum ctf_scope scope; -}; - -static inline -bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, - struct ctf_field_class *fc); - -static inline -void ctf_field_class_int_set_props(struct ctf_field_class_int *fc, - bt_field_class *ir_fc) -{ - bt_field_class_integer_set_field_value_range(ir_fc, - fc->base.size); - bt_field_class_integer_set_preferred_display_base(ir_fc, - fc->disp_base); -} - -static inline -bt_field_class *ctf_field_class_int_to_ir(struct ctx *ctx, - struct ctf_field_class_int *fc) -{ - bt_field_class *ir_fc; - - if (fc->is_signed) { - ir_fc = bt_field_class_signed_integer_create(ctx->ir_tc); - } else { - ir_fc = bt_field_class_unsigned_integer_create(ctx->ir_tc); - } - - BT_ASSERT(ir_fc); - ctf_field_class_int_set_props(fc, ir_fc); - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_enum_to_ir(struct ctx *ctx, - struct ctf_field_class_enum *fc) -{ - int ret; - bt_field_class *ir_fc; - uint64_t i; - - if (fc->base.is_signed) { - ir_fc = bt_field_class_signed_enumeration_create(ctx->ir_tc); - } else { - ir_fc = bt_field_class_unsigned_enumeration_create(ctx->ir_tc); - } - - BT_ASSERT(ir_fc); - ctf_field_class_int_set_props((void *) fc, ir_fc); - - for (i = 0; i < fc->mappings->len; i++) { - struct ctf_field_class_enum_mapping *mapping = - ctf_field_class_enum_borrow_mapping_by_index(fc, i); - - if (fc->base.is_signed) { - ret = bt_field_class_signed_enumeration_map_range( - ir_fc, mapping->label->str, - mapping->range.lower.i, mapping->range.upper.i); - } else { - ret = bt_field_class_unsigned_enumeration_map_range( - ir_fc, mapping->label->str, - mapping->range.lower.u, mapping->range.upper.u); - } - - BT_ASSERT(ret == 0); - } - - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_float_to_ir(struct ctx *ctx, - struct ctf_field_class_float *fc) -{ - bt_field_class *ir_fc; - - ir_fc = bt_field_class_real_create(ctx->ir_tc); - BT_ASSERT(ir_fc); - - if (fc->base.size == 32) { - bt_field_class_real_set_is_single_precision(ir_fc, - BT_TRUE); - } - - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_string_to_ir(struct ctx *ctx, - struct ctf_field_class_string *fc) -{ - bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc); - - BT_ASSERT(ir_fc); - return ir_fc; -} - -static inline -void translate_struct_field_class_members(struct ctx *ctx, - struct ctf_field_class_struct *fc, bt_field_class *ir_fc, - bool with_header_prefix, - struct ctf_field_class_struct *context_fc) -{ - uint64_t i; - int ret; - - for (i = 0; i < fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index(fc, i); - bt_field_class *member_ir_fc; - const char *name = named_fc->name->str; - - if (!named_fc->fc->in_ir) { - continue; - } - - member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc); - BT_ASSERT(member_ir_fc); - ret = bt_field_class_structure_append_member(ir_fc, name, - member_ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(member_ir_fc); - } -} - -static inline -bt_field_class *ctf_field_class_struct_to_ir(struct ctx *ctx, - struct ctf_field_class_struct *fc) -{ - bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc); - - BT_ASSERT(ir_fc); - translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL); - return ir_fc; -} - -static inline -bt_field_class *borrow_ir_fc_from_field_path(struct ctx *ctx, - struct ctf_field_path *field_path) -{ - bt_field_class *ir_fc = NULL; - struct ctf_field_class *fc = ctf_field_path_borrow_field_class( - field_path, ctx->tc, ctx->sc, ctx->ec); - - BT_ASSERT(fc); - - if (fc->in_ir) { - ir_fc = fc->ir_fc; - } - - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_variant_to_ir(struct ctx *ctx, - struct ctf_field_class_variant *fc) -{ - int ret; - bt_field_class *ir_fc = bt_field_class_variant_create(ctx->ir_tc); - uint64_t i; - - BT_ASSERT(ir_fc); - - if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER && - fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) { - ret = bt_field_class_variant_set_selector_field_class( - ir_fc, borrow_ir_fc_from_field_path(ctx, - &fc->tag_path)); - BT_ASSERT(ret == 0); - } - - for (i = 0; i < fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index(fc, i); - bt_field_class *option_ir_fc; - - BT_ASSERT(named_fc->fc->in_ir); - option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc); - BT_ASSERT(option_ir_fc); - ret = bt_field_class_variant_append_option( - ir_fc, named_fc->name->str, option_ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(option_ir_fc); - } - - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_array_to_ir(struct ctx *ctx, - struct ctf_field_class_array *fc) -{ - bt_field_class *ir_fc; - bt_field_class *elem_ir_fc; - - if (fc->base.is_text) { - ir_fc = bt_field_class_string_create(ctx->ir_tc); - BT_ASSERT(ir_fc); - goto end; - } - - elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc); - BT_ASSERT(elem_ir_fc); - ir_fc = bt_field_class_static_array_create(ctx->ir_tc, elem_ir_fc, - fc->length); - BT_ASSERT(ir_fc); - bt_field_class_put_ref(elem_ir_fc); - -end: - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_sequence_to_ir(struct ctx *ctx, - struct ctf_field_class_sequence *fc) -{ - int ret; - bt_field_class *ir_fc; - bt_field_class *elem_ir_fc; - - if (fc->base.is_text) { - ir_fc = bt_field_class_string_create(ctx->ir_tc); - BT_ASSERT(ir_fc); - goto end; - } - - elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc); - BT_ASSERT(elem_ir_fc); - ir_fc = bt_field_class_dynamic_array_create(ctx->ir_tc, elem_ir_fc); - BT_ASSERT(ir_fc); - bt_field_class_put_ref(elem_ir_fc); - BT_ASSERT(ir_fc); - - if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER && - fc->length_path.root != CTF_SCOPE_EVENT_HEADER) { - ret = bt_field_class_dynamic_array_set_length_field_class( - ir_fc, borrow_ir_fc_from_field_path(ctx, &fc->length_path)); - BT_ASSERT(ret == 0); - } - -end: - return ir_fc; -} - -static inline -bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, - struct ctf_field_class *fc) -{ - bt_field_class *ir_fc = NULL; - - BT_ASSERT(fc); - BT_ASSERT(fc->in_ir); - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - ir_fc = ctf_field_class_int_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ENUM: - ir_fc = ctf_field_class_enum_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_FLOAT: - ir_fc = ctf_field_class_float_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRING: - ir_fc = ctf_field_class_string_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRUCT: - ir_fc = ctf_field_class_struct_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ARRAY: - ir_fc = ctf_field_class_array_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - ir_fc = ctf_field_class_sequence_to_ir(ctx, (void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_VARIANT: - ir_fc = ctf_field_class_variant_to_ir(ctx, (void *) fc); - break; - default: - abort(); - } - - fc->ir_fc = ir_fc; - return ir_fc; -} - -static inline -bool ctf_field_class_struct_has_immediate_member_in_ir( - struct ctf_field_class_struct *fc) -{ - uint64_t i; - bool has_immediate_member_in_ir = false; - - /* - * If the structure field class has no members at all, then it - * was an empty structure in the beginning, so leave it existing - * and empty. - */ - if (fc->members->len == 0) { - has_immediate_member_in_ir = true; - goto end; - } - - for (i = 0; i < fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index(fc, i); - - if (named_fc->fc->in_ir) { - has_immediate_member_in_ir = true; - goto end; - } - } - -end: - return has_immediate_member_in_ir; -} - -static inline -bt_field_class *scope_ctf_field_class_to_ir(struct ctx *ctx) -{ - bt_field_class *ir_fc = NULL; - struct ctf_field_class *fc = NULL; - - switch (ctx->scope) { - case CTF_SCOPE_PACKET_CONTEXT: - fc = ctx->sc->packet_context_fc; - break; - case CTF_SCOPE_EVENT_COMMON_CONTEXT: - fc = ctx->sc->event_common_context_fc; - break; - case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: - fc = ctx->ec->spec_context_fc; - break; - case CTF_SCOPE_EVENT_PAYLOAD: - fc = ctx->ec->payload_fc; - break; - default: - abort(); - } - - if (fc && ctf_field_class_struct_has_immediate_member_in_ir( - (void *) fc)) { - ir_fc = ctf_field_class_to_ir(ctx, fc); - } - - return ir_fc; -} - -static inline -void ctf_event_class_to_ir(struct ctx *ctx) -{ - int ret; - bt_event_class *ir_ec = NULL; - bt_field_class *ir_fc; - - BT_ASSERT(ctx->ec); - - if (ctx->ec->is_translated) { - ir_ec = bt_stream_class_borrow_event_class_by_id( - ctx->ir_sc, ctx->ec->id); - BT_ASSERT(ir_ec); - goto end; - } - - ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id); - BT_ASSERT(ir_ec); - bt_event_class_put_ref(ir_ec); - ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT; - ir_fc = scope_ctf_field_class_to_ir(ctx); - if (ir_fc) { - ret = bt_event_class_set_specific_context_field_class( - ir_ec, ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(ir_fc); - } - - ctx->scope = CTF_SCOPE_EVENT_PAYLOAD; - ir_fc = scope_ctf_field_class_to_ir(ctx); - if (ir_fc) { - ret = bt_event_class_set_payload_field_class(ir_ec, - ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(ir_fc); - } - - if (ctx->ec->name->len > 0) { - ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str); - BT_ASSERT(ret == 0); - } - - if (ctx->ec->emf_uri->len > 0) { - ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str); - BT_ASSERT(ret == 0); - } - - if (ctx->ec->log_level != -1) { - bt_event_class_set_log_level(ir_ec, ctx->ec->log_level); - } - - ctx->ec->is_translated = true; - ctx->ec->ir_ec = ir_ec; - -end: - return; -} - - -static inline -void ctf_stream_class_to_ir(struct ctx *ctx) -{ - int ret; - bt_field_class *ir_fc; - - BT_ASSERT(ctx->sc); - - if (ctx->sc->is_translated) { - ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id( - ctx->ir_tc, ctx->sc->id); - BT_ASSERT(ctx->ir_sc); - goto end; - } - - ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id); - BT_ASSERT(ctx->ir_sc); - bt_stream_class_put_ref(ctx->ir_sc); - ctx->scope = CTF_SCOPE_PACKET_CONTEXT; - ir_fc = scope_ctf_field_class_to_ir(ctx); - if (ir_fc) { - ret = bt_stream_class_set_packet_context_field_class( - ctx->ir_sc, ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(ir_fc); - } - - ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT; - ir_fc = scope_ctf_field_class_to_ir(ctx); - if (ir_fc) { - ret = bt_stream_class_set_event_common_context_field_class( - ctx->ir_sc, ir_fc); - BT_ASSERT(ret == 0); - bt_field_class_put_ref(ir_fc); - } - - bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc, - BT_FALSE); - bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE); - - if (ctx->sc->default_clock_class) { - BT_ASSERT(ctx->sc->default_clock_class->ir_cc); - ret = bt_stream_class_set_default_clock_class(ctx->ir_sc, - ctx->sc->default_clock_class->ir_cc); - BT_ASSERT(ret == 0); - } - - bt_stream_class_set_packets_have_beginning_default_clock_snapshot( - ctx->ir_sc, ctx->sc->packets_have_ts_begin); - bt_stream_class_set_packets_have_end_default_clock_snapshot( - ctx->ir_sc, ctx->sc->packets_have_ts_end); - bt_stream_class_set_supports_discarded_events(ctx->ir_sc, - ctx->sc->has_discarded_events, - ctx->sc->discarded_events_have_default_cs); - bt_stream_class_set_supports_discarded_packets(ctx->ir_sc, - ctx->sc->has_discarded_packets, - ctx->sc->discarded_packets_have_default_cs); - ctx->sc->is_translated = true; - ctx->sc->ir_sc = ctx->ir_sc; - -end: - return; -} - -static inline -void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc) -{ - int ret; - - if (strlen(cc->name->str) > 0) { - ret = bt_clock_class_set_name(ir_cc, cc->name->str); - BT_ASSERT(ret == 0); - } - - if (strlen(cc->description->str) > 0) { - ret = bt_clock_class_set_description(ir_cc, cc->description->str); - BT_ASSERT(ret == 0); - } - - bt_clock_class_set_frequency(ir_cc, cc->frequency); - bt_clock_class_set_precision(ir_cc, cc->precision); - bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles); - - if (cc->has_uuid) { - bt_clock_class_set_uuid(ir_cc, cc->uuid); - } - - bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute); -} - -static inline -int ctf_trace_class_to_ir(struct ctx *ctx) -{ - int ret = 0; - uint64_t i; - - BT_ASSERT(ctx->tc); - BT_ASSERT(ctx->ir_tc); - - if (ctx->tc->is_translated) { - goto end; - } - - if (ctx->tc->is_uuid_set) { - bt_trace_class_set_uuid(ctx->ir_tc, ctx->tc->uuid); - } - - for (i = 0; i < ctx->tc->env_entries->len; i++) { - struct ctf_trace_class_env_entry *env_entry = - ctf_trace_class_borrow_env_entry_by_index(ctx->tc, i); - - switch (env_entry->type) { - case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT: - ret = bt_trace_class_set_environment_entry_integer( - ctx->ir_tc, env_entry->name->str, - env_entry->value.i); - break; - case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR: - ret = bt_trace_class_set_environment_entry_string( - ctx->ir_tc, env_entry->name->str, - env_entry->value.str->str); - break; - default: - abort(); - } - - if (ret) { - goto end; - } - } - - for (i = 0; i < ctx->tc->clock_classes->len; i++) { - struct ctf_clock_class *cc = ctx->tc->clock_classes->pdata[i]; - - cc->ir_cc = bt_clock_class_create( - bt_self_component_source_as_self_component( - ctx->self_comp)); - ctf_clock_class_to_ir(cc->ir_cc, cc); - } - - bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc, - BT_FALSE); - ctx->tc->is_translated = true; - ctx->tc->ir_tc = ctx->ir_tc; - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_translate(bt_self_component_source *self_comp, - bt_trace_class *ir_tc, struct ctf_trace_class *tc) -{ - int ret = 0; - uint64_t i; - struct ctx ctx = { 0 }; - - ctx.self_comp = self_comp; - ctx.tc = tc; - ctx.ir_tc = ir_tc; - ret = ctf_trace_class_to_ir(&ctx); - if (ret) { - goto end; - } - - for (i = 0; i < tc->stream_classes->len; i++) { - uint64_t j; - ctx.sc = tc->stream_classes->pdata[i]; - - ctf_stream_class_to_ir(&ctx); - - for (j = 0; j < ctx.sc->event_classes->len; j++) { - ctx.ec = ctx.sc->event_classes->pdata[j]; - - ctf_event_class_to_ir(&ctx); - ctx.ec = NULL; - } - - ctx.sc = NULL; - } - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c b/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c deleted file mode 100644 index 057e0b4e..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-DEF-CC" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static inline -int find_mapped_clock_class(struct ctf_field_class *fc, - struct ctf_clock_class **clock_class) -{ - int ret = 0; - uint64_t i; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - { - struct ctf_field_class_int *int_fc = (void *) fc; - - if (int_fc->mapped_clock_class) { - if (*clock_class && *clock_class != - int_fc->mapped_clock_class) { - BT_LOGE("Stream class contains more than one " - "clock class: expected-cc-name=\"%s\", " - "other-cc-name=\"%s\"", - (*clock_class)->name->str, - int_fc->mapped_clock_class->name->str); - ret = -1; - goto end; - } - - *clock_class = int_fc->mapped_clock_class; - } - - break; - } - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - ret = find_mapped_clock_class(named_fc->fc, - clock_class); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - ret = find_mapped_clock_class(named_fc->fc, - clock_class); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - ret = find_mapped_clock_class(array_fc->elem_fc, clock_class); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -static inline -int update_stream_class_default_clock_class( - struct ctf_stream_class *stream_class) -{ - int ret = 0; - struct ctf_clock_class *clock_class = - stream_class->default_clock_class; - uint64_t i; - - ret = find_mapped_clock_class(stream_class->packet_context_fc, - &clock_class); - if (ret) { - goto end; - } - - ret = find_mapped_clock_class(stream_class->event_header_fc, - &clock_class); - if (ret) { - goto end; - } - - ret = find_mapped_clock_class(stream_class->event_common_context_fc, - &clock_class); - if (ret) { - goto end; - } - - for (i = 0; i < stream_class->event_classes->len; i++) { - struct ctf_event_class *event_class = - stream_class->event_classes->pdata[i]; - - ret = find_mapped_clock_class(event_class->spec_context_fc, - &clock_class); - if (ret) { - goto end; - } - - ret = find_mapped_clock_class(event_class->payload_fc, - &clock_class); - if (ret) { - goto end; - } - } - - if (!stream_class->default_clock_class) { - stream_class->default_clock_class = clock_class; - } - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc) -{ - uint64_t i; - int ret = 0; - struct ctf_clock_class *clock_class = NULL; - - ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class); - if (ret) { - goto end; - } - - if (clock_class) { - ret = -1; - goto end; - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = - ctf_tc->stream_classes->pdata[i]; - - ret = update_stream_class_default_clock_class( - ctf_tc->stream_classes->pdata[i]); - if (ret) { - BT_LOGE("Stream class contains more than one " - "clock class: stream-class-id=%" PRIu64, - sc->id); - goto end; - } - } - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c b/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c deleted file mode 100644 index 806a9197..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-IN-IR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static -void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir) -{ - uint64_t i; - - if (!fc) { - goto end; - } - - fc->in_ir = in_ir; - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - force_update_field_class_in_ir(named_fc->fc, in_ir); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_named_field_class *named_fc; - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - force_update_field_class_in_ir(named_fc->fc, in_ir); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - force_update_field_class_in_ir(array_fc->elem_fc, in_ir); - break; - } - default: - break; - } - -end: - return; -} - -static -void update_field_class_in_ir(struct ctf_field_class *fc, - GHashTable *ft_dependents) -{ - int64_t i; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - { - struct ctf_field_class_int *int_fc = (void *) fc; - - /* - * Conditions to be in trace IR; one of: - * - * 1. Does NOT have a mapped clock class AND does not - * have a special meaning. - * 2. Another field class depends on it. - */ - if ((!int_fc->mapped_clock_class && - int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) || - bt_g_hash_table_contains(ft_dependents, fc)) { - fc->in_ir = true; - } - - break; - } - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - /* - * Make it part of IR if it's empty because it was - * originally empty. - */ - if (struct_fc->members->len == 0) { - fc->in_ir = true; - } - - /* Reverse order */ - for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - update_field_class_in_ir(named_fc->fc, ft_dependents); - - if (named_fc->fc->in_ir) { - /* At least one member is part of IR */ - fc->in_ir = true; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_named_field_class *named_fc; - struct ctf_field_class_variant *var_fc = (void *) fc; - - /* - * Reverse order, although it is not important for this - * loop because a field class within a variant field - * type's option cannot depend on a field class in - * another option of the same variant field class. - */ - for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) { - named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - update_field_class_in_ir(named_fc->fc, ft_dependents); - - if (named_fc->fc->in_ir) { - /* At least one option is part of IR */ - fc->in_ir = true; - } - } - - if (fc->in_ir) { - /* - * At least one option will make it to IR. In - * this case, make all options part of IR - * because the variant's tag could still select - * (dynamically) a removed option. This can mean - * having an empty structure as an option, for - * example, but at least all the options are - * selectable. - */ - for (i = 0; i < var_fc->options->len; i++) { - ctf_field_class_variant_borrow_option_by_index( - var_fc, i)->fc->in_ir = true; - } - - /* - * This variant field class is part of IR and - * depends on a tag field class (which must also - * be part of IR). - */ - g_hash_table_insert(ft_dependents, var_fc->tag_fc, - var_fc->tag_fc); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - update_field_class_in_ir(array_fc->elem_fc, ft_dependents); - fc->in_ir = array_fc->elem_fc->in_ir; - - if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) { - struct ctf_field_class_array *arr_fc = (void *) fc; - - assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE || - arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID); - - /* - * UUID field class: nothing depends on this, so - * it's not part of IR. - */ - if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) { - fc->in_ir = false; - array_fc->elem_fc->in_ir = false; - } - } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { - if (fc->in_ir) { - struct ctf_field_class_sequence *seq_fc = (void *) fc; - - /* - * This sequence field class is part of - * IR and depends on a length field class - * (which must also be part of IR). - */ - g_hash_table_insert(ft_dependents, - seq_fc->length_fc, seq_fc->length_fc); - } - } - - break; - } - default: - fc->in_ir = true; - break; - } - -end: - return; -} - -/* - * Scopes and field classes are processed in reverse order because we need - * to know if a given integer field class has dependents (sequence or - * variant field classes) when we reach it. Dependents can only be located - * after the length/tag field class in the metadata tree. - */ -BT_HIDDEN -int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc) -{ - int ret = 0; - uint64_t i; - - GHashTable *ft_dependents = g_hash_table_new(g_direct_hash, - g_direct_equal); - - BT_ASSERT(ft_dependents); - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; - uint64_t j; - - for (j = 0; j < sc->event_classes->len; j++) { - struct ctf_event_class *ec = sc->event_classes->pdata[j]; - - if (ec->is_translated) { - continue; - } - - update_field_class_in_ir(ec->payload_fc, ft_dependents); - update_field_class_in_ir(ec->spec_context_fc, - ft_dependents); - } - - if (!sc->is_translated) { - update_field_class_in_ir(sc->event_common_context_fc, - ft_dependents); - force_update_field_class_in_ir(sc->event_header_fc, - false); - update_field_class_in_ir(sc->packet_context_fc, - ft_dependents); - } - } - - if (!ctf_tc->is_translated) { - force_update_field_class_in_ir(ctf_tc->packet_header_fc, - false); - } - - g_hash_table_destroy(ft_dependents); - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-meanings.c b/plugins/ctf/common/metadata/ctf-meta-update-meanings.c deleted file mode 100644 index 1e78c3af..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-meanings.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-MEANINGS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static -int set_int_field_class_meaning_by_name(struct ctf_field_class *fc, - const char *field_name, const char *id_name, - enum ctf_field_class_meaning meaning) -{ - int ret = 0; - uint64_t i; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - { - struct ctf_field_class_int *int_fc = (void *) fc; - - if (field_name && strcmp(field_name, id_name) == 0) { - int_fc->meaning = meaning; - } - - break; - } - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - ret = set_int_field_class_meaning_by_name(named_fc->fc, - named_fc->name->str, id_name, meaning); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - ret = set_int_field_class_meaning_by_name(named_fc->fc, - NULL, id_name, meaning); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - ret = set_int_field_class_meaning_by_name(array_fc->elem_fc, - NULL, id_name, meaning); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -static -int update_stream_class_meanings(struct ctf_stream_class *sc) -{ - int ret = 0; - struct ctf_field_class_int *int_fc; - uint64_t i; - - if (!sc->is_translated) { - if (sc->packet_context_fc) { - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_begin"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_end"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME; - - /* - * Remove mapped clock class to avoid updating - * the clock immediately when decoding. - */ - int_fc->mapped_clock_class = NULL; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "events_discarded"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "packet_seq_num"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT; - - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "packet_size"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "content_size"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE; - } - } - - if (sc->event_header_fc) { - ret = set_int_field_class_meaning_by_name( - sc->event_header_fc, NULL, "id", - CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID); - if (ret) { - goto end; - } - } - } - - for (i = 0; i < sc->event_classes->len; i++) { - struct ctf_event_class *ec = sc->event_classes->pdata[i]; - - if (ec->is_translated) { - continue; - } - } - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc) -{ - int ret = 0; - struct ctf_field_class_int *int_fc; - struct ctf_named_field_class *named_fc; - uint64_t i; - - if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) { - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) ctf_tc->packet_header_fc, "magic"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) ctf_tc->packet_header_fc, "stream_id"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) ctf_tc->packet_header_fc, - "stream_instance_id"); - if (int_fc) { - int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID; - } - - named_fc = ctf_field_class_struct_borrow_member_by_name( - (void *) ctf_tc->packet_header_fc, "uuid"); - if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) { - struct ctf_field_class_array *array_fc = - (void *) named_fc->fc; - - array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID; - } - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - ret = update_stream_class_meanings( - ctf_tc->stream_classes->pdata[i]); - if (ret) { - goto end; - } - } - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c b/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c deleted file mode 100644 index 9bd0bc6c..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2019 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-SC-CONFIG" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -BT_HIDDEN -int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc) -{ - struct ctf_field_class_int *int_fc; - uint64_t i; - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = - ctf_tc->stream_classes->pdata[i]; - - if (sc->is_translated) { - continue; - } - - if (!sc->packet_context_fc) { - continue; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_begin"); - if (int_fc && int_fc->meaning == - CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) { - sc->packets_have_ts_begin = true; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_end"); - if (int_fc && int_fc->meaning == - CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) { - sc->packets_have_ts_end = true; - } - - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "events_discarded"); - if (int_fc && int_fc->meaning == - CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) { - sc->has_discarded_events = true; - } - - sc->discarded_events_have_default_cs = - sc->has_discarded_events && sc->packets_have_ts_begin && - sc->packets_have_ts_end; - int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( - (void *) sc->packet_context_fc, "packet_seq_num"); - if (int_fc && int_fc->meaning == - CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) { - sc->has_discarded_packets = true; - } - - sc->discarded_packets_have_default_cs = - sc->has_discarded_packets && - sc->packets_have_ts_begin && sc->packets_have_ts_end; - } - - return 0; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c b/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c deleted file mode 100644 index 29d231f0..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-TEXT-ARRAY-SEQ" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static inline -int set_text_array_sequence_field_class(struct ctf_field_class *fc) -{ - int ret = 0; - uint64_t i; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - ret = set_text_array_sequence_field_class(named_fc->fc); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - ret = set_text_array_sequence_field_class(named_fc->fc); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT || - array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) { - struct ctf_field_class_int *int_fc = - (void *) array_fc->elem_fc; - - if (int_fc->base.base.alignment == 8 && - int_fc->base.size == 8 && - int_fc->encoding == CTF_ENCODING_UTF8) { - array_fc->is_text = true; - - /* - * Force integer element to be unsigned; - * this makes the decoder enter a single - * path when reading a text - * array/sequence and we can safely - * decode bytes as characters anyway. - */ - int_fc->is_signed = false; - } - } - - ret = set_text_array_sequence_field_class(array_fc->elem_fc); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc) -{ - int ret = 0; - uint64_t i; - - if (!ctf_tc->is_translated) { - ret = set_text_array_sequence_field_class( - ctf_tc->packet_header_fc); - if (ret) { - goto end; - } - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; - uint64_t j; - - if (!sc->is_translated) { - ret = set_text_array_sequence_field_class( - sc->packet_context_fc); - if (ret) { - goto end; - } - - ret = set_text_array_sequence_field_class( - sc->event_header_fc); - if (ret) { - goto end; - } - - ret = set_text_array_sequence_field_class( - sc->event_common_context_fc); - if (ret) { - goto end; - } - } - - for (j = 0; j < sc->event_classes->len; j++) { - struct ctf_event_class *ec = - sc->event_classes->pdata[j]; - - if (ec->is_translated) { - continue; - } - - ret = set_text_array_sequence_field_class( - ec->spec_context_fc); - if (ret) { - goto end; - } - - ret = set_text_array_sequence_field_class( - ec->payload_fc); - if (ret) { - goto end; - } - } - } - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c b/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c deleted file mode 100644 index 05abb49c..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-VALUE-STORING-INDEXES" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static -int update_field_class_stored_value_index(struct ctf_field_class *fc, - struct ctf_trace_class *tc, - struct ctf_stream_class *sc, - struct ctf_event_class *ec) -{ - int ret = 0; - uint64_t i; - struct ctf_field_path *field_path = NULL; - struct ctf_field_class_int *tgt_fc = NULL; - uint64_t *stored_value_index = NULL; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - field_path = &var_fc->tag_path; - stored_value_index = &var_fc->stored_tag_index; - tgt_fc = (void *) var_fc->tag_fc; - break; - } - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_sequence *seq_fc = (void *) fc; - - field_path = &seq_fc->length_path; - stored_value_index = &seq_fc->stored_length_index; - tgt_fc = seq_fc->length_fc; - break; - } - default: - break; - } - - if (field_path) { - BT_ASSERT(tgt_fc); - BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT || - tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM); - if (tgt_fc->storing_index >= 0) { - /* Already storing its value */ - *stored_value_index = (uint64_t) tgt_fc->storing_index; - } else { - /* Not storing its value: allocate new index */ - tgt_fc->storing_index = tc->stored_value_count; - *stored_value_index = (uint64_t) tgt_fc->storing_index; - tc->stored_value_count++; - } - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - ret = update_field_class_stored_value_index(named_fc->fc, - tc, sc, ec); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - ret = update_field_class_stored_value_index(named_fc->fc, - tc, sc, ec); - if (ret) { - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - ret = update_field_class_stored_value_index(array_fc->elem_fc, - tc, sc, ec); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc) -{ - uint64_t i; - - if (!ctf_tc->is_translated) { - update_field_class_stored_value_index( - ctf_tc->packet_header_fc, ctf_tc, NULL, NULL); - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - uint64_t j; - struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; - - if (!sc->is_translated) { - update_field_class_stored_value_index(sc->packet_context_fc, - ctf_tc, sc, NULL); - update_field_class_stored_value_index(sc->event_header_fc, - ctf_tc, sc, NULL); - update_field_class_stored_value_index( - sc->event_common_context_fc, ctf_tc, sc, NULL); - } - - for (j = 0; j < sc->event_classes->len; j++) { - struct ctf_event_class *ec = - sc->event_classes->pdata[j]; - - if (!ec->is_translated) { - update_field_class_stored_value_index( - ec->spec_context_fc, ctf_tc, sc, ec); - update_field_class_stored_value_index( - ec->payload_fc, ctf_tc, sc, ec); - } - } - } - - return 0; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-validate.c b/plugins/ctf/common/metadata/ctf-meta-validate.c deleted file mode 100644 index 6070070c..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-validate.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-VALIDATE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static -int validate_stream_class(struct ctf_stream_class *sc) -{ - int ret = 0; - struct ctf_field_class_int *int_fc; - struct ctf_field_class *fc; - - if (sc->is_translated) { - goto end; - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_begin"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`timestamp_begin` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`timestamp_begin` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "timestamp_end"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`timestamp_end` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`timestamp_end` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "events_discarded"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`events_discarded` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`events_discarded` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "packet_seq_num"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`packet_seq_num` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`packet_seq_num` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "packet_size"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`packet_size` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`packet_size` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->packet_context_fc, "content_size"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet context field class: " - "`content_size` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet context field class: " - "`content_size` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) sc->event_header_fc, "id"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid event header field class: " - "`id` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid event header field class: " - "`id` member is signed."); - goto invalid; - } - } else { - if (sc->event_classes->len > 1) { - BT_LOGE_STR("Invalid event header field class: " - "missing `id` member as there's " - "more than one event class."); - goto invalid; - } - } - - goto end; - -invalid: - ret = -1; - -end: - return ret; -} - -BT_HIDDEN -int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc) -{ - int ret = 0; - struct ctf_field_class_int *int_fc; - uint64_t i; - - if (!ctf_tc->is_translated) { - struct ctf_field_class *fc; - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) ctf_tc->packet_header_fc, "magic"); - if (fc) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - (void *) ctf_tc->packet_header_fc, - 0); - - if (named_fc->fc != fc) { - BT_LOGE_STR("Invalid packet header field class: " - "`magic` member is not the first member."); - goto invalid; - } - - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet header field class: " - "`magic` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet header field class: " - "`magic` member is signed."); - goto invalid; - } - - if (int_fc->base.size != 32) { - BT_LOGE_STR("Invalid packet header field class: " - "`magic` member is not 32-bit."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) ctf_tc->packet_header_fc, "stream_id"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet header field class: " - "`stream_id` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet header field class: " - "`stream_id` member is signed."); - goto invalid; - } - } else { - if (ctf_tc->stream_classes->len > 1) { - BT_LOGE_STR("Invalid packet header field class: " - "missing `stream_id` member as there's " - "more than one stream class."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) ctf_tc->packet_header_fc, - "stream_instance_id"); - if (fc) { - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - BT_LOGE_STR("Invalid packet header field class: " - "`stream_instance_id` member is not an integer field class."); - goto invalid; - } - - int_fc = (void *) fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet header field class: " - "`stream_instance_id` member is signed."); - goto invalid; - } - } - - fc = ctf_field_class_struct_borrow_member_field_class_by_name( - (void *) ctf_tc->packet_header_fc, "uuid"); - if (fc) { - struct ctf_field_class_array *array_fc = (void *) fc; - - if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member is not an array field class."); - goto invalid; - } - - array_fc = (void *) fc; - - if (array_fc->length != 16) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member is not a 16-element array field class."); - goto invalid; - } - - if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member's element field class is not " - "an integer field class."); - goto invalid; - } - - int_fc = (void *) array_fc->base.elem_fc; - - if (int_fc->is_signed) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member's element field class " - "is a signed integer field class."); - goto invalid; - } - - if (int_fc->base.size != 8) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member's element field class " - "is not an 8-bit integer field class."); - goto invalid; - } - - if (int_fc->base.base.alignment != 8) { - BT_LOGE_STR("Invalid packet header field class: " - "`uuid` member's element field class's " - "alignment is not 8."); - goto invalid; - } - } - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = - ctf_tc->stream_classes->pdata[i]; - - ret = validate_stream_class(sc); - if (ret) { - BT_LOGE("Invalid stream class: sc-id=%" PRIu64, sc->id); - goto invalid; - } - } - - goto end; - -invalid: - ret = -1; - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/ctf-meta-visitors.h b/plugins/ctf/common/metadata/ctf-meta-visitors.h deleted file mode 100644 index 91ad9292..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-visitors.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _CTF_META_VISITORS_H -#define _CTF_META_VISITORS_H - -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include - -#include "ctf-meta.h" - -BT_HIDDEN -int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc); - -BT_HIDDEN -int ctf_trace_class_translate(bt_self_component_source *self_comp, - bt_trace_class *ir_tc, struct ctf_trace_class *tc); - -BT_HIDDEN -int ctf_trace_class_update_default_clock_classes( - struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc); - -BT_HIDDEN -void ctf_trace_class_warn_meaningless_header_fields( - struct ctf_trace_class *ctf_tc); - -#endif /* _CTF_META_VISITORS_H */ diff --git a/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c b/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c deleted file mode 100644 index 78bb4588..00000000 --- a/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-WARN-MEANINGLESS-HEADER-FIELDS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-meta-visitors.h" - -static inline -void warn_meaningless_field(const char *name, const char *scope_name) -{ - BT_LOGW("User field found in %s: ignoring: name=\"%s\"", - scope_name, name); -} - -static inline -void warn_meaningless_fields(struct ctf_field_class *fc, const char *name, - const char *scope_name) -{ - uint64_t i; - - if (!fc) { - goto end; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_FLOAT: - case CTF_FIELD_CLASS_TYPE_STRING: - warn_meaningless_field(name, scope_name); - break; - case CTF_FIELD_CLASS_TYPE_INT: - case CTF_FIELD_CLASS_TYPE_ENUM: - { - struct ctf_field_class_int *int_fc = (void *) fc; - - if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE && - !int_fc->mapped_clock_class) { - warn_meaningless_field(name, scope_name); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - warn_meaningless_fields(named_fc->fc, - named_fc->name->str, scope_name); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - warn_meaningless_fields(named_fc->fc, - named_fc->name->str, scope_name); - } - - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - { - struct ctf_field_class_array *array_fc = (void *) fc; - - if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) { - goto end; - } - - } - /* fall-through */ - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - warn_meaningless_fields(array_fc->elem_fc, name, scope_name); - break; - } - default: - abort(); - } - -end: - return; -} - -BT_HIDDEN -void ctf_trace_class_warn_meaningless_header_fields( - struct ctf_trace_class *ctf_tc) -{ - uint64_t i; - - if (!ctf_tc->is_translated) { - warn_meaningless_fields( - ctf_tc->packet_header_fc, NULL, "packet header"); - } - - for (i = 0; i < ctf_tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; - - if (!sc->is_translated) { - warn_meaningless_fields(sc->event_header_fc, NULL, - "event header"); - } - } -} diff --git a/plugins/ctf/common/metadata/ctf-meta.h b/plugins/ctf/common/metadata/ctf-meta.h deleted file mode 100644 index 3e789631..00000000 --- a/plugins/ctf/common/metadata/ctf-meta.h +++ /dev/null @@ -1,1680 +0,0 @@ -#ifndef _CTF_META_H -#define _CTF_META_H - -/* - * Copyright 2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include -#include -#include -#include -#include - -enum ctf_field_class_type { - CTF_FIELD_CLASS_TYPE_INT, - CTF_FIELD_CLASS_TYPE_ENUM, - CTF_FIELD_CLASS_TYPE_FLOAT, - CTF_FIELD_CLASS_TYPE_STRING, - CTF_FIELD_CLASS_TYPE_STRUCT, - CTF_FIELD_CLASS_TYPE_ARRAY, - CTF_FIELD_CLASS_TYPE_SEQUENCE, - CTF_FIELD_CLASS_TYPE_VARIANT, -}; - -enum ctf_field_class_meaning { - CTF_FIELD_CLASS_MEANING_NONE, - CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME, - CTF_FIELD_CLASS_MEANING_PACKET_END_TIME, - CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID, - CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID, - CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID, - CTF_FIELD_CLASS_MEANING_MAGIC, - CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT, - CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT, - CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE, - CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE, - CTF_FIELD_CLASS_MEANING_UUID, -}; - -enum ctf_byte_order { - CTF_BYTE_ORDER_DEFAULT, - CTF_BYTE_ORDER_LITTLE, - CTF_BYTE_ORDER_BIG, -}; - -enum ctf_encoding { - CTF_ENCODING_NONE, - CTF_ENCODING_UTF8, -}; - -enum ctf_scope { - CTF_SCOPE_PACKET_HEADER, - CTF_SCOPE_PACKET_CONTEXT, - CTF_SCOPE_EVENT_HEADER, - CTF_SCOPE_EVENT_COMMON_CONTEXT, - CTF_SCOPE_EVENT_SPECIFIC_CONTEXT, - CTF_SCOPE_EVENT_PAYLOAD, -}; - -struct ctf_clock_class { - GString *name; - GString *description; - uint64_t frequency; - uint64_t precision; - int64_t offset_seconds; - uint64_t offset_cycles; - uint8_t uuid[16]; - bool has_uuid; - bool is_absolute; - - /* Weak, set during translation */ - bt_clock_class *ir_cc; -}; - -struct ctf_field_class { - enum ctf_field_class_type type; - unsigned int alignment; - bool is_compound; - bool in_ir; - - /* Weak, set during translation. NULL if `in_ir` is false below. */ - bt_field_class *ir_fc; -}; - -struct ctf_field_class_bit_array { - struct ctf_field_class base; - enum ctf_byte_order byte_order; - unsigned int size; -}; - -struct ctf_field_class_int { - struct ctf_field_class_bit_array base; - enum ctf_field_class_meaning meaning; - bool is_signed; - bt_field_class_integer_preferred_display_base disp_base; - enum ctf_encoding encoding; - int64_t storing_index; - - /* Weak */ - struct ctf_clock_class *mapped_clock_class; -}; - -struct ctf_range { - union { - uint64_t u; - int64_t i; - } lower; - - union { - uint64_t u; - int64_t i; - } upper; -}; - -struct ctf_field_class_enum_mapping { - GString *label; - struct ctf_range range; -}; - -struct ctf_field_class_enum { - struct ctf_field_class_int base; - - /* Array of `struct ctf_field_class_enum_mapping` */ - GArray *mappings; -}; - -struct ctf_field_class_float { - struct ctf_field_class_bit_array base; -}; - -struct ctf_field_class_string { - struct ctf_field_class base; - enum ctf_encoding encoding; -}; - -struct ctf_named_field_class { - GString *name; - - /* Owned by this */ - struct ctf_field_class *fc; -}; - -struct ctf_field_class_struct { - struct ctf_field_class base; - - /* Array of `struct ctf_named_field_class` */ - GArray *members; -}; - -struct ctf_field_path { - enum ctf_scope root; - - /* Array of `int64_t` */ - GArray *path; -}; - -struct ctf_field_class_variant_range { - struct ctf_range range; - uint64_t option_index; -}; - -struct ctf_field_class_variant { - struct ctf_field_class base; - GString *tag_ref; - struct ctf_field_path tag_path; - uint64_t stored_tag_index; - - /* Array of `struct ctf_named_field_class` */ - GArray *options; - - /* Array of `struct ctf_field_class_variant_range` */ - GArray *ranges; - - /* Weak */ - struct ctf_field_class_enum *tag_fc; -}; - -struct ctf_field_class_array_base { - struct ctf_field_class base; - struct ctf_field_class *elem_fc; - bool is_text; -}; - -struct ctf_field_class_array { - struct ctf_field_class_array_base base; - enum ctf_field_class_meaning meaning; - uint64_t length; -}; - -struct ctf_field_class_sequence { - struct ctf_field_class_array_base base; - GString *length_ref; - struct ctf_field_path length_path; - uint64_t stored_length_index; - - /* Weak */ - struct ctf_field_class_int *length_fc; -}; - -struct ctf_event_class { - GString *name; - uint64_t id; - GString *emf_uri; - bt_event_class_log_level log_level; - bool is_translated; - - /* Owned by this */ - struct ctf_field_class *spec_context_fc; - - /* Owned by this */ - struct ctf_field_class *payload_fc; - - /* Weak, set during translation */ - bt_event_class *ir_ec; -}; - -struct ctf_stream_class { - uint64_t id; - bool is_translated; - bool packets_have_ts_begin; - bool packets_have_ts_end; - bool has_discarded_events; - bool has_discarded_packets; - bool discarded_events_have_default_cs; - bool discarded_packets_have_default_cs; - - /* Owned by this */ - struct ctf_field_class *packet_context_fc; - - /* Owned by this */ - struct ctf_field_class *event_header_fc; - - /* Owned by this */ - struct ctf_field_class *event_common_context_fc; - - /* Array of `struct ctf_event_class *`, owned by this */ - GPtrArray *event_classes; - - /* - * Hash table mapping event class IDs to `struct ctf_event_class *`, - * weak. - */ - GHashTable *event_classes_by_id; - - /* Weak */ - struct ctf_clock_class *default_clock_class; - - /* Weak, set during translation */ - bt_stream_class *ir_sc; -}; - -enum ctf_trace_class_env_entry_type { - CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT, - CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR, -}; - -struct ctf_trace_class_env_entry { - enum ctf_trace_class_env_entry_type type; - GString *name; - - struct { - int64_t i; - GString *str; - } value; -}; - -struct ctf_trace_class { - unsigned int major; - unsigned int minor; - uint8_t uuid[16]; - bool is_uuid_set; - enum ctf_byte_order default_byte_order; - - /* Owned by this */ - struct ctf_field_class *packet_header_fc; - - uint64_t stored_value_count; - - /* Array of `struct ctf_clock_class *` (owned by this) */ - GPtrArray *clock_classes; - - /* Array of `struct ctf_stream_class *` */ - GPtrArray *stream_classes; - - /* Array of `struct ctf_trace_class_env_entry` */ - GArray *env_entries; - - bool is_translated; - - /* Weak, set during translation */ - bt_trace_class *ir_tc; -}; - -static inline -void ctf_field_class_destroy(struct ctf_field_class *fc); - -static inline -void _ctf_field_class_init(struct ctf_field_class *fc, - enum ctf_field_class_type type, unsigned int alignment) -{ - BT_ASSERT(fc); - fc->type = type; - fc->alignment = alignment; - fc->in_ir = false; -} - -static inline -void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc, - enum ctf_field_class_type type) -{ - _ctf_field_class_init((void *) fc, type, 1); -} - -static inline -void _ctf_field_class_int_init(struct ctf_field_class_int *fc, - enum ctf_field_class_type type) -{ - _ctf_field_class_bit_array_init((void *) fc, type); - fc->meaning = CTF_FIELD_CLASS_MEANING_NONE; - fc->storing_index = -1; -} - -static inline -void ctf_field_path_init(struct ctf_field_path *field_path) -{ - BT_ASSERT(field_path); - field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t)); - BT_ASSERT(field_path->path); -} - -static inline -void ctf_field_path_fini(struct ctf_field_path *field_path) -{ - BT_ASSERT(field_path); - - if (field_path->path) { - g_array_free(field_path->path, TRUE); - } -} - -static inline -void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - named_fc->name = g_string_new(NULL); - BT_ASSERT(named_fc->name); -} - -static inline -void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - - if (named_fc->name) { - g_string_free(named_fc->name, TRUE); - } - - ctf_field_class_destroy(named_fc->fc); -} - -static inline -void _ctf_field_class_enum_mapping_init( - struct ctf_field_class_enum_mapping *mapping) -{ - BT_ASSERT(mapping); - mapping->label = g_string_new(NULL); - BT_ASSERT(mapping->label); -} - -static inline -void _ctf_field_class_enum_mapping_fini( - struct ctf_field_class_enum_mapping *mapping) -{ - BT_ASSERT(mapping); - - if (mapping->label) { - g_string_free(mapping->label, TRUE); - } -} - -static inline -struct ctf_field_class_int *ctf_field_class_int_create(void) -{ - struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1); - - BT_ASSERT(fc); - _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT); - return fc; -} - -static inline -struct ctf_field_class_float *ctf_field_class_float_create(void) -{ - struct ctf_field_class_float *fc = - g_new0(struct ctf_field_class_float, 1); - - BT_ASSERT(fc); - _ctf_field_class_bit_array_init((void *) fc, CTF_FIELD_CLASS_TYPE_FLOAT); - return fc; -} - -static inline -struct ctf_field_class_string *ctf_field_class_string_create(void) -{ - struct ctf_field_class_string *fc = - g_new0(struct ctf_field_class_string, 1); - - BT_ASSERT(fc); - _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRING, 8); - return fc; -} - -static inline -struct ctf_field_class_enum *ctf_field_class_enum_create(void) -{ - struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1); - - BT_ASSERT(fc); - _ctf_field_class_int_init((void *) fc, CTF_FIELD_CLASS_TYPE_ENUM); - fc->mappings = g_array_new(FALSE, TRUE, - sizeof(struct ctf_field_class_enum_mapping)); - BT_ASSERT(fc->mappings); - return fc; -} - -static inline -struct ctf_field_class_struct *ctf_field_class_struct_create(void) -{ - struct ctf_field_class_struct *fc = - g_new0(struct ctf_field_class_struct, 1); - - BT_ASSERT(fc); - _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRUCT, 1); - fc->members = g_array_new(FALSE, TRUE, - sizeof(struct ctf_named_field_class)); - BT_ASSERT(fc->members); - fc->base.is_compound = true; - return fc; -} - -static inline -struct ctf_field_class_variant *ctf_field_class_variant_create(void) -{ - struct ctf_field_class_variant *fc = - g_new0(struct ctf_field_class_variant, 1); - - BT_ASSERT(fc); - _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_VARIANT, 1); - fc->options = g_array_new(FALSE, TRUE, - sizeof(struct ctf_named_field_class)); - BT_ASSERT(fc->options); - fc->ranges = g_array_new(FALSE, TRUE, - sizeof(struct ctf_field_class_variant_range)); - BT_ASSERT(fc->ranges); - fc->tag_ref = g_string_new(NULL); - BT_ASSERT(fc->tag_ref); - ctf_field_path_init(&fc->tag_path); - fc->base.is_compound = true; - return fc; -} - -static inline -struct ctf_field_class_array *ctf_field_class_array_create(void) -{ - struct ctf_field_class_array *fc = - g_new0(struct ctf_field_class_array, 1); - - BT_ASSERT(fc); - _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_ARRAY, 1); - fc->base.base.is_compound = true; - return fc; -} - -static inline -struct ctf_field_class_sequence *ctf_field_class_sequence_create(void) -{ - struct ctf_field_class_sequence *fc = - g_new0(struct ctf_field_class_sequence, 1); - - BT_ASSERT(fc); - _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1); - fc->length_ref = g_string_new(NULL); - BT_ASSERT(fc->length_ref); - ctf_field_path_init(&fc->length_path); - fc->base.base.is_compound = true; - return fc; -} - -static inline -void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc) -{ - BT_ASSERT(fc); - g_free(fc); -} - -static inline -void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc) -{ - BT_ASSERT(fc); - - if (fc->mappings) { - uint64_t i; - - for (i = 0; i < fc->mappings->len; i++) { - struct ctf_field_class_enum_mapping *mapping = - &g_array_index(fc->mappings, - struct ctf_field_class_enum_mapping, i); - - _ctf_field_class_enum_mapping_fini(mapping); - } - - g_array_free(fc->mappings, TRUE); - } - - g_free(fc); -} - -static inline -void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc) -{ - BT_ASSERT(fc); - g_free(fc); -} - -static inline -void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc) -{ - BT_ASSERT(fc); - g_free(fc); -} - -static inline -void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc) -{ - BT_ASSERT(fc); - - if (fc->members) { - uint64_t i; - - for (i = 0; i < fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - &g_array_index(fc->members, - struct ctf_named_field_class, i); - - _ctf_named_field_class_fini(named_fc); - } - - g_array_free(fc->members, TRUE); - } - - g_free(fc); -} - -static inline -void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc) -{ - BT_ASSERT(fc); - ctf_field_class_destroy(fc->elem_fc); -} - -static inline -void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc) -{ - BT_ASSERT(fc); - _ctf_field_class_array_base_fini((void *) fc); - g_free(fc); -} - -static inline -void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc) -{ - BT_ASSERT(fc); - _ctf_field_class_array_base_fini((void *) fc); - - if (fc->length_ref) { - g_string_free(fc->length_ref, TRUE); - } - - ctf_field_path_fini(&fc->length_path); - g_free(fc); -} - -static inline -void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc) -{ - BT_ASSERT(fc); - - if (fc->options) { - uint64_t i; - - for (i = 0; i < fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - &g_array_index(fc->options, - struct ctf_named_field_class, i); - - _ctf_named_field_class_fini(named_fc); - } - - g_array_free(fc->options, TRUE); - } - - if (fc->ranges) { - g_array_free(fc->ranges, TRUE); - } - - if (fc->tag_ref) { - g_string_free(fc->tag_ref, TRUE); - } - - ctf_field_path_fini(&fc->tag_path); - g_free(fc); -} - -static inline -void ctf_field_class_destroy(struct ctf_field_class *fc) -{ - if (!fc) { - return; - } - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - _ctf_field_class_int_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ENUM: - _ctf_field_class_enum_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_FLOAT: - _ctf_field_class_float_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRING: - _ctf_field_class_string_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRUCT: - _ctf_field_class_struct_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ARRAY: - _ctf_field_class_array_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - _ctf_field_class_sequence_destroy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_VARIANT: - _ctf_field_class_variant_destroy((void *) fc); - break; - default: - abort(); - } -} - -static inline -void ctf_field_class_enum_append_mapping(struct ctf_field_class_enum *fc, - const char *label, uint64_t u_lower, uint64_t u_upper) -{ - struct ctf_field_class_enum_mapping *mapping; - - BT_ASSERT(fc); - BT_ASSERT(label); - g_array_set_size(fc->mappings, fc->mappings->len + 1); - - mapping = &g_array_index(fc->mappings, - struct ctf_field_class_enum_mapping, fc->mappings->len - 1); - _ctf_field_class_enum_mapping_init(mapping); - g_string_assign(mapping->label, label); - mapping->range.lower.u = u_lower; - mapping->range.upper.u = u_upper; -} - -static inline -struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_index( - struct ctf_field_class_enum *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->mappings->len); - return &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, - index); -} - -static inline -struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_index( - struct ctf_field_class_struct *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->members->len); - return &g_array_index(fc->members, struct ctf_named_field_class, - index); -} - -static inline -struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_name( - struct ctf_field_class_struct *fc, const char *name) -{ - uint64_t i; - struct ctf_named_field_class *ret_named_fc = NULL; - - BT_ASSERT(fc); - BT_ASSERT(name); - - for (i = 0; i < fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index(fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_named_fc = named_fc; - goto end; - } - } - -end: - return ret_named_fc; -} - -static inline -struct ctf_field_class *ctf_field_class_struct_borrow_member_field_class_by_name( - struct ctf_field_class_struct *struct_fc, const char *name) -{ - struct ctf_named_field_class *named_fc = NULL; - struct ctf_field_class *fc = NULL; - - if (!struct_fc) { - goto end; - } - - named_fc = ctf_field_class_struct_borrow_member_by_name(struct_fc, name); - if (!named_fc) { - goto end; - } - - fc = named_fc->fc; - -end: - return fc; -} - -static inline -struct ctf_field_class_int * -ctf_field_class_struct_borrow_member_int_field_class_by_name( - struct ctf_field_class_struct *struct_fc, const char *name) -{ - struct ctf_field_class_int *int_fc = NULL; - - int_fc = (void *) - ctf_field_class_struct_borrow_member_field_class_by_name( - struct_fc, name); - if (!int_fc) { - goto end; - } - - if (int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_INT && - int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_ENUM) { - int_fc = NULL; - goto end; - } - -end: - return int_fc; -} - - -static inline -void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc, - const char *name, struct ctf_field_class *member_fc) -{ - struct ctf_named_field_class *named_fc; - - BT_ASSERT(fc); - BT_ASSERT(name); - g_array_set_size(fc->members, fc->members->len + 1); - - named_fc = &g_array_index(fc->members, struct ctf_named_field_class, - fc->members->len - 1); - _ctf_named_field_class_init(named_fc); - g_string_assign(named_fc->name, name); - named_fc->fc = member_fc; - - if (member_fc->alignment > fc->base.alignment) { - fc->base.alignment = member_fc->alignment; - } -} - -static inline -struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_index( - struct ctf_field_class_variant *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->options->len); - return &g_array_index(fc->options, struct ctf_named_field_class, - index); -} - -static inline -struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_name( - struct ctf_field_class_variant *fc, const char *name) -{ - uint64_t i; - struct ctf_named_field_class *ret_named_fc = NULL; - - BT_ASSERT(fc); - BT_ASSERT(name); - - for (i = 0; i < fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index(fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_named_fc = named_fc; - goto end; - } - } - -end: - return ret_named_fc; -} - -static inline -struct ctf_field_class_variant_range * -ctf_field_class_variant_borrow_range_by_index( - struct ctf_field_class_variant *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->ranges->len); - return &g_array_index(fc->ranges, struct ctf_field_class_variant_range, - index); -} - -static inline -void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc, - const char *name, struct ctf_field_class *option_fc) -{ - struct ctf_named_field_class *named_fc; - - BT_ASSERT(fc); - BT_ASSERT(name); - g_array_set_size(fc->options, fc->options->len + 1); - - named_fc = &g_array_index(fc->options, struct ctf_named_field_class, - fc->options->len - 1); - _ctf_named_field_class_init(named_fc); - g_string_assign(named_fc->name, name); - named_fc->fc = option_fc; -} - -static inline -void ctf_field_class_variant_set_tag_field_class( - struct ctf_field_class_variant *fc, - struct ctf_field_class_enum *tag_fc) -{ - uint64_t option_i; - - BT_ASSERT(fc); - BT_ASSERT(tag_fc); - fc->tag_fc = tag_fc; - - for (option_i = 0; option_i < fc->options->len; option_i++) { - uint64_t mapping_i; - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - fc, option_i); - - for (mapping_i = 0; mapping_i < tag_fc->mappings->len; - mapping_i++) { - struct ctf_field_class_enum_mapping *mapping = - ctf_field_class_enum_borrow_mapping_by_index( - tag_fc, mapping_i); - - if (strcmp(named_fc->name->str, - mapping->label->str) == 0) { - struct ctf_field_class_variant_range range; - - range.range = mapping->range; - range.option_index = option_i; - g_array_append_val(fc->ranges, range); - } - } - } -} - -static inline -struct ctf_field_class *ctf_field_class_compound_borrow_field_class_by_index( - struct ctf_field_class *comp_fc, uint64_t index) -{ - struct ctf_field_class *fc = NULL; - - switch (comp_fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - (void *) comp_fc, index); - - BT_ASSERT(named_fc); - fc = named_fc->fc; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - (void *) comp_fc, index); - - BT_ASSERT(named_fc); - fc = named_fc->fc; - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct ctf_field_class_array_base *array_fc = (void *) comp_fc; - - fc = array_fc->elem_fc; - break; - } - default: - break; - } - - return fc; -} - -static inline -uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc) -{ - uint64_t field_count; - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - field_count = struct_fc->members->len; - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - field_count = var_fc->options->len; - break; - } - case CTF_FIELD_CLASS_TYPE_ARRAY: - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - /* - * Array and sequence types always contain a single - * member (the element type). - */ - field_count = 1; - break; - default: - abort(); - } - - return field_count; -} - -static inline -int64_t ctf_field_class_compound_get_field_class_index_from_name( - struct ctf_field_class *fc, const char *name) -{ - int64_t ret_index = -1; - uint64_t i; - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_STRUCT: - { - struct ctf_field_class_struct *struct_fc = (void *) fc; - - for (i = 0; i < struct_fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_index = (int64_t) i; - goto end; - } - } - - break; - } - case CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct ctf_field_class_variant *var_fc = (void *) fc; - - for (i = 0; i < var_fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_index = (int64_t) i; - goto end; - } - } - - break; - } - default: - break; - } - -end: - return ret_index; -} - -static inline -void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index) -{ - BT_ASSERT(fp); - g_array_append_val(fp->path, index); -} - -static inline -int64_t ctf_field_path_borrow_index_by_index(struct ctf_field_path *fp, - uint64_t index) -{ - BT_ASSERT(fp); - BT_ASSERT(index < fp->path->len); - return g_array_index(fp->path, int64_t, index); -} - -static inline -void ctf_field_path_clear(struct ctf_field_path *fp) -{ - BT_ASSERT(fp); - g_array_set_size(fp->path, 0); -} - -static inline -const char *ctf_scope_string(enum ctf_scope scope) -{ - switch (scope) { - case CTF_SCOPE_PACKET_HEADER: - return "CTF_SCOPE_PACKET_HEADER"; - case CTF_SCOPE_PACKET_CONTEXT: - return "CTF_SCOPE_PACKET_CONTEXT"; - case CTF_SCOPE_EVENT_HEADER: - return "CTF_SCOPE_EVENT_HEADER"; - case CTF_SCOPE_EVENT_COMMON_CONTEXT: - return "CTF_SCOPE_EVENT_COMMON_CONTEXT"; - case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: - return "CTF_SCOPE_EVENT_SPECIFIC_CONTEXT"; - case CTF_SCOPE_EVENT_PAYLOAD: - return "CTF_SCOPE_EVENT_PAYLOAD"; - default: - abort(); - } -} - -static inline -GString *ctf_field_path_string(struct ctf_field_path *path) -{ - GString *str = g_string_new(NULL); - uint64_t i; - - BT_ASSERT(path); - - if (!str) { - goto end; - } - - g_string_append_printf(str, "[%s", ctf_scope_string(path->root)); - - for (i = 0; i < path->path->len; i++) { - g_string_append_printf(str, ", %" PRId64, - ctf_field_path_borrow_index_by_index(path, i)); - } - - g_string_append(str, "]"); - -end: - return str; -} - -static inline -struct ctf_field_class *ctf_field_path_borrow_field_class( - struct ctf_field_path *field_path, - struct ctf_trace_class *tc, - struct ctf_stream_class *sc, - struct ctf_event_class *ec) -{ - uint64_t i; - struct ctf_field_class *fc; - - switch (field_path->root) { - case CTF_SCOPE_PACKET_HEADER: - fc = tc->packet_header_fc; - break; - case CTF_SCOPE_PACKET_CONTEXT: - fc = sc->packet_context_fc; - break; - case CTF_SCOPE_EVENT_HEADER: - fc = sc->event_header_fc; - break; - case CTF_SCOPE_EVENT_COMMON_CONTEXT: - fc = sc->event_common_context_fc; - break; - case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: - fc = ec->spec_context_fc; - break; - case CTF_SCOPE_EVENT_PAYLOAD: - fc = ec->payload_fc; - break; - default: - abort(); - } - - BT_ASSERT(fc); - - for (i = 0; i < field_path->path->len; i++) { - int64_t child_index = - ctf_field_path_borrow_index_by_index(field_path, i); - struct ctf_field_class *child_fc = - ctf_field_class_compound_borrow_field_class_by_index( - fc, child_index); - BT_ASSERT(child_fc); - fc = child_fc; - } - - BT_ASSERT(fc); - return fc; -} - -static inline -struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc); - -static inline -void ctf_field_class_bit_array_copy_content( - struct ctf_field_class_bit_array *dst_fc, - struct ctf_field_class_bit_array *src_fc) -{ - BT_ASSERT(dst_fc); - BT_ASSERT(src_fc); - dst_fc->byte_order = src_fc->byte_order; - dst_fc->size = src_fc->size; -} - -static inline -void ctf_field_class_int_copy_content( - struct ctf_field_class_int *dst_fc, - struct ctf_field_class_int *src_fc) -{ - ctf_field_class_bit_array_copy_content((void *) dst_fc, (void *) src_fc); - dst_fc->meaning = src_fc->meaning; - dst_fc->is_signed = src_fc->is_signed; - dst_fc->disp_base = src_fc->disp_base; - dst_fc->encoding = src_fc->encoding; - dst_fc->mapped_clock_class = src_fc->mapped_clock_class; - dst_fc->storing_index = src_fc->storing_index; -} - -static inline -struct ctf_field_class_int *_ctf_field_class_int_copy( - struct ctf_field_class_int *fc) -{ - struct ctf_field_class_int *copy_fc = ctf_field_class_int_create(); - - BT_ASSERT(copy_fc); - ctf_field_class_int_copy_content(copy_fc, fc); - return copy_fc; -} - -static inline -struct ctf_field_class_enum *_ctf_field_class_enum_copy( - struct ctf_field_class_enum *fc) -{ - struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create(); - uint64_t i; - - BT_ASSERT(copy_fc); - ctf_field_class_int_copy_content((void *) copy_fc, (void *) fc); - - for (i = 0; i < fc->mappings->len; i++) { - struct ctf_field_class_enum_mapping *mapping = - &g_array_index(fc->mappings, - struct ctf_field_class_enum_mapping, i); - - ctf_field_class_enum_append_mapping(copy_fc, mapping->label->str, - mapping->range.lower.u, mapping->range.upper.u); - } - - return copy_fc; -} - -static inline -struct ctf_field_class_float *_ctf_field_class_float_copy( - struct ctf_field_class_float *fc) -{ - struct ctf_field_class_float *copy_fc = ctf_field_class_float_create(); - - BT_ASSERT(copy_fc); - ctf_field_class_bit_array_copy_content((void *) copy_fc, (void *) fc); - return copy_fc; -} - -static inline -struct ctf_field_class_string *_ctf_field_class_string_copy( - struct ctf_field_class_string *fc) -{ - struct ctf_field_class_string *copy_fc = ctf_field_class_string_create(); - - BT_ASSERT(copy_fc); - return copy_fc; -} - -static inline -struct ctf_field_class_struct *_ctf_field_class_struct_copy( - struct ctf_field_class_struct *fc) -{ - struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create(); - uint64_t i; - - BT_ASSERT(copy_fc); - - for (i = 0; i < fc->members->len; i++) { - struct ctf_named_field_class *named_fc = - &g_array_index(fc->members, - struct ctf_named_field_class, i); - - ctf_field_class_struct_append_member(copy_fc, - named_fc->name->str, - ctf_field_class_copy(named_fc->fc)); - } - - return copy_fc; -} - -static inline -void ctf_field_path_copy_content(struct ctf_field_path *dst_fp, - struct ctf_field_path *src_fp) -{ - uint64_t i; - - BT_ASSERT(dst_fp); - BT_ASSERT(src_fp); - dst_fp->root = src_fp->root; - ctf_field_path_clear(dst_fp); - - for (i = 0; i < src_fp->path->len; i++) { - int64_t index = ctf_field_path_borrow_index_by_index( - src_fp, i); - - ctf_field_path_append_index(dst_fp, index); - } -} - -static inline -struct ctf_field_class_variant *_ctf_field_class_variant_copy( - struct ctf_field_class_variant *fc) -{ - struct ctf_field_class_variant *copy_fc = - ctf_field_class_variant_create(); - uint64_t i; - - BT_ASSERT(copy_fc); - - for (i = 0; i < fc->options->len; i++) { - struct ctf_named_field_class *named_fc = - &g_array_index(fc->options, - struct ctf_named_field_class, i); - - ctf_field_class_variant_append_option(copy_fc, - named_fc->name->str, - ctf_field_class_copy(named_fc->fc)); - } - - for (i = 0; i < fc->ranges->len; i++) { - struct ctf_field_class_variant_range *range = - &g_array_index(fc->ranges, - struct ctf_field_class_variant_range, i); - - g_array_append_val(copy_fc->ranges, *range); - } - - ctf_field_path_copy_content(©_fc->tag_path, &fc->tag_path); - g_string_assign(copy_fc->tag_ref, fc->tag_ref->str); - copy_fc->stored_tag_index = fc->stored_tag_index; - return copy_fc; -} - -static inline -void ctf_field_class_array_base_copy_content( - struct ctf_field_class_array_base *dst_fc, - struct ctf_field_class_array_base *src_fc) -{ - BT_ASSERT(dst_fc); - BT_ASSERT(src_fc); - dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc); - dst_fc->is_text = src_fc->is_text; -} - -static inline -struct ctf_field_class_array *_ctf_field_class_array_copy( - struct ctf_field_class_array *fc) -{ - struct ctf_field_class_array *copy_fc = ctf_field_class_array_create(); - - BT_ASSERT(copy_fc); - ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc); - copy_fc->length = fc->length; - return copy_fc; -} - -static inline -struct ctf_field_class_sequence *_ctf_field_class_sequence_copy( - struct ctf_field_class_sequence *fc) -{ - struct ctf_field_class_sequence *copy_fc = - ctf_field_class_sequence_create(); - - BT_ASSERT(copy_fc); - ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc); - ctf_field_path_copy_content(©_fc->length_path, &fc->length_path); - g_string_assign(copy_fc->length_ref, fc->length_ref->str); - copy_fc->stored_length_index = fc->stored_length_index; - return copy_fc; -} - -static inline -struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc) -{ - struct ctf_field_class *copy_fc = NULL; - - if (!fc) { - goto end; - } - - /* - * Translation should not have happened yet. - */ - BT_ASSERT(!fc->ir_fc); - - switch (fc->type) { - case CTF_FIELD_CLASS_TYPE_INT: - copy_fc = (void *) _ctf_field_class_int_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ENUM: - copy_fc = (void *) _ctf_field_class_enum_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_FLOAT: - copy_fc = (void *) _ctf_field_class_float_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRING: - copy_fc = (void *) _ctf_field_class_string_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_STRUCT: - copy_fc = (void *) _ctf_field_class_struct_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_ARRAY: - copy_fc = (void *) _ctf_field_class_array_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_SEQUENCE: - copy_fc = (void *) _ctf_field_class_sequence_copy((void *) fc); - break; - case CTF_FIELD_CLASS_TYPE_VARIANT: - copy_fc = (void *) _ctf_field_class_variant_copy((void *) fc); - break; - default: - abort(); - } - - copy_fc->type = fc->type; - copy_fc->alignment = fc->alignment; - copy_fc->in_ir = fc->in_ir; - -end: - return copy_fc; -} - -static inline -struct ctf_event_class *ctf_event_class_create(void) -{ - struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1); - - BT_ASSERT(ec); - ec->name = g_string_new(NULL); - BT_ASSERT(ec->name); - ec->emf_uri = g_string_new(NULL); - BT_ASSERT(ec->emf_uri); - ec->log_level = -1; - return ec; -} - -static inline -void ctf_event_class_destroy(struct ctf_event_class *ec) -{ - if (!ec) { - return; - } - - if (ec->name) { - g_string_free(ec->name, TRUE); - } - - if (ec->emf_uri) { - g_string_free(ec->emf_uri, TRUE); - } - - ctf_field_class_destroy(ec->spec_context_fc); - ctf_field_class_destroy(ec->payload_fc); - g_free(ec); -} - -static inline -struct ctf_stream_class *ctf_stream_class_create(void) -{ - struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1); - - BT_ASSERT(sc); - sc->event_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) ctf_event_class_destroy); - BT_ASSERT(sc->event_classes); - sc->event_classes_by_id = g_hash_table_new(g_direct_hash, - g_direct_equal); - BT_ASSERT(sc->event_classes_by_id); - return sc; -} - -static inline -void ctf_stream_class_destroy(struct ctf_stream_class *sc) -{ - if (!sc) { - return; - } - - if (sc->event_classes) { - g_ptr_array_free(sc->event_classes, TRUE); - } - - if (sc->event_classes_by_id) { - g_hash_table_destroy(sc->event_classes_by_id); - } - - ctf_field_class_destroy(sc->packet_context_fc); - ctf_field_class_destroy(sc->event_header_fc); - ctf_field_class_destroy(sc->event_common_context_fc); - g_free(sc); -} - -static inline -void ctf_stream_class_append_event_class(struct ctf_stream_class *sc, - struct ctf_event_class *ec) -{ - g_ptr_array_add(sc->event_classes, ec); - g_hash_table_insert(sc->event_classes_by_id, - GUINT_TO_POINTER((guint) ec->id), ec); -} - -static inline -struct ctf_event_class *ctf_stream_class_borrow_event_class_by_id( - struct ctf_stream_class *sc, uint64_t type) -{ - BT_ASSERT(sc); - return g_hash_table_lookup(sc->event_classes_by_id, - GUINT_TO_POINTER((guint) type)); -} - -static inline -void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry) -{ - BT_ASSERT(entry); - entry->name = g_string_new(NULL); - BT_ASSERT(entry->name); - entry->value.str = g_string_new(NULL); - BT_ASSERT(entry->value.str); -} - -static inline -void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry) -{ - BT_ASSERT(entry); - - if (entry->name) { - g_string_free(entry->name, TRUE); - } - - if (entry->value.str) { - g_string_free(entry->value.str, TRUE); - } -} - -static inline -struct ctf_clock_class *ctf_clock_class_create(void) -{ - struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1); - - BT_ASSERT(cc); - cc->name = g_string_new(NULL); - BT_ASSERT(cc->name); - cc->description = g_string_new(NULL); - BT_ASSERT(cc->description); - return cc; -} - -static inline -void ctf_clock_class_destroy(struct ctf_clock_class *cc) -{ - if (!cc) { - return; - } - - if (cc->name) { - g_string_free(cc->name, TRUE); - } - - if (cc->description) { - g_string_free(cc->description, TRUE); - } - - bt_clock_class_put_ref(cc->ir_cc); - g_free(cc); -} - -static inline -struct ctf_trace_class *ctf_trace_class_create(void) -{ - struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1); - - BT_ASSERT(tc); - tc->default_byte_order = -1; - tc->clock_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) ctf_clock_class_destroy); - BT_ASSERT(tc->clock_classes); - tc->stream_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) ctf_stream_class_destroy); - BT_ASSERT(tc->stream_classes); - tc->env_entries = g_array_new(FALSE, TRUE, - sizeof(struct ctf_trace_class_env_entry)); - return tc; -} - -static inline -void ctf_trace_class_destroy(struct ctf_trace_class *tc) -{ - if (!tc) { - return; - } - - ctf_field_class_destroy(tc->packet_header_fc); - - if (tc->clock_classes) { - g_ptr_array_free(tc->clock_classes, TRUE); - } - - if (tc->stream_classes) { - g_ptr_array_free(tc->stream_classes, TRUE); - } - - if (tc->env_entries) { - uint64_t i; - - for (i = 0; i < tc->env_entries->len; i++) { - struct ctf_trace_class_env_entry *entry = - &g_array_index(tc->env_entries, - struct ctf_trace_class_env_entry, i); - - _ctf_trace_class_env_entry_fini(entry); - } - - g_array_free(tc->env_entries, TRUE); - } - - g_free(tc); -} - -static inline -void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc, - const char *name, enum ctf_trace_class_env_entry_type type, - const char *str_value, int64_t i_value) -{ - struct ctf_trace_class_env_entry *entry; - - BT_ASSERT(tc); - BT_ASSERT(name); - g_array_set_size(tc->env_entries, tc->env_entries->len + 1); - - entry = &g_array_index(tc->env_entries, - struct ctf_trace_class_env_entry, tc->env_entries->len - 1); - entry->type = type; - _ctf_trace_class_env_entry_init(entry); - g_string_assign(entry->name, name); - - if (str_value) { - g_string_assign(entry->value.str, str_value); - } - - entry->value.i = i_value; -} - -static inline -struct ctf_stream_class *ctf_trace_class_borrow_stream_class_by_id( - struct ctf_trace_class *tc, uint64_t id) -{ - uint64_t i; - struct ctf_stream_class *ret_sc = NULL; - - BT_ASSERT(tc); - - for (i = 0; i < tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = tc->stream_classes->pdata[i]; - - if (sc->id == id) { - ret_sc = sc; - goto end; - } - } - -end: - return ret_sc; -} - -static inline -struct ctf_clock_class *ctf_trace_class_borrow_clock_class_by_name( - struct ctf_trace_class *tc, const char *name) -{ - uint64_t i; - struct ctf_clock_class *ret_cc = NULL; - - BT_ASSERT(tc); - BT_ASSERT(name); - - for (i = 0; i < tc->clock_classes->len; i++) { - struct ctf_clock_class *cc = tc->clock_classes->pdata[i]; - - BT_ASSERT(cc->name); - if (strcmp(cc->name->str, name) == 0) { - ret_cc = cc; - goto end; - } - } - -end: - return ret_cc; -} - -static inline -struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_index( - struct ctf_trace_class *tc, uint64_t index) -{ - BT_ASSERT(tc); - BT_ASSERT(index < tc->env_entries->len); - return &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, - index); -} - -static inline -struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_name( - struct ctf_trace_class *tc, const char *name) -{ - struct ctf_trace_class_env_entry *ret_entry = NULL; - uint64_t i; - - BT_ASSERT(tc); - BT_ASSERT(name); - - for (i = 0; i < tc->env_entries->len; i++) { - struct ctf_trace_class_env_entry *env_entry = - ctf_trace_class_borrow_env_entry_by_index(tc, i); - - if (strcmp(env_entry->name->str, name) == 0) { - ret_entry = env_entry; - goto end; - } - } - -end: - return ret_entry; -} - -#endif /* _CTF_META_H */ diff --git a/plugins/ctf/common/metadata/decoder.c b/plugins/ctf/common/metadata/decoder.c deleted file mode 100644 index c39e79e3..00000000 --- a/plugins/ctf/common/metadata/decoder.c +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2016-2017 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-DECODER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ast.h" -#include "decoder.h" -#include "scanner.h" - -#define TSDL_MAGIC 0x75d11d57 - -extern -int yydebug; - -struct ctf_metadata_decoder { - struct ctf_visitor_generate_ir *visitor; - uint8_t uuid[16]; - bool is_uuid_set; - int bo; - struct ctf_metadata_decoder_config config; -}; - -struct packet_header { - uint32_t magic; - uint8_t uuid[16]; - uint32_t checksum; - uint32_t content_size; - uint32_t packet_size; - uint8_t compression_scheme; - uint8_t encryption_scheme; - uint8_t checksum_scheme; - uint8_t major; - uint8_t minor; -} __attribute__((__packed__)); - -BT_HIDDEN -bool ctf_metadata_decoder_is_packetized(FILE *fp, int *byte_order) -{ - uint32_t magic; - size_t len; - int ret = 0; - - len = fread(&magic, sizeof(magic), 1, fp); - if (len != 1) { - BT_LOGD_STR("Cannot reade first metadata packet header: assuming the stream is not packetized."); - goto end; - } - - if (byte_order) { - if (magic == TSDL_MAGIC) { - ret = 1; - *byte_order = BYTE_ORDER; - } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) { - ret = 1; - *byte_order = BYTE_ORDER == BIG_ENDIAN ? - LITTLE_ENDIAN : BIG_ENDIAN; - } - } - -end: - rewind(fp); - - return ret; -} - -static -bool is_version_valid(unsigned int major, unsigned int minor) -{ - return major == 1 && minor == 8; -} - -static -int decode_packet(struct ctf_metadata_decoder *mdec, FILE *in_fp, FILE *out_fp, - int byte_order) -{ - struct packet_header header; - size_t readlen, writelen, toread; - uint8_t buf[512 + 1]; /* + 1 for debug-mode \0 */ - int ret = 0; - const long offset = ftell(in_fp); - - if (offset < 0) { - BT_LOGE_ERRNO("Failed to get current metadata file position", - "."); - goto error; - } - BT_LOGV("Decoding metadata packet: mdec-addr=%p, offset=%ld", - mdec, offset); - readlen = fread(&header, sizeof(header), 1, in_fp); - if (feof(in_fp) != 0) { - BT_LOGV("Reached end of file: offset=%ld", ftell(in_fp)); - goto end; - } - if (readlen < 1) { - BT_LOGV("Cannot decode metadata packet: offset=%ld", offset); - goto error; - } - - if (byte_order != BYTE_ORDER) { - header.magic = GUINT32_SWAP_LE_BE(header.magic); - header.checksum = GUINT32_SWAP_LE_BE(header.checksum); - header.content_size = GUINT32_SWAP_LE_BE(header.content_size); - header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size); - } - - if (header.compression_scheme) { - BT_LOGE("Metadata packet compression is not supported as of this version: " - "compression-scheme=%u, offset=%ld", - (unsigned int) header.compression_scheme, offset); - goto error; - } - - if (header.encryption_scheme) { - BT_LOGE("Metadata packet encryption is not supported as of this version: " - "encryption-scheme=%u, offset=%ld", - (unsigned int) header.encryption_scheme, offset); - goto error; - } - - if (header.checksum || header.checksum_scheme) { - BT_LOGE("Metadata packet checksum verification is not supported as of this version: " - "checksum-scheme=%u, checksum=%x, offset=%ld", - (unsigned int) header.checksum_scheme, header.checksum, - offset); - goto error; - } - - if (!is_version_valid(header.major, header.minor)) { - BT_LOGE("Invalid metadata packet version: " - "version=%u.%u, offset=%ld", - header.major, header.minor, offset); - goto error; - } - - /* Set expected trace UUID if not set; otherwise validate it */ - if (mdec) { - if (!mdec->is_uuid_set) { - memcpy(mdec->uuid, header.uuid, sizeof(header.uuid)); - mdec->is_uuid_set = true; - } else if (bt_uuid_compare(header.uuid, mdec->uuid)) { - BT_LOGE("Metadata UUID mismatch between packets of the same stream: " - "packet-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " - "expected-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " - "offset=%ld", - (unsigned int) header.uuid[0], - (unsigned int) header.uuid[1], - (unsigned int) header.uuid[2], - (unsigned int) header.uuid[3], - (unsigned int) header.uuid[4], - (unsigned int) header.uuid[5], - (unsigned int) header.uuid[6], - (unsigned int) header.uuid[7], - (unsigned int) header.uuid[8], - (unsigned int) header.uuid[9], - (unsigned int) header.uuid[10], - (unsigned int) header.uuid[11], - (unsigned int) header.uuid[12], - (unsigned int) header.uuid[13], - (unsigned int) header.uuid[14], - (unsigned int) header.uuid[15], - (unsigned int) mdec->uuid[0], - (unsigned int) mdec->uuid[1], - (unsigned int) mdec->uuid[2], - (unsigned int) mdec->uuid[3], - (unsigned int) mdec->uuid[4], - (unsigned int) mdec->uuid[5], - (unsigned int) mdec->uuid[6], - (unsigned int) mdec->uuid[7], - (unsigned int) mdec->uuid[8], - (unsigned int) mdec->uuid[9], - (unsigned int) mdec->uuid[10], - (unsigned int) mdec->uuid[11], - (unsigned int) mdec->uuid[12], - (unsigned int) mdec->uuid[13], - (unsigned int) mdec->uuid[14], - (unsigned int) mdec->uuid[15], - offset); - goto error; - } - } - - if ((header.content_size / CHAR_BIT) < sizeof(header)) { - BT_LOGE("Bad metadata packet content size: content-size=%u, " - "offset=%ld", header.content_size, offset); - goto error; - } - - toread = header.content_size / CHAR_BIT - sizeof(header); - - for (;;) { - size_t loop_read; - - loop_read = MIN(sizeof(buf) - 1, toread); - readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp); - if (ferror(in_fp)) { - BT_LOGE("Cannot read metadata packet buffer: " - "offset=%ld, read-size=%zu", - ftell(in_fp), loop_read); - goto error; - } - if (readlen > loop_read) { - BT_LOGE("fread returned more byte than expected: " - "read-size-asked=%zu, read-size-returned=%zu", - loop_read, readlen); - goto error; - } - - writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp); - if (writelen < readlen || ferror(out_fp)) { - BT_LOGE("Cannot write decoded metadata text to buffer: " - "read-offset=%ld, write-size=%zu", - ftell(in_fp), readlen); - goto error; - } - - toread -= readlen; - if (toread == 0) { - int fseek_ret; - - /* Read leftover padding */ - toread = (header.packet_size - header.content_size) / - CHAR_BIT; - fseek_ret = fseek(in_fp, toread, SEEK_CUR); - if (fseek_ret < 0) { - BT_LOGW_STR("Missing padding at the end of the metadata stream."); - } - break; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( - struct ctf_metadata_decoder *mdec, FILE *fp, - char **buf, int byte_order) -{ - FILE *out_fp; - size_t size; - int ret = 0; - int tret; - size_t packet_index = 0; - - out_fp = bt_open_memstream(buf, &size); - if (out_fp == NULL) { - BT_LOGE("Cannot open memory stream: %s: mdec-addr=%p", - strerror(errno), mdec); - goto error; - } - - for (;;) { - if (feof(fp) != 0) { - break; - } - - tret = decode_packet(mdec, fp, out_fp, byte_order); - if (tret) { - BT_LOGE("Cannot decode packet: index=%zu, mdec-addr=%p", - packet_index, mdec); - goto error; - } - - packet_index++; - } - - /* Make sure the whole string ends with a null character */ - tret = fputc('\0', out_fp); - if (tret == EOF) { - BT_LOGE("Cannot append '\\0' to the decoded metadata buffer: " - "mdec-addr=%p", mdec); - goto error; - } - - /* Close stream, which also flushes the buffer */ - ret = bt_close_memstream(buf, &size, out_fp); - /* - * See fclose(3). Further access to out_fp after both success - * and error, even through another bt_close_memstream(), results - * in undefined behavior. Nullify out_fp to ensure we don't - * fclose it twice on error. - */ - out_fp = NULL; - if (ret < 0) { - BT_LOGE("Cannot close memory stream: %s: mdec-addr=%p", - strerror(errno), mdec); - goto error; - } - - goto end; - -error: - ret = -1; - - if (out_fp) { - if (bt_close_memstream(buf, &size, out_fp)) { - BT_LOGE("Cannot close memory stream: %s: mdec-addr=%p", - strerror(errno), mdec); - } - } - - if (*buf) { - free(*buf); - *buf = NULL; - } - -end: - return ret; -} - -BT_HIDDEN -int ctf_metadata_decoder_packetized_file_stream_to_buf( - FILE *fp, char **buf, int byte_order) -{ - return ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( - NULL, fp, buf, byte_order); -} - -BT_HIDDEN -struct ctf_metadata_decoder *ctf_metadata_decoder_create( - bt_self_component_source *self_comp, - const struct ctf_metadata_decoder_config *config) -{ - struct ctf_metadata_decoder *mdec = - g_new0(struct ctf_metadata_decoder, 1); - struct ctf_metadata_decoder_config default_config = { - .clock_class_offset_s = 0, - .clock_class_offset_ns = 0, - }; - - if (!config) { - config = &default_config; - } - - BT_LOGD("Creating CTF metadata decoder: " - "clock-class-offset-s=%" PRId64 ", " - "clock-class-offset-ns=%" PRId64, - config->clock_class_offset_s, config->clock_class_offset_ns); - - if (!mdec) { - BT_LOGE_STR("Failed to allocate one CTF metadata decoder."); - goto end; - } - - mdec->config = *config; - mdec->visitor = ctf_visitor_generate_ir_create(self_comp, config); - if (!mdec->visitor) { - BT_LOGE("Failed to create a CTF IR metadata AST visitor: " - "mdec-addr=%p", mdec); - ctf_metadata_decoder_destroy(mdec); - mdec = NULL; - goto end; - } - - BT_LOGD("Creating CTF metadata decoder: " - "clock-class-offset-s=%" PRId64 ", " - "clock-class-offset-ns=%" PRId64 ", addr=%p", - config->clock_class_offset_s, config->clock_class_offset_ns, - mdec); - -end: - return mdec; -} - -BT_HIDDEN -void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec) -{ - if (!mdec) { - return; - } - - BT_LOGD("Destroying CTF metadata decoder: addr=%p", mdec); - ctf_visitor_generate_ir_destroy(mdec->visitor); - g_free(mdec); -} - -BT_HIDDEN -enum ctf_metadata_decoder_status ctf_metadata_decoder_decode( - struct ctf_metadata_decoder *mdec, FILE *fp) -{ - enum ctf_metadata_decoder_status status = - CTF_METADATA_DECODER_STATUS_OK; - int ret; - struct ctf_scanner *scanner = NULL; - char *buf = NULL; - bool close_fp = false; - - BT_ASSERT(mdec); - - if (ctf_metadata_decoder_is_packetized(fp, &mdec->bo)) { - BT_LOGD("Metadata stream is packetized: mdec-addr=%p", mdec); - ret = ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( - mdec, fp, &buf, mdec->bo); - if (ret) { - BT_LOGE("Cannot decode packetized metadata packets to metadata text: " - "mdec-addr=%p, ret=%d", mdec, ret); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - - if (strlen(buf) == 0) { - /* An empty metadata packet is OK. */ - goto end; - } - - /* Convert the real file pointer to a memory file pointer */ - fp = bt_fmemopen(buf, strlen(buf), "rb"); - close_fp = true; - if (!fp) { - BT_LOGE("Cannot memory-open metadata buffer: %s: " - "mdec-addr=%p", strerror(errno), mdec); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - } else { - unsigned int major, minor; - ssize_t nr_items; - const long init_pos = ftell(fp); - - BT_LOGD("Metadata stream is plain text: mdec-addr=%p", mdec); - - if (init_pos < 0) { - BT_LOGE_ERRNO("Failed to get current file position", "."); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - - /* Check text-only metadata header and version */ - nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor); - if (nr_items < 2) { - BT_LOGW("Missing \"/* CTF major.minor\" signature in plain text metadata file stream: " - "mdec-addr=%p", mdec); - } - - BT_LOGD("Found metadata stream version in signature: version=%u.%u", major, minor); - - if (!is_version_valid(major, minor)) { - BT_LOGE("Invalid metadata version found in plain text signature: " - "version=%u.%u, mdec-addr=%p", major, minor, - mdec); - status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION; - goto end; - } - - if (fseek(fp, init_pos, SEEK_SET)) { - BT_LOGE("Cannot seek metadata file stream to initial position: %s: " - "mdec-addr=%p", strerror(errno), mdec); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - } - - if (BT_LOG_ON_VERBOSE) { - yydebug = 1; - } - - /* Allocate a scanner and append the metadata text content */ - scanner = ctf_scanner_alloc(); - if (!scanner) { - BT_LOGE("Cannot allocate a metadata lexical scanner: " - "mdec-addr=%p", mdec); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - - BT_ASSERT(fp); - ret = ctf_scanner_append_ast(scanner, fp); - if (ret) { - BT_LOGE("Cannot create the metadata AST out of the metadata text: " - "mdec-addr=%p", mdec); - status = CTF_METADATA_DECODER_STATUS_INCOMPLETE; - goto end; - } - - ret = ctf_visitor_semantic_check(0, &scanner->ast->root); - if (ret) { - BT_LOGE("Validation of the metadata semantics failed: " - "mdec-addr=%p", mdec); - status = CTF_METADATA_DECODER_STATUS_ERROR; - goto end; - } - - ret = ctf_visitor_generate_ir_visit_node(mdec->visitor, - &scanner->ast->root); - switch (ret) { - case 0: - /* Success */ - break; - case -EINCOMPLETE: - BT_LOGD("While visiting metadata AST: incomplete data: " - "mdec-addr=%p", mdec); - status = CTF_METADATA_DECODER_STATUS_INCOMPLETE; - goto end; - default: - BT_LOGE("Failed to visit AST node to create CTF IR objects: " - "mdec-addr=%p, ret=%d", mdec, ret); - status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR; - goto end; - } - -end: - if (scanner) { - ctf_scanner_free(scanner); - } - - yydebug = 0; - - if (fp && close_fp) { - if (fclose(fp)) { - BT_LOGE("Cannot close metadata file stream: " - "mdec-addr=%p", mdec); - } - } - - if (buf) { - free(buf); - } - - return status; -} - -BT_HIDDEN -bt_trace_class *ctf_metadata_decoder_get_ir_trace_class( - struct ctf_metadata_decoder *mdec) -{ - return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor); -} - -BT_HIDDEN -struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class( - struct ctf_metadata_decoder *mdec) -{ - return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor); -} diff --git a/plugins/ctf/common/metadata/decoder.h b/plugins/ctf/common/metadata/decoder.h deleted file mode 100644 index b0f179be..00000000 --- a/plugins/ctf/common/metadata/decoder.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef _METADATA_DECODER_H -#define _METADATA_DECODER_H - -/* - * Copyright 2016-2017 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include - -#include - -/* A CTF metadata decoder object */ -struct ctf_metadata_decoder; - -/* CTF metadata decoder status */ -enum ctf_metadata_decoder_status { - CTF_METADATA_DECODER_STATUS_OK = 0, - CTF_METADATA_DECODER_STATUS_ERROR = -1, - CTF_METADATA_DECODER_STATUS_INCOMPLETE = -2, - CTF_METADATA_DECODER_STATUS_INVAL_VERSION = -3, - CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR = -4, -}; - -/* Decoding configuration */ -struct ctf_metadata_decoder_config { - int64_t clock_class_offset_s; - int64_t clock_class_offset_ns; -}; - -/* - * Creates a CTF metadata decoder. - * - * Returns `NULL` on error. - */ -BT_HIDDEN -struct ctf_metadata_decoder *ctf_metadata_decoder_create( - bt_self_component_source *self_comp, - const struct ctf_metadata_decoder_config *config); - -/* - * Destroys a CTF metadata decoder that you created with - * ctf_metadata_decoder_create(). - */ -BT_HIDDEN -void ctf_metadata_decoder_destroy( - struct ctf_metadata_decoder *metadata_decoder); - -/* - * Decodes a new chunk of CTF metadata. - * - * This function reads the metadata from the current position of `fp` - * until the end of this file stream. If it finds new information (new - * event class, new stream class, or new clock class), it appends this - * information to the decoder's trace object (as returned by - * ctf_metadata_decoder_get_ir_trace_class()), or it creates this trace. - * - * The metadata can be packetized or not. - * - * The metadata chunk needs to be complete and scannable, that is, - * zero or more complete top-level blocks. If it's incomplete, this - * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this - * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you - * need to call it again with the same metadata and more to make it - * complete. For example: - * - * First call: event { name = hell - * Second call: event { name = hello_world; ... }; - * - * If the conversion from the metadata text to CTF IR objects fails, - * this function returns `CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR`. - * - * If everything goes as expected, this function returns - * `CTF_METADATA_DECODER_STATUS_OK`. - */ -BT_HIDDEN -enum ctf_metadata_decoder_status ctf_metadata_decoder_decode( - struct ctf_metadata_decoder *metadata_decoder, FILE *fp); - -BT_HIDDEN -bt_trace_class *ctf_metadata_decoder_get_ir_trace_class( - struct ctf_metadata_decoder *mdec); - -BT_HIDDEN -struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class( - struct ctf_metadata_decoder *mdec); - -/* - * Checks whether or not a given metadata file stream is packetized, and - * if so, sets `*byte_order` to the byte order of the first packet. - */ -BT_HIDDEN -bool ctf_metadata_decoder_is_packetized(FILE *fp, int *byte_order); - -/* - * Decodes a packetized metadata file stream to a NULL-terminated - * text buffer using the given byte order. - */ -BT_HIDDEN -int ctf_metadata_decoder_packetized_file_stream_to_buf( - FILE *fp, char **buf, int byte_order); - -#endif /* _METADATA_DECODER_H */ diff --git a/plugins/ctf/common/metadata/lexer.l b/plugins/ctf/common/metadata/lexer.l deleted file mode 100644 index 694ddc3a..00000000 --- a/plugins/ctf/common/metadata/lexer.l +++ /dev/null @@ -1,148 +0,0 @@ -%{ -/* - * lexer.l - * - * Common Trace Formal Lexer - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-LEXER" -#include "logging.h" - -#include -#include -#include "scanner.h" -#include "parser.h" -#include "ast.h" - -#define YY_FATAL_ERROR(_msg) BT_LOGF_STR(_msg) - -#define PARSE_INTEGER_LITERAL(base) \ - do { \ - errno = 0; \ - yylval->ull = strtoull(yytext, NULL, base); \ - if (errno) { \ - _BT_LOGE_LINENO(yylineno, \ - "Cannot parser constant integer: " \ - "base=%d, text=\"%s\"", base, yytext); \ - return CTF_ERROR; \ - } \ - } while (0) - -BT_HIDDEN -void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src); - -static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) - __attribute__((unused)); -static int input (yyscan_t yyscanner) __attribute__((unused)); - -BT_HIDDEN -int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim); - -%} - -%x comment_ml comment_sl string_lit char_const -%option reentrant yylineno noyywrap bison-bridge -%option extra-type="struct ctf_scanner *" - /* bison-locations */ -INTEGER_SUFFIX (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu) -DIGIT [0-9] -NONDIGIT [a-zA-Z_] -HEXDIGIT [0-9A-Fa-f] -OCTALDIGIT [0-7] -UCHARLOWERCASE \\u{HEXDIGIT}{4} -UCHARUPPERCASE \\U{HEXDIGIT}{8} -ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE} -IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})* -%% - - /* - * Using start conditions to deal with comments - * and strings. - */ - -"/*" BEGIN(comment_ml); -[^*\n]* /* eat anything that's not a '*' */ -"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ -\n -"*"+"/" BEGIN(INITIAL); - -"//"[^\n]*\n /* skip comment */ - -L?\"(\\.|[^\\"])*\" { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; } -L?\'(\\.|[^\\'])*\' { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; } - -"[" return CTF_LSBRAC; -"]" return CTF_RSBRAC; -"(" return CTF_LPAREN; -")" return CTF_RPAREN; -"{" return CTF_LBRAC; -"}" return CTF_RBRAC; -"->" return CTF_RARROW; -"*" return CTF_STAR; -"+" return CTF_PLUS; -"-" return CTF_MINUS; -"<" return CTF_LT; -">" return CTF_GT; -:= return CTF_TYPEASSIGN; -: return CTF_COLON; -; return CTF_SEMICOLON; -"..." return CTF_DOTDOTDOT; -"." return CTF_DOT; -= return CTF_EQUAL; -"," return CTF_COMMA; -align setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN; -const setstring(yyextra, yylval, yytext); return CTF_CONST; -char setstring(yyextra, yylval, yytext); return CTF_CHAR; -clock setstring(yyextra, yylval, yytext); return CTF_CLOCK; -double setstring(yyextra, yylval, yytext); return CTF_DOUBLE; -enum setstring(yyextra, yylval, yytext); return CTF_ENUM; -env setstring(yyextra, yylval, yytext); return CTF_ENV; -event setstring(yyextra, yylval, yytext); return CTF_EVENT; -floating_point setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT; -float setstring(yyextra, yylval, yytext); return CTF_FLOAT; -integer setstring(yyextra, yylval, yytext); return CTF_INTEGER; -int setstring(yyextra, yylval, yytext); return CTF_INT; -long setstring(yyextra, yylval, yytext); return CTF_LONG; -short setstring(yyextra, yylval, yytext); return CTF_SHORT; -signed setstring(yyextra, yylval, yytext); return CTF_SIGNED; -stream setstring(yyextra, yylval, yytext); return CTF_STREAM; -string setstring(yyextra, yylval, yytext); return CTF_STRING; -struct setstring(yyextra, yylval, yytext); return CTF_STRUCT; -trace setstring(yyextra, yylval, yytext); return CTF_TRACE; -callsite setstring(yyextra, yylval, yytext); return CTF_CALLSITE; -typealias setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS; -typedef setstring(yyextra, yylval, yytext); return CTF_TYPEDEF; -unsigned setstring(yyextra, yylval, yytext); return CTF_UNSIGNED; -variant setstring(yyextra, yylval, yytext); return CTF_VARIANT; -void setstring(yyextra, yylval, yytext); return CTF_VOID; -_Bool setstring(yyextra, yylval, yytext); return CTF_BOOL; -_Complex setstring(yyextra, yylval, yytext); return CTF_COMPLEX; -_Imaginary setstring(yyextra, yylval, yytext); return CTF_IMAGINARY; -[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL; -0{OCTALDIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL; -0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL; - -{IDENTIFIER} BT_LOGV("Got identifier: id=\"%s\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER; -[ \t\r\n] ; /* ignore */ -. _BT_LOGE_LINENO(yylineno, "Invalid character: char=\"%c\", val=0x%02x", isprint(yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR; -%% diff --git a/plugins/ctf/common/metadata/logging.c b/plugins/ctf/common/metadata/logging.c deleted file mode 100644 index c5140a3b..00000000 --- a/plugins/ctf/common/metadata/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL metadata_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL"); diff --git a/plugins/ctf/common/metadata/logging.h b/plugins/ctf/common/metadata/logging.h deleted file mode 100644 index 236cbc23..00000000 --- a/plugins/ctf/common/metadata/logging.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CTF_METADATA_LOGGING_H -#define CTF_METADATA_LOGGING_H - -/* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL metadata_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(metadata_log_level); - -#define _BT_LOGV_LINENO(_lineno, _msg, args...) \ - BT_LOGV("At line %u in metadata stream: " _msg, _lineno, ## args) - -#define _BT_LOGW_LINENO(_lineno, _msg, args...) \ - BT_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args) - -#define _BT_LOGE_LINENO(_lineno, _msg, args...) \ - BT_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args) - -#endif /* CTF_METADATA_LOGGING_H */ diff --git a/plugins/ctf/common/metadata/objstack.c b/plugins/ctf/common/metadata/objstack.c deleted file mode 100644 index 2f703800..00000000 --- a/plugins/ctf/common/metadata/objstack.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * objstack.c - * - * Common Trace Format Object Stack. - * - * Copyright 2013 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-OBJSTACK" -#include "logging.h" - -#include -#include -#include -#include - -#define OBJSTACK_ALIGN 8 /* Object stack alignment */ -#define OBJSTACK_INIT_LEN 128 -#define OBJSTACK_POISON 0xcc - -struct objstack { - struct bt_list_head head; /* list of struct objstack_node */ -}; - -struct objstack_node { - struct bt_list_head node; - size_t len; - size_t used_len; - char __attribute__ ((aligned (OBJSTACK_ALIGN))) data[]; -}; - -BT_HIDDEN -struct objstack *objstack_create(void) -{ - struct objstack *objstack; - struct objstack_node *node; - - objstack = calloc(1, sizeof(*objstack)); - if (!objstack) { - BT_LOGE_STR("Failed to allocate one object stack."); - return NULL; - } - node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, - sizeof(char)); - if (!node) { - BT_LOGE_STR("Failed to allocate one object stack node."); - free(objstack); - return NULL; - } - BT_INIT_LIST_HEAD(&objstack->head); - bt_list_add_tail(&node->node, &objstack->head); - node->len = OBJSTACK_INIT_LEN; - return objstack; -} - -static -void objstack_node_free(struct objstack_node *node) -{ - size_t offset, len; - char *p; - - if (!node) - return; - p = (char *) node; - len = sizeof(*node) + node->len; - for (offset = 0; offset < len; offset++) - p[offset] = OBJSTACK_POISON; - free(node); -} - -BT_HIDDEN -void objstack_destroy(struct objstack *objstack) -{ - struct objstack_node *node, *p; - - if (!objstack) - return; - bt_list_for_each_entry_safe(node, p, &objstack->head, node) { - bt_list_del(&node->node); - objstack_node_free(node); - } - free(objstack); -} - -static -struct objstack_node *objstack_append_node(struct objstack *objstack) -{ - struct objstack_node *last_node, *new_node; - - /* Get last node */ - last_node = bt_list_entry(objstack->head.prev, - struct objstack_node, node); - - /* Allocate new node with double of size of last node */ - new_node = calloc(sizeof(struct objstack_node) + (last_node->len << 1), - sizeof(char)); - if (!new_node) { - BT_LOGE_STR("Failed to allocate one object stack node."); - return NULL; - } - bt_list_add_tail(&new_node->node, &objstack->head); - new_node->len = last_node->len << 1; - return new_node; -} - -BT_HIDDEN -void *objstack_alloc(struct objstack *objstack, size_t len) -{ - struct objstack_node *last_node; - void *p; - - len = ALIGN(len, OBJSTACK_ALIGN); - - /* Get last node */ - last_node = bt_list_entry(objstack->head.prev, - struct objstack_node, node); - while (last_node->len - last_node->used_len < len) { - last_node = objstack_append_node(objstack); - if (!last_node) { - return NULL; - } - } - p = &last_node->data[last_node->used_len]; - last_node->used_len += len; - return p; -} diff --git a/plugins/ctf/common/metadata/objstack.h b/plugins/ctf/common/metadata/objstack.h deleted file mode 100644 index c026eb54..00000000 --- a/plugins/ctf/common/metadata/objstack.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _OBJSTACK_H -#define _OBJSTACK_H - -/* - * objstack.h - * - * Common Trace Format Object Stack. - * - * Copyright 2013 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -struct objstack; - -BT_HIDDEN -struct objstack *objstack_create(void); -BT_HIDDEN -void objstack_destroy(struct objstack *objstack); - -/* - * Allocate len bytes of zeroed memory. - * Return NULL on error. - */ -BT_HIDDEN -void *objstack_alloc(struct objstack *objstack, size_t len); - -#endif /* _OBJSTACK_H */ diff --git a/plugins/ctf/common/metadata/parser.y b/plugins/ctf/common/metadata/parser.y deleted file mode 100644 index 4e666dbb..00000000 --- a/plugins/ctf/common/metadata/parser.y +++ /dev/null @@ -1,2592 +0,0 @@ -%{ -/* - * ctf-parser.y - * - * Common Trace Format Metadata Grammar. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-PARSER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scanner.h" -#include "parser.h" -#include "ast.h" -#include "objstack.h" - -#if BT_LOG_ENABLED_VERBOSE -# define YYDEBUG 1 -# define YYFPRINTF(_stream, _fmt, args...) BT_LOGV(_fmt, ## args) -#else -# define YYDEBUG 0 -#endif - -/* Join two lists, put "add" at the end of "head". */ -static inline void -_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head) -{ - /* Do nothing if the list which gets added is empty. */ - if (add != add->next) { - add->next->prev = head->prev; - add->prev->next = head; - head->prev->next = add->next; - head->prev = add->prev; - } -} - -BT_HIDDEN -int yyparse(struct ctf_scanner *scanner, yyscan_t yyscanner); -BT_HIDDEN -int yylex(union YYSTYPE *yyval, yyscan_t yyscanner); -BT_HIDDEN -int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals); -BT_HIDDEN -int yylex_destroy(yyscan_t yyscanner); -BT_HIDDEN -void yyrestart(FILE * in_str, yyscan_t yyscanner); -BT_HIDDEN -int yyget_lineno(yyscan_t yyscanner); -BT_HIDDEN -char *yyget_text(yyscan_t yyscanner); - -static const char *node_type_to_str[] = { -#define ENTRY(S) [S] = #S, - FOREACH_CTF_NODES(ENTRY) -#undef ENTRY -}; - -/* - * Static node for out of memory errors. Only "type" is used. lineno is - * always left at 0. The rest of the node content can be overwritten, - * but is never used. - */ -static struct ctf_node error_node = { - .type = NODE_ERROR, -}; - -BT_HIDDEN -const char *node_type(struct ctf_node *node) -{ - if (node->type < NR_NODE_TYPES) - return node_type_to_str[node->type]; - else - return NULL; -} - -void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src) -{ - lvalp->s = objstack_alloc(scanner->objstack, strlen(src) + 1); - strcpy(lvalp->s, src); -} - -static -int str_check(size_t str_len, size_t offset, size_t len) -{ - /* check overflow */ - if (offset + len < offset) - return -1; - if (offset + len > str_len) - return -1; - return 0; -} - -static -int bt_isodigit(int c) -{ - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - return 1; - default: - return 0; - } -} - -static -int parse_base_sequence(const char *src, size_t len, size_t pos, - char *buffer, size_t *buf_len, int base) -{ - const size_t max_char = 3; - int nr_char = 0; - - while (!str_check(len, pos, 1) && nr_char < max_char) { - char c = src[pos++]; - - if (base == 8) { - if (bt_isodigit(c)) - buffer[nr_char++] = c; - else - break; - } else if (base == 16) { - if (isxdigit(c)) - buffer[nr_char++] = c; - else - break; - - } else { - /* Unsupported base */ - return -1; - } - } - BT_ASSERT(nr_char > 0); - buffer[nr_char] = '\0'; - *buf_len = nr_char; - return 0; -} - -static -int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, - size_t len, const char *src, char delim) -{ - size_t pos = 0, dpos = 0; - - if (str_check(len, pos, 1)) - return -1; - if (src[pos++] != delim) - return -1; - - while (src[pos] != delim) { - char c; - - if (str_check(len, pos, 1)) - return -1; - c = src[pos++]; - if (c == '\\') { - if (str_check(len, pos, 1)) - return -1; - c = src[pos++]; - - switch (c) { - case 'a': - c = '\a'; - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case '\\': - c = '\\'; - break; - case '\'': - c = '\''; - break; - case '\"': - c = '\"'; - break; - case '?': - c = '?'; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - char oct_buffer[4]; - size_t oct_len; - - if (parse_base_sequence(src, len, pos - 1, - oct_buffer, &oct_len, 8)) - return -1; - c = strtoul(&oct_buffer[0], NULL, 8); - pos += oct_len - 1; - break; - } - case 'x': - { - char hex_buffer[4]; - size_t hex_len; - - if (parse_base_sequence(src, len, pos, - hex_buffer, &hex_len, 16)) - return -1; - c = strtoul(&hex_buffer[0], NULL, 16); - pos += hex_len; - break; - } - default: - return -1; - } - } - if (str_check(len, dpos, 1)) - return -1; - lvalp->s[dpos++] = c; - } - - if (str_check(len, dpos, 1)) - return -1; - lvalp->s[dpos++] = '\0'; - - if (str_check(len, pos, 1)) - return -1; - if (src[pos++] != delim) - return -1; - - if (str_check(len, pos, 1)) - return -1; - if (src[pos] != '\0') - return -1; - return 0; -} - -int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, - const char *src, char delim) -{ - size_t len; - - len = strlen(src) + 1; - lvalp->s = objstack_alloc(scanner->objstack, len); - if (src[0] == 'L') { - // TODO: import wide string - _BT_LOGE_LINENO(yyget_lineno(scanner), - "wide characters are not supported as of this version: " - "scanner-addr=%p", scanner); - return -1; - } else { - return import_basic_string(scanner, lvalp, len, src, delim); - } -} - -static void init_scope(struct ctf_scanner_scope *scope, - struct ctf_scanner_scope *parent) -{ - scope->parent = parent; - scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, NULL); -} - -static void finalize_scope(struct ctf_scanner_scope *scope) -{ - g_hash_table_destroy(scope->classes); -} - -static void push_scope(struct ctf_scanner *scanner) -{ - struct ctf_scanner_scope *ns; - - BT_LOGV("Pushing scope: scanner-addr=%p", scanner); - ns = malloc(sizeof(struct ctf_scanner_scope)); - init_scope(ns, scanner->cs); - scanner->cs = ns; -} - -static void pop_scope(struct ctf_scanner *scanner) -{ - struct ctf_scanner_scope *os; - - BT_LOGV("Popping scope: scanner-addr=%p", scanner); - os = scanner->cs; - scanner->cs = os->parent; - finalize_scope(os); - free(os); -} - -static int lookup_type(struct ctf_scanner_scope *s, const char *id) -{ - int ret; - - ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id)); - BT_LOGV("Looked up type: scanner-addr=%p, id=\"%s\", ret=%d", - s, id, ret); - return ret; -} - -BT_HIDDEN -int is_type(struct ctf_scanner *scanner, const char *id) -{ - struct ctf_scanner_scope *it; - int ret = 0; - - for (it = scanner->cs; it != NULL; it = it->parent) { - if (lookup_type(it, id)) { - ret = 1; - break; - } - } - BT_LOGV("Found if ID is type: scanner-addr=%p, id=\"%s\", ret=%d", - scanner, id, ret); - return ret; -} - -static void add_type(struct ctf_scanner *scanner, char *id) -{ - BT_LOGV("Adding type: scanner-addr=%p, id=\"%s\"", - scanner, id); - if (lookup_type(scanner->cs, id)) - return; - g_hash_table_insert(scanner->cs->classes, id, id); -} - -static struct ctf_node *make_node(struct ctf_scanner *scanner, - enum node_type type) -{ - struct ctf_node *node; - - node = objstack_alloc(scanner->objstack, sizeof(*node)); - if (!node) { - _BT_LOGE_LINENO(yyget_lineno(scanner->scanner), - "failed to allocate one stack entry: " - "scanner-addr=%p", scanner); - return &error_node; - } - node->type = type; - node->lineno = yyget_lineno(scanner->scanner); - BT_INIT_LIST_HEAD(&node->tmp_head); - bt_list_add(&node->siblings, &node->tmp_head); - - switch (type) { - case NODE_ROOT: - node->type = NODE_ERROR; - BT_LOGE("Trying to create root node: scanner-addr=%p", - scanner); - break; - case NODE_EVENT: - BT_INIT_LIST_HEAD(&node->u.event.declaration_list); - break; - case NODE_STREAM: - BT_INIT_LIST_HEAD(&node->u.stream.declaration_list); - break; - case NODE_ENV: - BT_INIT_LIST_HEAD(&node->u.env.declaration_list); - break; - case NODE_TRACE: - BT_INIT_LIST_HEAD(&node->u.trace.declaration_list); - break; - case NODE_CLOCK: - BT_INIT_LIST_HEAD(&node->u.clock.declaration_list); - break; - case NODE_CALLSITE: - BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list); - break; - case NODE_CTF_EXPRESSION: - BT_INIT_LIST_HEAD(&node->u.ctf_expression.left); - BT_INIT_LIST_HEAD(&node->u.ctf_expression.right); - break; - case NODE_UNARY_EXPRESSION: - break; - case NODE_TYPEDEF: - BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators); - break; - case NODE_TYPEALIAS_TARGET: - BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators); - break; - case NODE_TYPEALIAS_ALIAS: - BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators); - break; - case NODE_TYPEALIAS: - break; - case NODE_TYPE_SPECIFIER: - break; - case NODE_TYPE_SPECIFIER_LIST: - BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head); - break; - case NODE_POINTER: - break; - case NODE_TYPE_DECLARATOR: - BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers); - break; - case NODE_FLOATING_POINT: - BT_INIT_LIST_HEAD(&node->u.floating_point.expressions); - break; - case NODE_INTEGER: - BT_INIT_LIST_HEAD(&node->u.integer.expressions); - break; - case NODE_STRING: - BT_INIT_LIST_HEAD(&node->u.string.expressions); - break; - case NODE_ENUMERATOR: - BT_INIT_LIST_HEAD(&node->u.enumerator.values); - break; - case NODE_ENUM: - BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list); - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators); - break; - case NODE_VARIANT: - BT_INIT_LIST_HEAD(&node->u.variant.declaration_list); - break; - case NODE_STRUCT: - BT_INIT_LIST_HEAD(&node->u._struct.declaration_list); - BT_INIT_LIST_HEAD(&node->u._struct.min_align); - break; - case NODE_UNKNOWN: - default: - node->type = NODE_ERROR; - BT_LOGE("Unknown node type: scanner-addr=%p, node-type=%d", - scanner, type); - break; - } - - return node; -} - -static int reparent_ctf_expression(struct ctf_node *node, - struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_EVENT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); - break; - case NODE_STREAM: - _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); - break; - case NODE_ENV: - _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); - break; - case NODE_TRACE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); - break; - case NODE_CLOCK: - _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); - break; - case NODE_CALLSITE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); - break; - case NODE_FLOATING_POINT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions); - break; - case NODE_INTEGER: - _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions); - break; - case NODE_STRING: - _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions); - break; - - case NODE_ROOT: - case NODE_CTF_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_VARIANT: - case NODE_STRUCT: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_ROOT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list); - break; - case NODE_EVENT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); - break; - case NODE_STREAM: - _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); - break; - case NODE_ENV: - _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); - break; - case NODE_TRACE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); - break; - case NODE_CLOCK: - _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); - break; - case NODE_CALLSITE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); - break; - case NODE_VARIANT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); - break; - case NODE_STRUCT: - _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); - break; - - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_CTF_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_ROOT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list); - break; - case NODE_EVENT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); - break; - case NODE_STREAM: - _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); - break; - case NODE_ENV: - _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); - break; - case NODE_TRACE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); - break; - case NODE_CLOCK: - _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); - break; - case NODE_CALLSITE: - _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); - break; - case NODE_VARIANT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); - break; - case NODE_STRUCT: - _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); - break; - - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_CTF_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_field_class_specifier(struct ctf_node *node, - struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_TYPE_SPECIFIER_LIST: - _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head); - break; - - case NODE_TYPE_SPECIFIER: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_VARIANT: - case NODE_STRUCT: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPE_DECLARATOR: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_TYPEALIAS: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_CTF_EXPRESSION: - case NODE_POINTER: - case NODE_ENUMERATOR: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_field_class_specifier_list(struct ctf_node *node, - struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_ROOT: - bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list); - break; - case NODE_EVENT: - bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list); - break; - case NODE_STREAM: - bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list); - break; - case NODE_ENV: - bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list); - break; - case NODE_TRACE: - bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list); - break; - case NODE_CLOCK: - bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list); - break; - case NODE_CALLSITE: - bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list); - break; - case NODE_VARIANT: - bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list); - break; - case NODE_STRUCT: - bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list); - break; - case NODE_TYPEDEF: - parent->u.field_class_def.field_class_specifier_list = node; - break; - case NODE_TYPEALIAS_TARGET: - parent->u.field_class_alias_target.field_class_specifier_list = node; - break; - case NODE_TYPEALIAS_ALIAS: - parent->u.field_class_alias_name.field_class_specifier_list = node; - break; - case NODE_ENUM: - parent->u._enum.container_field_class = node; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - parent->u.struct_or_variant_declaration.field_class_specifier_list = node; - break; - case NODE_TYPE_DECLARATOR: - case NODE_TYPE_SPECIFIER: - case NODE_TYPEALIAS: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_CTF_EXPRESSION: - case NODE_POINTER: - case NODE_ENUMERATOR: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_field_class_declarator(struct ctf_node *node, - struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_TYPE_DECLARATOR: - parent->u.field_class_declarator.type = TYPEDEC_NESTED; - parent->u.field_class_declarator.u.nested.field_class_declarator = node; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators); - break; - case NODE_TYPEDEF: - _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators); - break; - case NODE_TYPEALIAS_TARGET: - _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators); - break; - case NODE_TYPEALIAS_ALIAS: - _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators); - break; - - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_VARIANT: - case NODE_STRUCT: - case NODE_TYPEALIAS: - case NODE_ENUM: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_CTF_EXPRESSION: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_ENUMERATOR: - case NODE_UNARY_EXPRESSION: - return -EPERM; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -/* - * set_parent_node - * - * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to - * create the link declared by the input, -ENOENT if node or parent is NULL, - * -EINVAL if there is an internal structure problem. - */ -static int set_parent_node(struct ctf_node *node, - struct ctf_node *parent) -{ - if (!node || !parent) - return -ENOENT; - - /* Note: Linking to parent will be done only by an external visitor */ - - switch (node->type) { - case NODE_ROOT: - BT_LOGE_STR("Trying to reparent root node."); - return -EINVAL; - - case NODE_EVENT: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event); - } else { - return -EPERM; - } - break; - case NODE_STREAM: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream); - } else { - return -EPERM; - } - break; - case NODE_ENV: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env); - } else { - return -EPERM; - } - break; - case NODE_TRACE: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace); - } else { - return -EPERM; - } - break; - case NODE_CLOCK: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock); - } else { - return -EPERM; - } - break; - case NODE_CALLSITE: - if (parent->type == NODE_ROOT) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite); - } else { - return -EPERM; - } - break; - - case NODE_CTF_EXPRESSION: - return reparent_ctf_expression(node, parent); - case NODE_UNARY_EXPRESSION: - if (parent->type == NODE_TYPE_DECLARATOR) - parent->u.field_class_declarator.bitfield_len = node; - else - return -EPERM; - break; - - case NODE_TYPEDEF: - return reparent_typedef(node, parent); - case NODE_TYPEALIAS_TARGET: - if (parent->type == NODE_TYPEALIAS) - parent->u.field_class_alias.target = node; - else - return -EINVAL; - /* fall-through */ - case NODE_TYPEALIAS_ALIAS: - if (parent->type == NODE_TYPEALIAS) - parent->u.field_class_alias.alias = node; - else - return -EINVAL; - /* fall-through */ - case NODE_TYPEALIAS: - return reparent_field_class_alias(node, parent); - - case NODE_POINTER: - if (parent->type == NODE_TYPE_DECLARATOR) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers); - } else - return -EPERM; - break; - case NODE_TYPE_DECLARATOR: - return reparent_field_class_declarator(node, parent); - - case NODE_TYPE_SPECIFIER_LIST: - return reparent_field_class_specifier_list(node, parent); - - case NODE_TYPE_SPECIFIER: - return reparent_field_class_specifier(node, parent); - - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUM: - case NODE_VARIANT: - case NODE_STRUCT: - return -EINVAL; /* Dealt with internally within grammar */ - - case NODE_ENUMERATOR: - if (parent->type == NODE_ENUM) { - _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list); - } else { - return -EPERM; - } - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - switch (parent->type) { - case NODE_STRUCT: - _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); - break; - case NODE_VARIANT: - _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); - break; - default: - return -EINVAL; - } - break; - - case NODE_UNKNOWN: - default: - BT_LOGE("Unknown node type: node-type=%d", parent->type); - return -EINVAL; - } - return 0; -} - -BT_HIDDEN -void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str) -{ - _BT_LOGE_LINENO(yyget_lineno(scanner->scanner), - "%s: token=\"%s\"", str, yyget_text(scanner->scanner)); -} - -BT_HIDDEN -int yywrap(void) -{ - return 1; -} - -#define reparent_error(scanner, str) \ -do { \ - yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \ - YYERROR; \ -} while (0) - -static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner) -{ - struct ctf_ast *ast; - - ast = objstack_alloc(scanner->objstack, sizeof(*ast)); - if (!ast) - return NULL; - ast->root.type = NODE_ROOT; - BT_INIT_LIST_HEAD(&ast->root.tmp_head); - BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list); - BT_INIT_LIST_HEAD(&ast->root.u.root.trace); - BT_INIT_LIST_HEAD(&ast->root.u.root.env); - BT_INIT_LIST_HEAD(&ast->root.u.root.stream); - BT_INIT_LIST_HEAD(&ast->root.u.root.event); - BT_INIT_LIST_HEAD(&ast->root.u.root.clock); - BT_INIT_LIST_HEAD(&ast->root.u.root.callsite); - return ast; -} - -int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input) -{ - /* Start processing new stream */ - yyrestart(input, scanner->scanner); - return yyparse(scanner, scanner->scanner); -} - -struct ctf_scanner *ctf_scanner_alloc(void) -{ - struct ctf_scanner *scanner; - int ret; - - scanner = malloc(sizeof(*scanner)); - if (!scanner) - return NULL; - memset(scanner, 0, sizeof(*scanner)); - ret = yylex_init_extra(scanner, &scanner->scanner); - if (ret) { - BT_LOGE("yylex_init_extra() failed: ret=%d", ret); - goto cleanup_scanner; - } - scanner->objstack = objstack_create(); - if (!scanner->objstack) - goto cleanup_lexer; - scanner->ast = ctf_ast_alloc(scanner); - if (!scanner->ast) - goto cleanup_objstack; - init_scope(&scanner->root_scope, NULL); - scanner->cs = &scanner->root_scope; - - return scanner; - -cleanup_objstack: - objstack_destroy(scanner->objstack); -cleanup_lexer: - ret = yylex_destroy(scanner->scanner); - if (!ret) - BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d", - scanner, ret); -cleanup_scanner: - free(scanner); - return NULL; -} - -void ctf_scanner_free(struct ctf_scanner *scanner) -{ - int ret; - - if (!scanner) - return; - finalize_scope(&scanner->root_scope); - objstack_destroy(scanner->objstack); - ret = yylex_destroy(scanner->scanner); - if (ret) - BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d", - scanner, ret); - free(scanner); -} - -%} - -%define api.pure - /* %locations */ -%error-verbose -%parse-param {struct ctf_scanner *scanner} -%parse-param {yyscan_t yyscanner} -%lex-param {yyscan_t yyscanner} -/* - * Expect two shift-reduce conflicts. Caused by enum name-opt : type {} - * vs struct { int :value; } (unnamed bit-field). The default is to - * shift, so whenever we encounter an enumeration, we are doing the - * proper thing (shift). It is illegal to declare an enumeration - * "bit-field", so it is OK if this situation ends up in a parsing - * error. - */ -%expect 2 -%start file -%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN -%token IDENTIFIER ID_TYPE -%token CTF_ERROR -%union -{ - long long ll; - unsigned long long ull; - char c; - char *s; - struct ctf_node *n; -} - -%type CTF_STRING_LITERAL CTF_CHARACTER_LITERAL - -%type keywords - -%type CTF_INTEGER_LITERAL -%type postfix_expression unary_expression unary_expression_or_range - -%type declaration -%type event_declaration -%type stream_declaration -%type env_declaration -%type trace_declaration -%type clock_declaration -%type callsite_declaration -%type integer_declaration_specifiers -%type declaration_specifiers -%type alias_declaration_specifiers - -%type field_class_declarator_list -%type integer_field_class_specifier -%type field_class_specifier -%type struct_class_specifier -%type variant_field_class_specifier -%type enum_field_class_specifier -%type struct_or_variant_declaration_list -%type struct_or_variant_declaration -%type struct_or_variant_declarator_list -%type struct_or_variant_declarator -%type enumerator_list -%type enumerator -%type abstract_declarator_list -%type abstract_declarator -%type direct_abstract_declarator -%type alias_abstract_declarator_list -%type alias_abstract_declarator -%type direct_alias_abstract_declarator -%type declarator -%type direct_declarator -%type field_class_declarator -%type direct_field_class_declarator -%type pointer -%type ctf_assignment_expression_list -%type ctf_assignment_expression - -%% - -file: - declaration - { - if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root)) - reparent_error(scanner, "error reparenting to root"); - } - | file declaration - { - if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root)) - reparent_error(scanner, "error reparenting to root"); - } - ; - -keywords: - CTF_VOID - { $$ = yylval.s; } - | CTF_CHAR - { $$ = yylval.s; } - | CTF_SHORT - { $$ = yylval.s; } - | CTF_INT - { $$ = yylval.s; } - | CTF_LONG - { $$ = yylval.s; } - | CTF_FLOAT - { $$ = yylval.s; } - | CTF_DOUBLE - { $$ = yylval.s; } - | CTF_SIGNED - { $$ = yylval.s; } - | CTF_UNSIGNED - { $$ = yylval.s; } - | CTF_BOOL - { $$ = yylval.s; } - | CTF_COMPLEX - { $$ = yylval.s; } - | CTF_IMAGINARY - { $$ = yylval.s; } - | CTF_FLOATING_POINT - { $$ = yylval.s; } - | CTF_INTEGER - { $$ = yylval.s; } - | CTF_STRING - { $$ = yylval.s; } - | CTF_ENUM - { $$ = yylval.s; } - | CTF_VARIANT - { $$ = yylval.s; } - | CTF_STRUCT - { $$ = yylval.s; } - | CTF_CONST - { $$ = yylval.s; } - | CTF_TYPEDEF - { $$ = yylval.s; } - | CTF_EVENT - { $$ = yylval.s; } - | CTF_STREAM - { $$ = yylval.s; } - | CTF_ENV - { $$ = yylval.s; } - | CTF_TRACE - { $$ = yylval.s; } - | CTF_CLOCK - { $$ = yylval.s; } - | CTF_CALLSITE - { $$ = yylval.s; } - | CTF_TOK_ALIGN - { $$ = yylval.s; } - ; - - -/* 2: Phrase structure grammar */ - -postfix_expression: - IDENTIFIER - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - } - | keywords - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - } - | CTF_INTEGER_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT; - $$->u.unary_expression.u.unsigned_constant = $1; - } - | CTF_STRING_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = $1; - } - | CTF_CHARACTER_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = $1; - } - | CTF_LPAREN unary_expression CTF_RPAREN - { - $$ = $2; - } - | postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_SBRAC; - $$->u.unary_expression.u.sbrac_exp = $3; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - | postfix_expression CTF_DOT IDENTIFIER - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - $$->u.unary_expression.link = UNARY_DOTLINK; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - | postfix_expression CTF_DOT ID_TYPE - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - $$->u.unary_expression.link = UNARY_DOTLINK; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - | postfix_expression CTF_DOT keywords - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - $$->u.unary_expression.link = UNARY_DOTLINK; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - | postfix_expression CTF_RARROW IDENTIFIER - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - $$->u.unary_expression.link = UNARY_ARROWLINK; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - | postfix_expression CTF_RARROW ID_TYPE - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = yylval.s; - $$->u.unary_expression.link = UNARY_ARROWLINK; - bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - ; - -unary_expression: - postfix_expression - { $$ = $1; } - | CTF_PLUS postfix_expression - { - $$ = $2; - if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT - && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) { - reparent_error(scanner, "expecting numeric constant"); - } - } - | CTF_MINUS postfix_expression - { - $$ = $2; - if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) { - $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT; - $$->u.unary_expression.u.signed_constant = - -($$->u.unary_expression.u.unsigned_constant); - } else if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) { - $$->u.unary_expression.u.signed_constant = - -($$->u.unary_expression.u.signed_constant); - } else { - reparent_error(scanner, "expecting numeric constant"); - } - } - ; - -unary_expression_or_range: - unary_expression CTF_DOTDOTDOT unary_expression - { - $$ = $1; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head); - $3->u.unary_expression.link = UNARY_DOTDOTDOT; - } - | unary_expression - { $$ = $1; } - ; - -/* 2.2: Declarations */ - -declaration: - declaration_specifiers CTF_SEMICOLON - { $$ = $1; } - | event_declaration - { $$ = $1; } - | stream_declaration - { $$ = $1; } - | env_declaration - { $$ = $1; } - | trace_declaration - { $$ = $1; } - | clock_declaration - { $$ = $1; } - | callsite_declaration - { $$ = $1; } - | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; - _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); - } - ; - -event_declaration: - event_declaration_begin event_declaration_end - { - $$ = make_node(scanner, NODE_EVENT); - } - | event_declaration_begin ctf_assignment_expression_list event_declaration_end - { - $$ = make_node(scanner, NODE_EVENT); - if (set_parent_node($2, $$)) - reparent_error(scanner, "event_declaration"); - } - ; - -event_declaration_begin: - CTF_EVENT CTF_LBRAC - { push_scope(scanner); } - ; - -event_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - - -stream_declaration: - stream_declaration_begin stream_declaration_end - { - $$ = make_node(scanner, NODE_STREAM); - } - | stream_declaration_begin ctf_assignment_expression_list stream_declaration_end - { - $$ = make_node(scanner, NODE_STREAM); - if (set_parent_node($2, $$)) - reparent_error(scanner, "stream_declaration"); - } - ; - -stream_declaration_begin: - CTF_STREAM CTF_LBRAC - { push_scope(scanner); } - ; - -stream_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - -env_declaration: - env_declaration_begin env_declaration_end - { - $$ = make_node(scanner, NODE_ENV); - } - | env_declaration_begin ctf_assignment_expression_list env_declaration_end - { - $$ = make_node(scanner, NODE_ENV); - if (set_parent_node($2, $$)) - reparent_error(scanner, "env declaration"); - } - ; - -env_declaration_begin: - CTF_ENV CTF_LBRAC - { push_scope(scanner); } - ; - -env_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - -trace_declaration: - trace_declaration_begin trace_declaration_end - { - $$ = make_node(scanner, NODE_TRACE); - } - | trace_declaration_begin ctf_assignment_expression_list trace_declaration_end - { - $$ = make_node(scanner, NODE_TRACE); - if (set_parent_node($2, $$)) - reparent_error(scanner, "trace_declaration"); - } - ; - -trace_declaration_begin: - CTF_TRACE CTF_LBRAC - { push_scope(scanner); } - ; - -trace_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - -clock_declaration: - CTF_CLOCK clock_declaration_begin clock_declaration_end - { - $$ = make_node(scanner, NODE_CLOCK); - } - | CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end - { - $$ = make_node(scanner, NODE_CLOCK); - if (set_parent_node($3, $$)) - reparent_error(scanner, "trace_declaration"); - } - ; - -clock_declaration_begin: - CTF_LBRAC - { push_scope(scanner); } - ; - -clock_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - -callsite_declaration: - CTF_CALLSITE callsite_declaration_begin callsite_declaration_end - { - $$ = make_node(scanner, NODE_CALLSITE); - } - | CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end - { - $$ = make_node(scanner, NODE_CALLSITE); - if (set_parent_node($3, $$)) - reparent_error(scanner, "trace_declaration"); - } - ; - -callsite_declaration_begin: - CTF_LBRAC - { push_scope(scanner); } - ; - -callsite_declaration_end: - CTF_RBRAC CTF_SEMICOLON - { pop_scope(scanner); } - ; - -integer_declaration_specifiers: - CTF_CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | integer_field_class_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | integer_declaration_specifiers CTF_CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | integer_declaration_specifiers integer_field_class_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); - } - ; - -declaration_specifiers: - CTF_CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | field_class_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | declaration_specifiers CTF_CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | declaration_specifiers field_class_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); - } - ; - -field_class_declarator_list: - field_class_declarator - { $$ = $1; } - | field_class_declarator_list CTF_COMMA field_class_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -integer_field_class_specifier: - CTF_CHAR - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_CHAR; - } - | CTF_SHORT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_SHORT; - } - | CTF_INT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INT; - } - | CTF_LONG - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_LONG; - } - | CTF_SIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_SIGNED; - } - | CTF_UNSIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED; - } - | CTF_BOOL - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_BOOL; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE; - $$->u.field_class_specifier.id_type = yylval.s; - } - | CTF_INTEGER CTF_LBRAC CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INTEGER; - $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); - } - | CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INTEGER; - $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); - if (set_parent_node($3, $$->u.field_class_specifier.node)) - reparent_error(scanner, "integer reparent error"); - } - ; - -field_class_specifier: - CTF_VOID - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_VOID; - } - | CTF_CHAR - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_CHAR; - } - | CTF_SHORT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_SHORT; - } - | CTF_INT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INT; - } - | CTF_LONG - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_LONG; - } - | CTF_FLOAT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_FLOAT; - } - | CTF_DOUBLE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_DOUBLE; - } - | CTF_SIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_SIGNED; - } - | CTF_UNSIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED; - } - | CTF_BOOL - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_BOOL; - } - | CTF_COMPLEX - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_COMPLEX; - } - | CTF_IMAGINARY - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE; - $$->u.field_class_specifier.id_type = yylval.s; - } - | CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT; - $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT); - } - | CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT; - $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT); - if (set_parent_node($3, $$->u.field_class_specifier.node)) - reparent_error(scanner, "floating point reparent error"); - } - | CTF_INTEGER CTF_LBRAC CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INTEGER; - $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); - } - | CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_INTEGER; - $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); - if (set_parent_node($3, $$->u.field_class_specifier.node)) - reparent_error(scanner, "integer reparent error"); - } - | CTF_STRING - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_STRING; - $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); - } - | CTF_STRING CTF_LBRAC CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_STRING; - $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); - } - | CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_STRING; - $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); - if (set_parent_node($3, $$->u.field_class_specifier.node)) - reparent_error(scanner, "string reparent error"); - } - | CTF_ENUM enum_field_class_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_ENUM; - $$->u.field_class_specifier.node = $2; - } - | CTF_VARIANT variant_field_class_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_VARIANT; - $$->u.field_class_specifier.node = $2; - } - | CTF_STRUCT struct_class_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.field_class_specifier.type = TYPESPEC_STRUCT; - $$->u.field_class_specifier.node = $2; - } - ; - -struct_class_specifier: - struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - if ($2 && set_parent_node($2, $$)) - reparent_error(scanner, "struct reparent error"); - } - | IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - $$->u._struct.name = $1; - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "struct reparent error"); - } - | ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - $$->u._struct.name = $1; - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "struct reparent error"); - } - | IDENTIFIER - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 0; - $$->u._struct.name = $1; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 0; - $$->u._struct.name = $1; - } - | struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align); - if ($2 && set_parent_node($2, $$)) - reparent_error(scanner, "struct reparent error"); - } - | IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - $$->u._struct.name = $1; - bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align); - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "struct reparent error"); - } - | ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN - { - $$ = make_node(scanner, NODE_STRUCT); - $$->u._struct.has_body = 1; - $$->u._struct.name = $1; - bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align); - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "struct reparent error"); - } - ; - -struct_declaration_begin: - CTF_LBRAC - { push_scope(scanner); } - ; - -struct_declaration_end: - CTF_RBRAC - { pop_scope(scanner); } - ; - -variant_field_class_specifier: - variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - if ($2 && set_parent_node($2, $$)) - reparent_error(scanner, "variant reparent error"); - } - | CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.choice = $2; - if ($5 && set_parent_node($5, $$)) - reparent_error(scanner, "variant reparent error"); - } - | CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.choice = $2; - if ($5 && set_parent_node($5, $$)) - reparent_error(scanner, "variant reparent error"); - } - | IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "variant reparent error"); - } - | IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - if ($6 && set_parent_node($6, $$)) - reparent_error(scanner, "variant reparent error"); - } - | IDENTIFIER CTF_LT IDENTIFIER CTF_GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - | IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - if ($6 && set_parent_node($6, $$)) - reparent_error(scanner, "variant reparent error"); - } - | IDENTIFIER CTF_LT ID_TYPE CTF_GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - | ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - if ($3 && set_parent_node($3, $$)) - reparent_error(scanner, "variant reparent error"); - } - | ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - if ($6 && set_parent_node($6, $$)) - reparent_error(scanner, "variant reparent error"); - } - | ID_TYPE CTF_LT IDENTIFIER CTF_GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - | ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 1; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - if ($6 && set_parent_node($6, $$)) - reparent_error(scanner, "variant reparent error"); - } - | ID_TYPE CTF_LT ID_TYPE CTF_GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - ; - -variant_declaration_begin: - CTF_LBRAC - { push_scope(scanner); } - ; - -variant_declaration_end: - CTF_RBRAC - { pop_scope(scanner); } - ; - -enum_field_class_specifier: - CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); - } - | CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - ($$)->u._enum.container_field_class = $2; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_field_class = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); - } - | ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_field_class = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); - } - | CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - ($$)->u._enum.container_field_class = $2; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_field_class = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 0; - $$->u._enum.enum_id = $1; - } - | ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); - } - | ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_field_class = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 0; - $$->u._enum.enum_id = $1; - } - ; - -struct_or_variant_declaration_list: - /* empty */ - { $$ = NULL; } - | struct_or_variant_declaration_list struct_or_variant_declaration - { - if ($1) { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); - } else { - $$ = $2; - bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); - } - } - ; - -struct_or_variant_declaration: - declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION); - ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators); - } - | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; - _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); - } - ; - -alias_declaration_specifiers: - CTF_CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | field_class_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | IDENTIFIER - { - struct ctf_node *node; - - add_type(scanner, $1); - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_ID_TYPE; - node->u.field_class_specifier.id_type = yylval.s; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | alias_declaration_specifiers CTF_CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - | alias_declaration_specifiers field_class_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); - } - | alias_declaration_specifiers IDENTIFIER - { - struct ctf_node *node; - - add_type(scanner, $2); - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.field_class_specifier.type = TYPESPEC_ID_TYPE; - node->u.field_class_specifier.id_type = yylval.s; - bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); - } - ; - -struct_or_variant_declarator_list: - struct_or_variant_declarator - { $$ = $1; } - | struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -struct_or_variant_declarator: - declarator - { $$ = $1; } - | CTF_COLON unary_expression - { $$ = $2; } - | declarator CTF_COLON unary_expression - { - $$ = $1; - if (set_parent_node($3, $1)) - reparent_error(scanner, "struct_or_variant_declarator"); - } - ; - -enumerator_list: - enumerator - { $$ = $1; } - | enumerator_list CTF_COMMA enumerator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -enumerator: - IDENTIFIER - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - } - | keywords - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - } - | CTF_STRING_LITERAL - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - } - | IDENTIFIER CTF_EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - | ID_TYPE CTF_EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - | keywords CTF_EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - | CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - ; - -abstract_declarator_list: - abstract_declarator - { $$ = $1; } - | abstract_declarator_list CTF_COMMA abstract_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -abstract_declarator: - direct_abstract_declarator - { $$ = $1; } - | pointer direct_abstract_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); - } - ; - -direct_abstract_declarator: - /* empty */ - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_ID; - /* id is NULL */ - } - | IDENTIFIER - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_ID; - $$->u.field_class_declarator.u.id = $1; - } - | CTF_LPAREN abstract_declarator CTF_RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $2; - } - | direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); - } - | direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - $$->u.field_class_declarator.u.nested.abstract_array = 1; - } - ; - -alias_abstract_declarator_list: - alias_abstract_declarator - { $$ = $1; } - | alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -alias_abstract_declarator: - direct_alias_abstract_declarator - { $$ = $1; } - | pointer direct_alias_abstract_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); - } - ; - -direct_alias_abstract_declarator: - /* empty */ - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_ID; - /* id is NULL */ - } - | CTF_LPAREN alias_abstract_declarator CTF_RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $2; - } - | direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); - } - | direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - $$->u.field_class_declarator.u.nested.abstract_array = 1; - } - ; - -declarator: - direct_declarator - { $$ = $1; } - | pointer direct_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); - } - ; - -direct_declarator: - IDENTIFIER - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_ID; - $$->u.field_class_declarator.u.id = $1; - } - | CTF_LPAREN declarator CTF_RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $2; - } - | direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); - } - ; - -field_class_declarator: - direct_field_class_declarator - { $$ = $1; } - | pointer direct_field_class_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); - } - ; - -direct_field_class_declarator: - IDENTIFIER - { - add_type(scanner, $1); - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_ID; - $$->u.field_class_declarator.u.id = $1; - } - | CTF_LPAREN field_class_declarator CTF_RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $2; - } - | direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.field_class_declarator.type = TYPEDEC_NESTED; - $$->u.field_class_declarator.u.nested.field_class_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); - } - ; - -pointer: - CTF_STAR - { - $$ = make_node(scanner, NODE_POINTER); - } - | CTF_STAR pointer - { - $$ = make_node(scanner, NODE_POINTER); - bt_list_splice(&($2)->tmp_head, &($$)->tmp_head); - } - | CTF_STAR type_qualifier_list pointer - { - $$ = make_node(scanner, NODE_POINTER); - $$->u.pointer.const_qualifier = 1; - bt_list_splice(&($3)->tmp_head, &($$)->tmp_head); - } - ; - -type_qualifier_list: - /* pointer assumes only const type qualifier */ - CTF_CONST - | type_qualifier_list CTF_CONST - ; - -/* 2.3: CTF-specific declarations */ - -ctf_assignment_expression_list: - ctf_assignment_expression CTF_SEMICOLON - { $$ = $1; } - | ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); - } - ; - -ctf_assignment_expression: - unary_expression CTF_EQUAL unary_expression - { - /* - * Because we have left and right, cannot use - * set_parent_node. - */ - $$ = make_node(scanner, NODE_CTF_EXPRESSION); - _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left); - if ($1->u.unary_expression.type != UNARY_STRING) - reparent_error(scanner, "ctf_assignment_expression left expects string"); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right); - } - | unary_expression CTF_TYPEASSIGN declaration_specifiers /* Only allow struct */ - { - /* - * Because we have left and right, cannot use - * set_parent_node. - */ - $$ = make_node(scanner, NODE_CTF_EXPRESSION); - _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left); - if ($1->u.unary_expression.type != UNARY_STRING) - reparent_error(scanner, "ctf_assignment_expression left expects string"); - bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right); - } - | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEDEF declaration_specifiers field_class_declarator_list - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_def.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | declaration_specifiers CTF_TYPEDEF field_class_declarator_list - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); - } - | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; - _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; - _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); - } - ; diff --git a/plugins/ctf/common/metadata/scanner-symbols.h b/plugins/ctf/common/metadata/scanner-symbols.h deleted file mode 100644 index 9b9e3631..00000000 --- a/plugins/ctf/common/metadata/scanner-symbols.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _CTF_SCANNER_SYMBOLS -#define _CTF_SCANNER_SYMBOLS - -/* - * ctf-scanner-symbols.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define yy_create_buffer bt_yy_create_buffer -#define yy_delete_buffer bt_yy_delete_buffer -#define yy_flush_buffer bt_yy_flush_buffer -#define yy_scan_buffer bt_yy_scan_buffer -#define yy_scan_bytes bt_yy_scan_bytes -#define yy_scan_string bt_yy_scan_string -#define yy_switch_to_buffer bt_yy_switch_to_buffer -#define yyalloc bt_yyalloc -#define yyfree bt_yyfree -#define yyget_column bt_yyget_column -#define yyget_debug bt_yyget_debug -#define yyget_extra bt_yyget_extra -#define yyget_in bt_yyget_in -#define yyget_leng bt_yyget_leng -#define yyget_lineno bt_yyget_lineno -#define yyget_lval bt_yyget_lval -#define yyget_out bt_yyget_out -#define yyget_text bt_yyget_text -#define yylex_init bt_yylex_init -#define yypop_buffer_state bt_yypop_buffer_state -#define yypush_buffer_state bt_yypush_buffer_state -#define yyrealloc bt_yyrealloc -#define yyset_column bt_yyset_column -#define yyset_debug bt_yyset_debug -#define yyset_extra bt_yyset_extra -#define yyset_in bt_yyset_in -#define yyset_lineno bt_yyset_lineno -#define yyset_lval bt_yyset_lval -#define yyset_out bt_yyset_out - -#endif /* _CTF_SCANNER_SYMBOLS */ diff --git a/plugins/ctf/common/metadata/scanner.h b/plugins/ctf/common/metadata/scanner.h deleted file mode 100644 index 34d6c462..00000000 --- a/plugins/ctf/common/metadata/scanner.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _CTF_SCANNER_H -#define _CTF_SCANNER_H - -/* - * ctf-scanner.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include "ast.h" - -#ifndef YY_TYPEDEF_YY_SCANNER_T -#define YY_TYPEDEF_YY_SCANNER_T -typedef void* yyscan_t; -#endif - -struct ctf_scanner_scope; -struct ctf_scanner_scope { - struct ctf_scanner_scope *parent; - GHashTable *classes; -}; - -struct ctf_scanner { - yyscan_t scanner; - struct ctf_ast *ast; - struct ctf_scanner_scope root_scope; - struct ctf_scanner_scope *cs; - struct objstack *objstack; -}; - -struct ctf_scanner *ctf_scanner_alloc(void); -void ctf_scanner_free(struct ctf_scanner *scanner); -int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input); - -static inline -struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner) -{ - return scanner->ast; -} - -BT_HIDDEN -int is_type(struct ctf_scanner *scanner, const char *id); - -#endif /* _CTF_SCANNER_H */ diff --git a/plugins/ctf/common/metadata/visitor-generate-ir.c b/plugins/ctf/common/metadata/visitor-generate-ir.c deleted file mode 100644 index 90074233..00000000 --- a/plugins/ctf/common/metadata/visitor-generate-ir.c +++ /dev/null @@ -1,5090 +0,0 @@ -/* - * ctf-visitor-generate-ir.c - * - * Common Trace Format metadata visitor (generates CTF IR objects). - * - * Based on older ctf-visitor-generate-io-struct.c. - * - * Copyright 2010 - Mathieu Desnoyers - * Copyright 2015-2018 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-IR-VISITOR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scanner.h" -#include "parser.h" -#include "ast.h" -#include "decoder.h" -#include "ctf-meta.h" -#include "ctf-meta-visitors.h" - -/* Bit value (left shift) */ -#define _BV(_val) (1 << (_val)) - -/* Bit is set in a set of bits */ -#define _IS_SET(_set, _mask) (*(_set) & (_mask)) - -/* Set bit in a set of bits */ -#define _SET(_set, _mask) (*(_set) |= (_mask)) - -/* Try to push scope, or go to the `error` label */ -#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR() \ - do { \ - ret = ctx_push_scope(ctx); \ - if (ret) { \ - BT_LOGE_STR("Cannot push scope."); \ - goto error; \ - } \ - } while (0) - -/* Bits for verifying existing attributes in various declarations */ -enum { - _CLOCK_NAME_SET = _BV(0), - _CLOCK_UUID_SET = _BV(1), - _CLOCK_FREQ_SET = _BV(2), - _CLOCK_PRECISION_SET = _BV(3), - _CLOCK_OFFSET_S_SET = _BV(4), - _CLOCK_OFFSET_SET = _BV(5), - _CLOCK_ABSOLUTE_SET = _BV(6), - _CLOCK_DESCRIPTION_SET = _BV(7), -}; - -enum { - _INTEGER_ALIGN_SET = _BV(0), - _INTEGER_SIZE_SET = _BV(1), - _INTEGER_BASE_SET = _BV(2), - _INTEGER_ENCODING_SET = _BV(3), - _INTEGER_BYTE_ORDER_SET = _BV(4), - _INTEGER_SIGNED_SET = _BV(5), - _INTEGER_MAP_SET = _BV(6), -}; - -enum { - _FLOAT_ALIGN_SET = _BV(0), - _FLOAT_MANT_DIG_SET = _BV(1), - _FLOAT_EXP_DIG_SET = _BV(2), - _FLOAT_BYTE_ORDER_SET = _BV(3), -}; - -enum { - _STRING_ENCODING_SET = _BV(0), -}; - -enum { - _TRACE_MINOR_SET = _BV(0), - _TRACE_MAJOR_SET = _BV(1), - _TRACE_BYTE_ORDER_SET = _BV(2), - _TRACE_UUID_SET = _BV(3), - _TRACE_PACKET_HEADER_SET = _BV(4), -}; - -enum { - _STREAM_ID_SET = _BV(0), - _STREAM_PACKET_CONTEXT_SET = _BV(1), - _STREAM_EVENT_HEADER_SET = _BV(2), - _STREAM_EVENT_CONTEXT_SET = _BV(3), -}; - -enum { - _EVENT_NAME_SET = _BV(0), - _EVENT_ID_SET = _BV(1), - _EVENT_MODEL_EMF_URI_SET = _BV(2), - _EVENT_STREAM_ID_SET = _BV(3), - _EVENT_LOG_LEVEL_SET = _BV(4), - _EVENT_CONTEXT_SET = _BV(5), - _EVENT_FIELDS_SET = _BV(6), -}; - -enum loglevel { - LOG_LEVEL_EMERG = 0, - LOG_LEVEL_ALERT = 1, - LOG_LEVEL_CRIT = 2, - LOG_LEVEL_ERR = 3, - LOG_LEVEL_WARNING = 4, - LOG_LEVEL_NOTICE = 5, - LOG_LEVEL_INFO = 6, - LOG_LEVEL_DEBUG_SYSTEM = 7, - LOG_LEVEL_DEBUG_PROGRAM = 8, - LOG_LEVEL_DEBUG_PROCESS = 9, - LOG_LEVEL_DEBUG_MODULE = 10, - LOG_LEVEL_DEBUG_UNIT = 11, - LOG_LEVEL_DEBUG_FUNCTION = 12, - LOG_LEVEL_DEBUG_LINE = 13, - LOG_LEVEL_DEBUG = 14, - _NR_LOGLEVELS = 15, -}; - -/* Prefixes of class aliases */ -#define _PREFIX_ALIAS 'a' -#define _PREFIX_ENUM 'e' -#define _PREFIX_STRUCT 's' -#define _PREFIX_VARIANT 'v' - -/* First entry in a BT list */ -#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member) \ - bt_list_entry((_ptr)->next, _class, _member) - -#define _BT_LOGE_DUP_ATTR(_node, _attr, _entity) \ - _BT_LOGE_LINENO((_node)->lineno, \ - "Duplicate attribute in %s: attr-name=\"%s\"", \ - _entity, _attr) - -#define _BT_LOGE_NODE(_node, _msg, args...) \ - _BT_LOGE_LINENO((_node)->lineno, _msg, ## args) - -#define _BT_LOGW_NODE(_node, _msg, args...) \ - _BT_LOGW_LINENO((_node)->lineno, _msg, ## args) - -#define _BT_LOGV_NODE(_node, _msg, args...) \ - _BT_LOGV_LINENO((_node)->lineno, _msg, ## args) - -/* - * Declaration scope of a visitor context. This represents a TSDL - * lexical scope, so that aliases and named structures, variants, - * and enumerations may be registered and looked up hierarchically. - */ -struct ctx_decl_scope { - /* - * Alias name to field class. - * - * GQuark -> struct ctf_field_class * (owned by this) - */ - GHashTable *decl_map; - - /* Parent scope; NULL if this is the root declaration scope */ - struct ctx_decl_scope *parent_scope; -}; - -/* - * Visitor context (private). - */ -struct ctx { - bt_self_component_source *self_comp; - /* Trace IR trace class being filled (owned by this) */ - bt_trace_class *trace_class; - - /* CTF meta trace being filled (owned by this) */ - struct ctf_trace_class *ctf_tc; - - /* Current declaration scope (top of the stack) (owned by this) */ - struct ctx_decl_scope *current_scope; - - /* True if trace declaration is visited */ - bool is_trace_visited; - - /* True if this is an LTTng trace */ - bool is_lttng; - - /* Config passed by the user */ - struct ctf_metadata_decoder_config decoder_config; -}; - -/* - * Visitor (public). - */ -struct ctf_visitor_generate_ir; - -/** - * Creates a new declaration scope. - * - * @param par_scope Parent scope (NULL if creating a root scope) - * @returns New declaration scope, or NULL on error - */ -static -struct ctx_decl_scope *ctx_decl_scope_create(struct ctx_decl_scope *par_scope) -{ - struct ctx_decl_scope *scope; - - scope = g_new(struct ctx_decl_scope, 1); - if (!scope) { - BT_LOGE_STR("Failed to allocate one declaration scope."); - goto end; - } - - scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) ctf_field_class_destroy); - scope->parent_scope = par_scope; - -end: - return scope; -} - -/** - * Destroys a declaration scope. - * - * This function does not destroy the parent scope. - * - * @param scope Scope to destroy - */ -static -void ctx_decl_scope_destroy(struct ctx_decl_scope *scope) -{ - if (!scope) { - goto end; - } - - g_hash_table_destroy(scope->decl_map); - g_free(scope); - -end: - return; -} - -/** - * Returns the GQuark of a prefixed alias. - * - * @param prefix Prefix character - * @param name Name - * @returns Associated GQuark, or 0 on error - */ -static -GQuark get_prefixed_named_quark(char prefix, const char *name) -{ - GQuark qname = 0; - - BT_ASSERT(name); - - /* Prefix character + original string + '\0' */ - char *prname = g_new(char, strlen(name) + 2); - if (!prname) { - BT_LOGE_STR("Failed to allocate a string."); - goto end; - } - - sprintf(prname, "%c%s", prefix, name); - qname = g_quark_from_string(prname); - g_free(prname); - -end: - return qname; -} - -/** - * Looks up a prefixed class alias within a declaration scope. - * - * @param scope Declaration scope - * @param prefix Prefix character - * @param name Alias name - * @param levels Number of levels to dig into (-1 means infinite) - * @param copy True to return a copy - * @returns Declaration (owned by caller if \p copy is true), - * or NULL if not found - */ -static -struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias( - struct ctx_decl_scope *scope, char prefix, const char *name, - int levels, bool copy) -{ - GQuark qname = 0; - int cur_levels = 0; - struct ctf_field_class *decl = NULL; - struct ctx_decl_scope *cur_scope = scope; - - BT_ASSERT(scope); - BT_ASSERT(name); - qname = get_prefixed_named_quark(prefix, name); - if (!qname) { - goto end; - } - - if (levels < 0) { - levels = INT_MAX; - } - - while (cur_scope && cur_levels < levels) { - decl = g_hash_table_lookup(cur_scope->decl_map, - (gconstpointer) GUINT_TO_POINTER(qname)); - if (decl) { - /* Caller's reference */ - if (copy) { - decl = ctf_field_class_copy(decl); - BT_ASSERT(decl); - } - - goto end; - } - - cur_scope = cur_scope->parent_scope; - cur_levels++; - } - -end: - return decl; -} - -/** - * Looks up a class alias within a declaration scope. - * - * @param scope Declaration scope - * @param name Alias name - * @param levels Number of levels to dig into (-1 means infinite) - * @param copy True to return a copy - * @returns Declaration (owned by caller if \p copy is true), - * or NULL if not found - */ -static -struct ctf_field_class *ctx_decl_scope_lookup_alias( - struct ctx_decl_scope *scope, const char *name, int levels, - bool copy) -{ - return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS, - name, levels, copy); -} - -/** - * Looks up an enumeration within a declaration scope. - * - * @param scope Declaration scope - * @param name Enumeration name - * @param levels Number of levels to dig into (-1 means infinite) - * @param copy True to return a copy - * @returns Declaration (owned by caller if \p copy is true), - * or NULL if not found - */ -static -struct ctf_field_class_enum *ctx_decl_scope_lookup_enum( - struct ctx_decl_scope *scope, const char *name, int levels, - bool copy) -{ - return (void *) ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM, - name, levels, copy); -} - -/** - * Looks up a structure within a declaration scope. - * - * @param scope Declaration scope - * @param name Structure name - * @param levels Number of levels to dig into (-1 means infinite) - * @param copy True to return a copy - * @returns Declaration (owned by caller if \p copy is true), - * or NULL if not found - */ -static -struct ctf_field_class_struct *ctx_decl_scope_lookup_struct( - struct ctx_decl_scope *scope, const char *name, int levels, - bool copy) -{ - return (void *) ctx_decl_scope_lookup_prefix_alias(scope, - _PREFIX_STRUCT, name, levels, copy); -} - -/** - * Looks up a variant within a declaration scope. - * - * @param scope Declaration scope - * @param name Variant name - * @param levels Number of levels to dig into (-1 means infinite) - * @param copy True to return a copy - * @returns Declaration (owned by caller if \p copy is true), - * or NULL if not found - */ -static -struct ctf_field_class_variant *ctx_decl_scope_lookup_variant( - struct ctx_decl_scope *scope, const char *name, int levels, - bool copy) -{ - return (void *) ctx_decl_scope_lookup_prefix_alias(scope, - _PREFIX_VARIANT, name, levels, copy); -} - -/** - * Registers a prefixed class alias within a declaration scope. - * - * @param scope Declaration scope - * @param prefix Prefix character - * @param name Alias name (non-NULL) - * @param decl Field class to register (copied) - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope, - char prefix, const char *name, struct ctf_field_class *decl) -{ - int ret = 0; - GQuark qname = 0; - - BT_ASSERT(scope); - BT_ASSERT(name); - BT_ASSERT(decl); - qname = get_prefixed_named_quark(prefix, name); - if (!qname) { - ret = -ENOMEM; - goto end; - } - - /* Make sure alias does not exist in local scope */ - if (ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1, - false)) { - ret = -EEXIST; - goto end; - } - - decl = ctf_field_class_copy(decl); - BT_ASSERT(decl); - g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl); - -end: - return ret; -} - -/** - * Registers a class alias within a declaration scope. - * - * @param scope Declaration scope - * @param name Alias name (non-NULL) - * @param decl Field class to register (copied) - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope, - const char *name, struct ctf_field_class *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ALIAS, - name, (void *) decl); -} - -/** - * Registers an enumeration declaration within a declaration scope. - * - * @param scope Declaration scope - * @param name Enumeration name (non-NULL) - * @param decl Enumeration field class to register (copied) - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope, - const char *name, struct ctf_field_class_enum *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM, - name, (void *) decl); -} - -/** - * Registers a structure declaration within a declaration scope. - * - * @param scope Declaration scope - * @param name Structure name (non-NULL) - * @param decl Structure field class to register (copied) - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope, - const char *name, struct ctf_field_class_struct *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT, - name, (void *) decl); -} - -/** - * Registers a variant declaration within a declaration scope. - * - * @param scope Declaration scope - * @param name Variant name (non-NULL) - * @param decl Variant field class to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope, - const char *name, struct ctf_field_class_variant *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT, - name, (void *) decl); -} - -/** - * Destroys a visitor context. - * - * @param ctx Visitor context to destroy - */ -static -void ctx_destroy(struct ctx *ctx) -{ - struct ctx_decl_scope *scope; - - if (!ctx) { - goto end; - } - - scope = ctx->current_scope; - - /* - * Destroy all scopes, from current one to the root scope. - */ - while (scope) { - struct ctx_decl_scope *parent_scope = scope->parent_scope; - - ctx_decl_scope_destroy(scope); - scope = parent_scope; - } - - bt_trace_class_put_ref(ctx->trace_class); - - if (ctx->ctf_tc) { - ctf_trace_class_destroy(ctx->ctf_tc); - } - - g_free(ctx); - -end: - return; -} - -/** - * Creates a new visitor context. - * - * @param trace Associated trace - * @returns New visitor context, or NULL on error - */ -static -struct ctx *ctx_create(bt_self_component_source *self_comp, - const struct ctf_metadata_decoder_config *decoder_config) -{ - struct ctx *ctx = NULL; - - BT_ASSERT(decoder_config); - - ctx = g_new0(struct ctx, 1); - if (!ctx) { - BT_LOGE_STR("Failed to allocate one visitor context."); - goto error; - } - - if (self_comp) { - ctx->trace_class = bt_trace_class_create( - bt_self_component_source_as_self_component(self_comp)); - if (!ctx->trace_class) { - BT_LOGE_STR("Cannot create empty trace class."); - goto error; - } - ctx->self_comp = self_comp; - } - - ctx->ctf_tc = ctf_trace_class_create(); - if (!ctx->ctf_tc) { - BT_LOGE_STR("Cannot create CTF trace class."); - goto error; - } - - /* Root declaration scope */ - ctx->current_scope = ctx_decl_scope_create(NULL); - if (!ctx->current_scope) { - BT_LOGE_STR("Cannot create declaration scope."); - goto error; - } - - ctx->decoder_config = *decoder_config; - goto end; - -error: - ctx_destroy(ctx); - ctx = NULL; - -end: - return ctx; -} - -/** - * Pushes a new declaration scope on top of a visitor context's - * declaration scope stack. - * - * @param ctx Visitor context - * @returns 0 on success, or a negative value on error - */ -static -int ctx_push_scope(struct ctx *ctx) -{ - int ret = 0; - struct ctx_decl_scope *new_scope; - - BT_ASSERT(ctx); - new_scope = ctx_decl_scope_create(ctx->current_scope); - if (!new_scope) { - BT_LOGE_STR("Cannot create declaration scope."); - ret = -ENOMEM; - goto end; - } - - ctx->current_scope = new_scope; - -end: - return ret; -} - -static -void ctx_pop_scope(struct ctx *ctx) -{ - struct ctx_decl_scope *parent_scope = NULL; - - BT_ASSERT(ctx); - - if (!ctx->current_scope) { - goto end; - } - - parent_scope = ctx->current_scope->parent_scope; - ctx_decl_scope_destroy(ctx->current_scope); - ctx->current_scope = parent_scope; - -end: - return; -} - -static -int visit_field_class_specifier_list(struct ctx *ctx, struct ctf_node *ts_list, - struct ctf_field_class **decl); - -static -char *remove_underscores_from_field_ref(const char *field_ref) -{ - const char *in_ch; - char *out_ch; - char *ret; - enum { - UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE, - UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE, - } state = UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE; - - BT_ASSERT(field_ref); - ret = calloc(strlen(field_ref) + 1, 1); - if (!ret) { - BT_LOGE("Failed to allocate a string: size=%zu", - strlen(field_ref) + 1); - goto end; - } - - in_ch = field_ref; - out_ch = ret; - - while (*in_ch != '\0') { - switch (*in_ch) { - case ' ': - case '\t': - /* Remove whitespace */ - in_ch++; - continue; - case '_': - if (state == UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE) { - in_ch++; - state = UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE; - continue; - } - - goto copy; - case '.': - state = UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE; - goto copy; - default: - state = UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE; - goto copy; - } - -copy: - *out_ch = *in_ch; - in_ch++; - out_ch++; - } - -end: - return ret; -} - -static -int is_unary_string(struct bt_list_head *head) -{ - int ret = TRUE; - struct ctf_node *node; - - bt_list_for_each_entry(node, head, siblings) { - if (node->type != NODE_UNARY_EXPRESSION) { - ret = FALSE; - } - - if (node->u.unary_expression.type != UNARY_STRING) { - ret = FALSE; - } - } - - return ret; -} - -static -char *concatenate_unary_strings(struct bt_list_head *head) -{ - int i = 0; - GString *str; - struct ctf_node *node; - - str = g_string_new(NULL); - BT_ASSERT(str); - - bt_list_for_each_entry(node, head, siblings) { - char *src_string; - - if ( - node->type != NODE_UNARY_EXPRESSION || - node->u.unary_expression.type != UNARY_STRING || - !( - ( - node->u.unary_expression.link != - UNARY_LINK_UNKNOWN - ) ^ (i == 0) - ) - ) { - goto error; - } - - switch (node->u.unary_expression.link) { - case UNARY_DOTLINK: - g_string_append(str, "."); - break; - case UNARY_ARROWLINK: - g_string_append(str, "->"); - break; - case UNARY_DOTDOTDOT: - g_string_append(str, "..."); - break; - default: - break; - } - - src_string = node->u.unary_expression.u.string; - g_string_append(str, src_string); - i++; - } - - /* Destroys the container, returns the underlying string */ - return g_string_free(str, FALSE); - -error: - /* This always returns NULL */ - return g_string_free(str, TRUE); -} - -static -const char *get_map_clock_name_value(struct bt_list_head *head) -{ - int i = 0; - struct ctf_node *node; - const char *name = NULL; - - bt_list_for_each_entry(node, head, siblings) { - char *src_string; - int uexpr_type = node->u.unary_expression.type; - int uexpr_link = node->u.unary_expression.link; - int cond = node->type != NODE_UNARY_EXPRESSION || - uexpr_type != UNARY_STRING || - !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0)); - if (cond) { - goto error; - } - - /* Needs to be chained with . */ - switch (node->u.unary_expression.link) { - case UNARY_DOTLINK: - break; - case UNARY_ARROWLINK: - case UNARY_DOTDOTDOT: - goto error; - default: - break; - } - - src_string = node->u.unary_expression.u.string; - - switch (i) { - case 0: - if (strcmp("clock", src_string)) { - goto error; - } - break; - case 1: - name = src_string; - break; - case 2: - if (strcmp("value", src_string)) { - goto error; - } - break; - default: - /* Extra identifier, unknown */ - goto error; - } - - i++; - } - - return name; - -error: - return NULL; -} - -static -int is_unary_unsigned(struct bt_list_head *head) -{ - int ret = TRUE; - struct ctf_node *node; - - bt_list_for_each_entry(node, head, siblings) { - if (node->type != NODE_UNARY_EXPRESSION) { - ret = FALSE; - } - - if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) { - ret = FALSE; - } - } - - return ret; -} - -static -int get_unary_unsigned(struct bt_list_head *head, uint64_t *value) -{ - int i = 0; - int ret = 0; - struct ctf_node *node; - - *value = 0; - - if (bt_list_empty(head)) { - ret = -1; - goto end; - } - - bt_list_for_each_entry(node, head, siblings) { - int uexpr_type = node->u.unary_expression.type; - int uexpr_link = node->u.unary_expression.link; - int cond = node->type != NODE_UNARY_EXPRESSION || - uexpr_type != UNARY_UNSIGNED_CONSTANT || - uexpr_link != UNARY_LINK_UNKNOWN || i != 0; - if (cond) { - _BT_LOGE_NODE(node, "Invalid constant unsigned integer."); - ret = -EINVAL; - goto end; - } - - *value = node->u.unary_expression.u.unsigned_constant; - i++; - } - -end: - return ret; -} - -static -int is_unary_signed(struct bt_list_head *head) -{ - int ret = TRUE; - struct ctf_node *node; - - bt_list_for_each_entry(node, head, siblings) { - if (node->type != NODE_UNARY_EXPRESSION) { - ret = FALSE; - } - - if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) { - ret = FALSE; - } - } - - return ret; -} - -static -int get_unary_signed(struct bt_list_head *head, int64_t *value) -{ - int i = 0; - int ret = 0; - struct ctf_node *node; - - bt_list_for_each_entry(node, head, siblings) { - int uexpr_type = node->u.unary_expression.type; - int uexpr_link = node->u.unary_expression.link; - int cond = node->type != NODE_UNARY_EXPRESSION || - (uexpr_type != UNARY_UNSIGNED_CONSTANT && - uexpr_type != UNARY_SIGNED_CONSTANT) || - uexpr_link != UNARY_LINK_UNKNOWN || i != 0; - if (cond) { - ret = -EINVAL; - goto end; - } - - switch (uexpr_type) { - case UNARY_UNSIGNED_CONSTANT: - *value = (int64_t) - node->u.unary_expression.u.unsigned_constant; - break; - case UNARY_SIGNED_CONSTANT: - *value = node->u.unary_expression.u.signed_constant; - break; - default: - ret = -EINVAL; - goto end; - } - - i++; - } - -end: - return ret; -} - -static -int get_unary_uuid(struct bt_list_head *head, unsigned char *uuid) -{ - int i = 0; - int ret = 0; - struct ctf_node *node; - - bt_list_for_each_entry(node, head, siblings) { - int uexpr_type = node->u.unary_expression.type; - int uexpr_link = node->u.unary_expression.link; - const char *src_string; - - if (node->type != NODE_UNARY_EXPRESSION || - uexpr_type != UNARY_STRING || - uexpr_link != UNARY_LINK_UNKNOWN || - i != 0) { - ret = -EINVAL; - goto end; - } - - src_string = node->u.unary_expression.u.string; - ret = bt_uuid_parse(src_string, uuid); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot parse UUID: uuid=\"%s\"", src_string); - goto end; - } - } - -end: - return ret; -} - -static -int get_boolean(struct ctf_node *unary_expr) -{ - int ret = 0; - - if (unary_expr->type != NODE_UNARY_EXPRESSION) { - _BT_LOGE_NODE(unary_expr, - "Expecting unary expression: node-type=%d", - unary_expr->type); - ret = -EINVAL; - goto end; - } - - switch (unary_expr->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0); - break; - case UNARY_SIGNED_CONSTANT: - ret = (unary_expr->u.unary_expression.u.signed_constant != 0); - break; - case UNARY_STRING: - { - const char *str = unary_expr->u.unary_expression.u.string; - - if (!strcmp(str, "true") || !strcmp(str, "TRUE")) { - ret = TRUE; - } else if (!strcmp(str, "false") || !strcmp(str, "FALSE")) { - ret = FALSE; - } else { - _BT_LOGE_NODE(unary_expr, - "Unexpected boolean value: value=\"%s\"", str); - ret = -EINVAL; - goto end; - } - break; - } - default: - _BT_LOGE_NODE(unary_expr, - "Unexpected unary expression type: node-type=%d", - unary_expr->u.unary_expression.type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -enum ctf_byte_order byte_order_from_unary_expr(struct ctf_node *unary_expr) -{ - const char *str; - enum ctf_byte_order bo = -1; - - if (unary_expr->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(unary_expr, - "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`."); - goto end; - } - - str = unary_expr->u.unary_expression.u.string; - - if (!strcmp(str, "be") || !strcmp(str, "network")) { - bo = CTF_BYTE_ORDER_BIG; - } else if (!strcmp(str, "le")) { - bo = CTF_BYTE_ORDER_LITTLE; - } else if (!strcmp(str, "native")) { - bo = CTF_BYTE_ORDER_DEFAULT; - } else { - _BT_LOGE_NODE(unary_expr, - "Unexpected \"byte_order\" attribute value: " - "expecting `be`, `le`, `network`, or `native`: value=\"%s\"", - str); - goto end; - } - -end: - return bo; -} - -static -enum ctf_byte_order get_real_byte_order(struct ctx *ctx, - struct ctf_node *uexpr) -{ - enum ctf_byte_order bo = byte_order_from_unary_expr(uexpr); - - if (bo == CTF_BYTE_ORDER_DEFAULT) { - bo = ctx->ctf_tc->default_byte_order; - } - - return bo; -} - -static -int is_align_valid(uint64_t align) -{ - return (align != 0) && !(align & (align - UINT64_C(1))); -} - -static -int get_class_specifier_name(struct ctx *ctx, struct ctf_node *cls_specifier, - GString *str) -{ - int ret = 0; - - if (cls_specifier->type != NODE_TYPE_SPECIFIER) { - _BT_LOGE_NODE(cls_specifier, - "Unexpected node type: node-type=%d", - cls_specifier->type); - ret = -EINVAL; - goto end; - } - - switch (cls_specifier->u.field_class_specifier.type) { - case TYPESPEC_VOID: - g_string_append(str, "void"); - break; - case TYPESPEC_CHAR: - g_string_append(str, "char"); - break; - case TYPESPEC_SHORT: - g_string_append(str, "short"); - break; - case TYPESPEC_INT: - g_string_append(str, "int"); - break; - case TYPESPEC_LONG: - g_string_append(str, "long"); - break; - case TYPESPEC_FLOAT: - g_string_append(str, "float"); - break; - case TYPESPEC_DOUBLE: - g_string_append(str, "double"); - break; - case TYPESPEC_SIGNED: - g_string_append(str, "signed"); - break; - case TYPESPEC_UNSIGNED: - g_string_append(str, "unsigned"); - break; - case TYPESPEC_BOOL: - g_string_append(str, "bool"); - break; - case TYPESPEC_COMPLEX: - g_string_append(str, "_Complex"); - break; - case TYPESPEC_IMAGINARY: - g_string_append(str, "_Imaginary"); - break; - case TYPESPEC_CONST: - g_string_append(str, "const"); - break; - case TYPESPEC_ID_TYPE: - if (cls_specifier->u.field_class_specifier.id_type) { - g_string_append(str, - cls_specifier->u.field_class_specifier.id_type); - } - break; - case TYPESPEC_STRUCT: - { - struct ctf_node *node = cls_specifier->u.field_class_specifier.node; - - if (!node->u._struct.name) { - _BT_LOGE_NODE(node, "Unexpected empty structure field class name."); - ret = -EINVAL; - goto end; - } - - g_string_append(str, "struct "); - g_string_append(str, node->u._struct.name); - break; - } - case TYPESPEC_VARIANT: - { - struct ctf_node *node = cls_specifier->u.field_class_specifier.node; - - if (!node->u.variant.name) { - _BT_LOGE_NODE(node, "Unexpected empty variant field class name."); - ret = -EINVAL; - goto end; - } - - g_string_append(str, "variant "); - g_string_append(str, node->u.variant.name); - break; - } - case TYPESPEC_ENUM: - { - struct ctf_node *node = cls_specifier->u.field_class_specifier.node; - - if (!node->u._enum.enum_id) { - _BT_LOGE_NODE(node, - "Unexpected empty enumeration field class (`enum`) name."); - ret = -EINVAL; - goto end; - } - - g_string_append(str, "enum "); - g_string_append(str, node->u._enum.enum_id); - break; - } - case TYPESPEC_FLOATING_POINT: - case TYPESPEC_INTEGER: - case TYPESPEC_STRING: - default: - _BT_LOGE_NODE(cls_specifier->u.field_class_specifier.node, - "Unexpected field class specifier type: %d", - cls_specifier->u.field_class_specifier.type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int get_class_specifier_list_name(struct ctx *ctx, - struct ctf_node *cls_specifier_list, GString *str) -{ - int ret = 0; - struct ctf_node *iter; - int alias_item_nr = 0; - struct bt_list_head *head = - &cls_specifier_list->u.field_class_specifier_list.head; - - bt_list_for_each_entry(iter, head, siblings) { - if (alias_item_nr != 0) { - g_string_append(str, " "); - } - - alias_item_nr++; - ret = get_class_specifier_name(ctx, iter, str); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -GQuark create_class_alias_identifier(struct ctx *ctx, - struct ctf_node *cls_specifier_list, - struct ctf_node *node_field_class_declarator) -{ - int ret; - char *str_c; - GString *str; - GQuark qalias = 0; - struct ctf_node *iter; - struct bt_list_head *pointers = - &node_field_class_declarator->u.field_class_declarator.pointers; - - str = g_string_new(""); - ret = get_class_specifier_list_name(ctx, cls_specifier_list, str); - if (ret) { - g_string_free(str, TRUE); - goto end; - } - - bt_list_for_each_entry(iter, pointers, siblings) { - g_string_append(str, " *"); - - if (iter->u.pointer.const_qualifier) { - g_string_append(str, " const"); - } - } - - str_c = g_string_free(str, FALSE); - qalias = g_quark_from_string(str_c); - g_free(str_c); - -end: - return qalias; -} - -static -int visit_field_class_declarator(struct ctx *ctx, - struct ctf_node *cls_specifier_list, - GQuark *field_name, struct ctf_node *node_field_class_declarator, - struct ctf_field_class **field_decl, - struct ctf_field_class *nested_decl) -{ - /* - * During this whole function, nested_decl is always OURS, - * whereas field_decl is an output which we create, but - * belongs to the caller (it is moved). - */ - int ret = 0; - *field_decl = NULL; - - /* Validate field class declarator node */ - if (node_field_class_declarator) { - if (node_field_class_declarator->u.field_class_declarator.type == - TYPEDEC_UNKNOWN) { - _BT_LOGE_NODE(node_field_class_declarator, - "Unexpected field class declarator type: type=%d", - node_field_class_declarator->u.field_class_declarator.type); - ret = -EINVAL; - goto error; - } - - /* TODO: GCC bitfields not supported yet */ - if (node_field_class_declarator->u.field_class_declarator.bitfield_len != - NULL) { - _BT_LOGE_NODE(node_field_class_declarator, - "GCC bitfields are not supported as of this version."); - ret = -EPERM; - goto error; - } - } - - /* Find the right nested declaration if not provided */ - if (!nested_decl) { - struct bt_list_head *pointers = - &node_field_class_declarator->u.field_class_declarator.pointers; - - if (node_field_class_declarator && !bt_list_empty(pointers)) { - GQuark qalias; - - /* - * If we have a pointer declarator, it HAS to - * be present in the field class aliases (else - * fail). - */ - qalias = create_class_alias_identifier(ctx, - cls_specifier_list, node_field_class_declarator); - nested_decl = - ctx_decl_scope_lookup_alias(ctx->current_scope, - g_quark_to_string(qalias), -1, true); - if (!nested_decl) { - _BT_LOGE_NODE(node_field_class_declarator, - "Cannot find class alias: name=\"%s\"", - g_quark_to_string(qalias)); - ret = -EINVAL; - goto error; - } - - if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) { - /* Pointer: force integer's base to 16 */ - struct ctf_field_class_int *int_fc = - (void *) nested_decl; - - int_fc->disp_base = - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; - } - } else { - ret = visit_field_class_specifier_list(ctx, - cls_specifier_list, &nested_decl); - if (ret) { - BT_ASSERT(!nested_decl); - goto error; - } - } - } - - BT_ASSERT(nested_decl); - - if (!node_field_class_declarator) { - *field_decl = nested_decl; - nested_decl = NULL; - goto end; - } - - if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) { - if (node_field_class_declarator->u.field_class_declarator.u.id) { - const char *id = - node_field_class_declarator->u.field_class_declarator.u.id; - - if (id[0] == '_') { - id++; - } - - *field_name = g_quark_from_string(id); - } else { - *field_name = 0; - } - - *field_decl = nested_decl; - nested_decl = NULL; - goto end; - } else { - struct ctf_node *first; - struct ctf_field_class *decl = NULL; - struct ctf_field_class *outer_field_decl = NULL; - struct bt_list_head *length = - &node_field_class_declarator-> - u.field_class_declarator.u.nested.length; - - /* Create array/sequence, pass nested_decl as child */ - if (bt_list_empty(length)) { - _BT_LOGE_NODE(node_field_class_declarator, - "Expecting length field reference or value."); - ret = -EINVAL; - goto error; - } - - first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings); - if (first->type != NODE_UNARY_EXPRESSION) { - _BT_LOGE_NODE(first, - "Unexpected node type: node-type=%d", - first->type); - ret = -EINVAL; - goto error; - } - - switch (first->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - { - struct ctf_field_class_array *array_decl = NULL; - - array_decl = ctf_field_class_array_create(); - BT_ASSERT(array_decl); - array_decl->length = - first->u.unary_expression.u.unsigned_constant; - array_decl->base.elem_fc = nested_decl; - nested_decl = NULL; - decl = (void *) array_decl; - break; - } - case UNARY_STRING: - { - /* Lookup unsigned integer definition, create seq. */ - struct ctf_field_class_sequence *seq_decl = NULL; - char *length_name = concatenate_unary_strings(length); - - if (!length_name) { - _BT_LOGE_NODE(node_field_class_declarator, - "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (strncmp(length_name, "env.", 4) == 0) { - /* This is, in fact, an array */ - const char *env_entry_name = &length_name[4]; - struct ctf_trace_class_env_entry *env_entry = - ctf_trace_class_borrow_env_entry_by_name( - ctx->ctf_tc, env_entry_name); - struct ctf_field_class_array *array_decl; - - if (!env_entry) { - _BT_LOGE_NODE(node_field_class_declarator, - "Cannot find environment entry: " - "name=\"%s\"", env_entry_name); - ret = -EINVAL; - goto error; - } - - if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) { - _BT_LOGE_NODE(node_field_class_declarator, - "Wrong environment entry type " - "(expecting integer): " - "name=\"%s\"", env_entry_name); - ret = -EINVAL; - goto error; - } - - if (env_entry->value.i < 0) { - _BT_LOGE_NODE(node_field_class_declarator, - "Invalid, negative array length: " - "env-entry-name=\"%s\", " - "value=%" PRId64, - env_entry_name, - env_entry->value.i); - ret = -EINVAL; - goto error; - } - - array_decl = ctf_field_class_array_create(); - BT_ASSERT(array_decl); - array_decl->length = - (uint64_t) env_entry->value.i; - array_decl->base.elem_fc = nested_decl; - nested_decl = NULL; - decl = (void *) array_decl; - } else { - char *length_name_no_underscore = - remove_underscores_from_field_ref( - length_name); - if (!length_name_no_underscore) { - /* - * remove_underscores_from_field_ref() - * logs errors - */ - ret = -EINVAL; - goto error; - } - seq_decl = ctf_field_class_sequence_create(); - BT_ASSERT(seq_decl); - seq_decl->base.elem_fc = nested_decl; - nested_decl = NULL; - g_string_assign(seq_decl->length_ref, - length_name_no_underscore); - free(length_name_no_underscore); - decl = (void *) seq_decl; - } - - g_free(length_name); - break; - } - default: - ret = -EINVAL; - goto error; - } - - BT_ASSERT(!nested_decl); - BT_ASSERT(decl); - BT_ASSERT(!*field_decl); - - /* - * At this point, we found the next nested declaration. - * We currently own this (and lost the ownership of - * nested_decl in the meantime). Pass this next - * nested declaration as the content of the outer - * container, MOVING its ownership. - */ - ret = visit_field_class_declarator(ctx, cls_specifier_list, - field_name, - node_field_class_declarator-> - u.field_class_declarator.u.nested.field_class_declarator, - &outer_field_decl, decl); - decl = NULL; - if (ret) { - BT_ASSERT(!outer_field_decl); - ret = -EINVAL; - goto error; - } - - BT_ASSERT(outer_field_decl); - *field_decl = outer_field_decl; - outer_field_decl = NULL; - } - - BT_ASSERT(*field_decl); - goto end; - -error: - ctf_field_class_destroy(*field_decl); - *field_decl = NULL; - - if (ret >= 0) { - ret = -1; - } - -end: - ctf_field_class_destroy(nested_decl); - nested_decl = NULL; - return ret; -} - -static -int visit_struct_decl_field(struct ctx *ctx, - struct ctf_field_class_struct *struct_decl, - struct ctf_node *cls_specifier_list, - struct bt_list_head *field_class_declarators) -{ - int ret = 0; - struct ctf_node *iter; - struct ctf_field_class *field_decl = NULL; - - bt_list_for_each_entry(iter, field_class_declarators, siblings) { - field_decl = NULL; - GQuark qfield_name; - const char *field_name; - - ret = visit_field_class_declarator(ctx, cls_specifier_list, - &qfield_name, iter, &field_decl, NULL); - if (ret) { - BT_ASSERT(!field_decl); - _BT_LOGE_NODE(cls_specifier_list, - "Cannot visit field class declarator: ret=%d", ret); - goto error; - } - - BT_ASSERT(field_decl); - field_name = g_quark_to_string(qfield_name); - - /* Check if field with same name already exists */ - if (ctf_field_class_struct_borrow_member_by_name( - struct_decl, field_name)) { - _BT_LOGE_NODE(cls_specifier_list, - "Duplicate field in structure field class: " - "field-name=\"%s\"", field_name); - ret = -EINVAL; - goto error; - } - - /* Add field to structure */ - ctf_field_class_struct_append_member(struct_decl, - field_name, field_decl); - field_decl = NULL; - } - - return 0; - -error: - ctf_field_class_destroy(field_decl); - field_decl = NULL; - return ret; -} - -static -int visit_variant_decl_field(struct ctx *ctx, - struct ctf_field_class_variant *variant_decl, - struct ctf_node *cls_specifier_list, - struct bt_list_head *field_class_declarators) -{ - int ret = 0; - struct ctf_node *iter; - struct ctf_field_class *field_decl = NULL; - - bt_list_for_each_entry(iter, field_class_declarators, siblings) { - field_decl = NULL; - GQuark qfield_name; - const char *field_name; - - ret = visit_field_class_declarator(ctx, cls_specifier_list, - &qfield_name, iter, &field_decl, NULL); - if (ret) { - BT_ASSERT(!field_decl); - _BT_LOGE_NODE(cls_specifier_list, - "Cannot visit field class declarator: ret=%d", ret); - goto error; - } - - BT_ASSERT(field_decl); - field_name = g_quark_to_string(qfield_name); - - /* Check if field with same name already exists */ - if (ctf_field_class_variant_borrow_option_by_name( - variant_decl, field_name)) { - _BT_LOGE_NODE(cls_specifier_list, - "Duplicate field in variant field class: " - "field-name=\"%s\"", field_name); - ret = -EINVAL; - goto error; - } - - /* Add field to structure */ - ctf_field_class_variant_append_option(variant_decl, - field_name, field_decl); - field_decl = NULL; - } - - return 0; - -error: - ctf_field_class_destroy(field_decl); - field_decl = NULL; - return ret; -} - -static -int visit_field_class_def(struct ctx *ctx, struct ctf_node *cls_specifier_list, - struct bt_list_head *field_class_declarators) -{ - int ret = 0; - GQuark qidentifier; - struct ctf_node *iter; - struct ctf_field_class *class_decl = NULL; - - bt_list_for_each_entry(iter, field_class_declarators, siblings) { - ret = visit_field_class_declarator(ctx, cls_specifier_list, - &qidentifier, iter, &class_decl, NULL); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit field class declarator: ret=%d", ret); - ret = -EINVAL; - goto end; - } - - /* Do not allow field class def and alias of untagged variants */ - if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) { - struct ctf_field_class_variant *var_fc = - (void *) class_decl; - - if (var_fc->tag_path.path->len == 0) { - _BT_LOGE_NODE(iter, - "Type definition of untagged variant field class is not allowed."); - ret = -EPERM; - goto end; - } - } - - ret = ctx_decl_scope_register_alias(ctx->current_scope, - g_quark_to_string(qidentifier), class_decl); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot register field class alias: name=\"%s\"", - g_quark_to_string(qidentifier)); - goto end; - } - } - -end: - ctf_field_class_destroy(class_decl); - class_decl = NULL; - return ret; -} - -static -int visit_field_class_alias(struct ctx *ctx, struct ctf_node *target, - struct ctf_node *alias) -{ - int ret = 0; - GQuark qalias; - struct ctf_node *node; - GQuark qdummy_field_name; - struct ctf_field_class *class_decl = NULL; - - /* Create target field class */ - if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) { - node = NULL; - } else { - node = _BT_LIST_FIRST_ENTRY( - &target->u.field_class_alias_target.field_class_declarators, - struct ctf_node, siblings); - } - - ret = visit_field_class_declarator(ctx, - target->u.field_class_alias_target.field_class_specifier_list, - &qdummy_field_name, node, &class_decl, NULL); - if (ret) { - BT_ASSERT(!class_decl); - _BT_LOGE_NODE(node, - "Cannot visit field class declarator: ret=%d", ret); - goto end; - } - - /* Do not allow field class def and alias of untagged variants */ - if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) { - struct ctf_field_class_variant *var_fc = (void *) class_decl; - - if (var_fc->tag_path.path->len == 0) { - _BT_LOGE_NODE(target, - "Type definition of untagged variant field class is not allowed."); - ret = -EPERM; - goto end; - } - } - - /* - * The semantic validator does not check whether the target is - * abstract or not (if it has an identifier). Check it here. - */ - if (qdummy_field_name != 0) { - _BT_LOGE_NODE(target, - "Expecting empty identifier: id=\"%s\"", - g_quark_to_string(qdummy_field_name)); - ret = -EINVAL; - goto end; - } - - /* Create alias identifier */ - node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators, - struct ctf_node, siblings); - qalias = create_class_alias_identifier(ctx, - alias->u.field_class_alias_name.field_class_specifier_list, node); - ret = ctx_decl_scope_register_alias(ctx->current_scope, - g_quark_to_string(qalias), class_decl); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot register class alias: name=\"%s\"", - g_quark_to_string(qalias)); - goto end; - } - -end: - ctf_field_class_destroy(class_decl); - class_decl = NULL; - return ret; -} - -static -int visit_struct_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct ctf_field_class_struct *struct_decl) -{ - int ret = 0; - - switch (entry_node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, - entry_node->u.field_class_def.field_class_specifier_list, - &entry_node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot add field class found in structure field class: ret=%d", - ret); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target, - entry_node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot add field class alias found in structure field class: ret=%d", - ret); - goto end; - } - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - /* Field */ - ret = visit_struct_decl_field(ctx, struct_decl, - entry_node->u.struct_or_variant_declaration. - field_class_specifier_list, - &entry_node->u.struct_or_variant_declaration. - field_class_declarators); - if (ret) { - goto end; - } - break; - default: - _BT_LOGE_NODE(entry_node, - "Unexpected node type: node-type=%d", entry_node->type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int visit_variant_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct ctf_field_class_variant *variant_decl) -{ - int ret = 0; - - switch (entry_node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, - entry_node->u.field_class_def.field_class_specifier_list, - &entry_node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot add field class found in variant field class: ret=%d", - ret); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target, - entry_node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot add field class alias found in variant field class: ret=%d", - ret); - goto end; - } - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - /* Field */ - ret = visit_variant_decl_field(ctx, variant_decl, - entry_node->u.struct_or_variant_declaration. - field_class_specifier_list, - &entry_node->u.struct_or_variant_declaration. - field_class_declarators); - if (ret) { - goto end; - } - break; - default: - _BT_LOGE_NODE(entry_node, - "Unexpected node type: node-type=%d", - entry_node->type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int visit_struct_decl(struct ctx *ctx, const char *name, - struct bt_list_head *decl_list, int has_body, - struct bt_list_head *min_align, - struct ctf_field_class_struct **struct_decl) -{ - int ret = 0; - - BT_ASSERT(struct_decl); - *struct_decl = NULL; - - /* For named struct (without body), lookup in declaration scope */ - if (!has_body) { - if (!name) { - BT_LOGE_STR("Bodyless structure field class: missing name."); - ret = -EPERM; - goto error; - } - - *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope, - name, -1, true); - if (!*struct_decl) { - BT_LOGE("Cannot find structure field class: name=\"struct %s\"", - name); - ret = -EINVAL; - goto error; - } - } else { - struct ctf_node *entry_node; - uint64_t min_align_value = 0; - - if (name) { - if (ctx_decl_scope_lookup_struct( - ctx->current_scope, name, 1, false)) { - BT_LOGE("Structure field class already declared in local scope: " - "name=\"struct %s\"", name); - ret = -EINVAL; - goto error; - } - } - - if (!bt_list_empty(min_align)) { - ret = get_unary_unsigned(min_align, &min_align_value); - if (ret) { - BT_LOGE("Unexpected unary expression for structure field class's `align` attribute: " - "ret=%d", ret); - goto error; - } - } - - *struct_decl = ctf_field_class_struct_create(); - BT_ASSERT(*struct_decl); - - if (min_align_value != 0) { - (*struct_decl)->base.alignment = min_align_value; - } - - _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - ret = visit_struct_decl_entry(ctx, entry_node, - *struct_decl); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot visit structure field class entry: " - "ret=%d", ret); - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (name) { - ret = ctx_decl_scope_register_struct(ctx->current_scope, - name, *struct_decl); - if (ret) { - BT_LOGE("Cannot register structure field class in declaration scope: " - "name=\"struct %s\", ret=%d", name, ret); - goto error; - } - } - } - - return 0; - -error: - ctf_field_class_destroy((void *) *struct_decl); - *struct_decl = NULL; - return ret; -} - -static -int visit_variant_decl(struct ctx *ctx, const char *name, - const char *tag, struct bt_list_head *decl_list, - int has_body, struct ctf_field_class_variant **variant_decl) -{ - int ret = 0; - struct ctf_field_class_variant *untagged_variant_decl = NULL; - - BT_ASSERT(variant_decl); - *variant_decl = NULL; - - /* For named variant (without body), lookup in declaration scope */ - if (!has_body) { - if (!name) { - BT_LOGE_STR("Bodyless variant field class: missing name."); - ret = -EPERM; - goto error; - } - - untagged_variant_decl = - ctx_decl_scope_lookup_variant(ctx->current_scope, - name, -1, true); - if (!untagged_variant_decl) { - BT_LOGE("Cannot find variant field class: name=\"variant %s\"", - name); - ret = -EINVAL; - goto error; - } - } else { - struct ctf_node *entry_node; - - if (name) { - if (ctx_decl_scope_lookup_variant(ctx->current_scope, - name, 1, false)) { - BT_LOGE("Variant field class already declared in local scope: " - "name=\"variant %s\"", name); - ret = -EINVAL; - goto error; - } - } - - untagged_variant_decl = ctf_field_class_variant_create(); - BT_ASSERT(untagged_variant_decl); - _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - ret = visit_variant_decl_entry(ctx, entry_node, - untagged_variant_decl); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot visit variant field class entry: " - "ret=%d", ret); - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (name) { - ret = ctx_decl_scope_register_variant( - ctx->current_scope, name, - untagged_variant_decl); - if (ret) { - BT_LOGE("Cannot register variant field class in declaration scope: " - "name=\"variant %s\", ret=%d", name, ret); - goto error; - } - } - } - - /* - * If tagged, create tagged variant and return; otherwise - * return untagged variant. - */ - if (!tag) { - *variant_decl = untagged_variant_decl; - untagged_variant_decl = NULL; - } else { - /* - * At this point, we have a fresh untagged variant; nobody - * else owns it. Set its tag now. - */ - char *tag_no_underscore = - remove_underscores_from_field_ref(tag); - - if (!tag_no_underscore) { - /* remove_underscores_from_field_ref() logs errors */ - goto error; - } - - g_string_assign(untagged_variant_decl->tag_ref, - tag_no_underscore); - free(tag_no_underscore); - *variant_decl = untagged_variant_decl; - untagged_variant_decl = NULL; - } - - BT_ASSERT(!untagged_variant_decl); - BT_ASSERT(*variant_decl); - return 0; - -error: - ctf_field_class_destroy((void *) untagged_variant_decl); - untagged_variant_decl = NULL; - ctf_field_class_destroy((void *) *variant_decl); - *variant_decl = NULL; - return ret; -} - -struct uori { - bool is_signed; - union { - uint64_t u; - uint64_t i; - } value; -}; - -static -int visit_enum_decl_entry(struct ctx *ctx, struct ctf_node *enumerator, - struct ctf_field_class_enum *enum_decl, struct uori *last) -{ - int ret = 0; - int nr_vals = 0; - struct ctf_node *iter; - struct uori start = { - .is_signed = false, - .value.u = 0, - }; - struct uori end = { - .is_signed = false, - .value.u = 0, - }; - const char *label = enumerator->u.enumerator.id; - const char *effective_label = label; - struct bt_list_head *values = &enumerator->u.enumerator.values; - - bt_list_for_each_entry(iter, values, siblings) { - struct uori *target; - - if (iter->type != NODE_UNARY_EXPRESSION) { - _BT_LOGE_NODE(iter, - "Wrong expression for enumeration field class label: " - "node-type=%d, label=\"%s\"", iter->type, - label); - ret = -EINVAL; - goto error; - } - - if (nr_vals == 0) { - target = &start; - } else { - target = &end; - } - - switch (iter->u.unary_expression.type) { - case UNARY_SIGNED_CONSTANT: - target->is_signed = true; - target->value.i = - iter->u.unary_expression.u.signed_constant; - break; - case UNARY_UNSIGNED_CONSTANT: - target->is_signed = false; - target->value.u = - iter->u.unary_expression.u.unsigned_constant; - break; - default: - _BT_LOGE_NODE(iter, - "Invalid enumeration field class entry: " - "expecting constant signed or unsigned integer: " - "node-type=%d, label=\"%s\"", - iter->u.unary_expression.type, label); - ret = -EINVAL; - goto error; - } - - if (nr_vals > 1) { - _BT_LOGE_NODE(iter, - "Invalid enumeration field class entry: label=\"%s\"", - label); - ret = -EINVAL; - goto error; - } - - nr_vals++; - } - - if (nr_vals == 0) { - start = *last; - } - - if (nr_vals <= 1) { - end = start; - } - - if (end.is_signed) { - last->value.i = end.value.i + 1; - } else { - last->value.u = end.value.u + 1; - } - - if (label[0] == '_') { - /* - * Strip the first underscore of any enumeration field - * class's label in case this enumeration FC is used as - * a variant FC tag later. The variant FC choice names - * could also start with `_`, in which case the prefix - * is removed, and it the resulting choice name needs to - * match tag labels. - */ - effective_label = &label[1]; - } - - ctf_field_class_enum_append_mapping(enum_decl, effective_label, - start.value.u, end.value.u); - return 0; - -error: - return ret; -} - -static -int visit_enum_decl(struct ctx *ctx, const char *name, - struct ctf_node *container_cls, - struct bt_list_head *enumerator_list, - int has_body, struct ctf_field_class_enum **enum_decl) -{ - int ret = 0; - GQuark qdummy_id; - struct ctf_field_class_int *integer_decl = NULL; - - BT_ASSERT(enum_decl); - *enum_decl = NULL; - - /* For named enum (without body), lookup in declaration scope */ - if (!has_body) { - if (!name) { - BT_LOGE_STR("Bodyless enumeration field class: missing name."); - ret = -EPERM; - goto error; - } - - *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope, - name, -1, true); - if (!*enum_decl) { - BT_LOGE("Cannot find enumeration field class: " - "name=\"enum %s\"", name); - ret = -EINVAL; - goto error; - } - } else { - struct ctf_node *iter; - struct uori last_value = { - .is_signed = false, - .value.u = 0, - }; - - if (name) { - if (ctx_decl_scope_lookup_enum(ctx->current_scope, - name, 1, false)) { - BT_LOGE("Enumeration field class already declared in local scope: " - "name=\"enum %s\"", name); - ret = -EINVAL; - goto error; - } - } - - if (!container_cls) { - integer_decl = (void *) ctx_decl_scope_lookup_alias( - ctx->current_scope, "int", -1, true); - if (!integer_decl) { - BT_LOGE_STR("Cannot find implicit `int` field class alias for enumeration field class."); - ret = -EINVAL; - goto error; - } - } else { - ret = visit_field_class_declarator(ctx, container_cls, - &qdummy_id, NULL, (void *) &integer_decl, - NULL); - if (ret) { - BT_ASSERT(!integer_decl); - ret = -EINVAL; - goto error; - } - } - - BT_ASSERT(integer_decl); - - if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) { - BT_LOGE("Container field class for enumeration field class is not an integer field class: " - "fc-type=%d", integer_decl->base.base.type); - ret = -EINVAL; - goto error; - } - - *enum_decl = ctf_field_class_enum_create(); - BT_ASSERT(*enum_decl); - (*enum_decl)->base.base.base.alignment = - integer_decl->base.base.alignment; - ctf_field_class_int_copy_content((void *) *enum_decl, - (void *) integer_decl); - last_value.is_signed = (*enum_decl)->base.is_signed; - - bt_list_for_each_entry(iter, enumerator_list, siblings) { - ret = visit_enum_decl_entry(ctx, iter, *enum_decl, - &last_value); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit enumeration field class entry: " - "ret=%d", ret); - goto error; - } - } - - if (name) { - ret = ctx_decl_scope_register_enum(ctx->current_scope, - name, *enum_decl); - if (ret) { - BT_LOGE("Cannot register enumeration field class in declaration scope: " - "ret=%d", ret); - goto error; - } - } - } - - goto end; - -error: - ctf_field_class_destroy((void *) *enum_decl); - *enum_decl = NULL; - -end: - ctf_field_class_destroy((void *) integer_decl); - integer_decl = NULL; - return ret; -} - -static -int visit_field_class_specifier(struct ctx *ctx, - struct ctf_node *cls_specifier_list, - struct ctf_field_class **decl) -{ - int ret = 0; - GString *str = NULL; - - *decl = NULL; - str = g_string_new(""); - ret = get_class_specifier_list_name(ctx, cls_specifier_list, str); - if (ret) { - _BT_LOGE_NODE(cls_specifier_list, - "Cannot get field class specifier list's name: ret=%d", ret); - goto error; - } - - *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1, - true); - if (!*decl) { - _BT_LOGE_NODE(cls_specifier_list, - "Cannot find field class alias: name=\"%s\"", str->str); - ret = -EINVAL; - goto error; - } - - goto end; - -error: - ctf_field_class_destroy(*decl); - *decl = NULL; - -end: - if (str) { - g_string_free(str, TRUE); - } - - return ret; -} - -static -int visit_integer_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct ctf_field_class_int **integer_decl) -{ - int set = 0; - int ret = 0; - int signedness = 0; - struct ctf_node *expression; - uint64_t alignment = 0, size = 0; - struct ctf_clock_class *mapped_clock_class = NULL; - enum ctf_encoding encoding = CTF_ENCODING_NONE; - bt_field_class_integer_preferred_display_base base = - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; - enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order; - - *integer_decl = NULL; - - bt_list_for_each_entry(expression, expressions, siblings) { - struct ctf_node *left, *right; - - left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, - struct ctf_node, siblings); - right = _BT_LIST_FIRST_ENTRY( - &expression->u.ctf_expression.right, struct ctf_node, - siblings); - - if (left->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(left, - "Unexpected unary expression type: type=%d", - left->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "signed")) { - if (_IS_SET(&set, _INTEGER_SIGNED_SET)) { - _BT_LOGE_DUP_ATTR(left, "signed", - "integer field class"); - ret = -EPERM; - goto error; - } - - signedness = get_boolean(right); - if (signedness < 0) { - _BT_LOGE_NODE(right, - "Invalid boolean value for integer field class's `signed` attribute: " - "ret=%d", ret); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_SIGNED_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "byte_order")) { - if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) { - _BT_LOGE_DUP_ATTR(left, "byte_order", - "integer field class"); - ret = -EPERM; - goto error; - } - - byte_order = get_real_byte_order(ctx, right); - if (byte_order == -1) { - _BT_LOGE_NODE(right, - "Invalid `byte_order` attribute in integer field class: " - "ret=%d", ret); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_BYTE_ORDER_SET); - } else if (!strcmp(left->u.unary_expression.u.string, "size")) { - if (_IS_SET(&set, _INTEGER_SIZE_SET)) { - _BT_LOGE_DUP_ATTR(left, "size", - "integer field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _BT_LOGE_NODE(right, - "Invalid `size` attribute in integer field class: " - "expecting unsigned constant integer: " - "node-type=%d", - right->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - size = right->u.unary_expression.u.unsigned_constant; - if (size == 0) { - _BT_LOGE_NODE(right, - "Invalid `size` attribute in integer field class: " - "expecting positive constant integer: " - "size=%" PRIu64, size); - ret = -EINVAL; - goto error; - } else if (size > 64) { - _BT_LOGE_NODE(right, - "Invalid `size` attribute in integer field class: " - "integer fields over 64 bits are not supported as of this version: " - "size=%" PRIu64, size); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_SIZE_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "align")) { - if (_IS_SET(&set, _INTEGER_ALIGN_SET)) { - _BT_LOGE_DUP_ATTR(left, "align", - "integer field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _BT_LOGE_NODE(right, - "Invalid `align` attribute in integer field class: " - "expecting unsigned constant integer: " - "node-type=%d", - right->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - alignment = - right->u.unary_expression.u.unsigned_constant; - if (!is_align_valid(alignment)) { - _BT_LOGE_NODE(right, - "Invalid `align` attribute in integer field class: " - "expecting power of two: " - "align=%" PRIu64, alignment); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_ALIGN_SET); - } else if (!strcmp(left->u.unary_expression.u.string, "base")) { - if (_IS_SET(&set, _INTEGER_BASE_SET)) { - _BT_LOGE_DUP_ATTR(left, "base", - "integer field class"); - ret = -EPERM; - goto error; - } - - switch (right->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - { - uint64_t constant = right->u.unary_expression. - u.unsigned_constant; - - switch (constant) { - case 2: - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY; - break; - case 8: - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL; - break; - case 10: - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; - break; - case 16: - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; - break; - default: - _BT_LOGE_NODE(right, - "Invalid `base` attribute in integer field class: " - "base=%" PRIu64, - right->u.unary_expression.u.unsigned_constant); - ret = -EINVAL; - goto error; - } - break; - } - case UNARY_STRING: - { - char *s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _BT_LOGE_NODE(right, - "Unexpected unary expression for integer field class's `base` attribute."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "decimal") || - !strcmp(s_right, "dec") || - !strcmp(s_right, "d") || - !strcmp(s_right, "i") || - !strcmp(s_right, "u")) { - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; - } else if (!strcmp(s_right, "hexadecimal") || - !strcmp(s_right, "hex") || - !strcmp(s_right, "x") || - !strcmp(s_right, "X") || - !strcmp(s_right, "p")) { - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; - } else if (!strcmp(s_right, "octal") || - !strcmp(s_right, "oct") || - !strcmp(s_right, "o")) { - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL; - } else if (!strcmp(s_right, "binary") || - !strcmp(s_right, "b")) { - base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY; - } else { - _BT_LOGE_NODE(right, - "Unexpected unary expression for integer field class's `base` attribute: " - "base=\"%s\"", s_right); - g_free(s_right); - ret = -EINVAL; - goto error; - } - - g_free(s_right); - break; - } - default: - _BT_LOGE_NODE(right, - "Invalid `base` attribute in integer field class: " - "expecting unsigned constant integer or unary string."); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_BASE_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "encoding")) { - char *s_right; - - if (_IS_SET(&set, _INTEGER_ENCODING_SET)) { - _BT_LOGE_DUP_ATTR(left, "encoding", - "integer field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(right, - "Invalid `encoding` attribute in integer field class: " - "expecting unary string."); - ret = -EINVAL; - goto error; - } - - s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _BT_LOGE_NODE(right, - "Unexpected unary expression for integer field class's `encoding` attribute."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "UTF8") || - !strcmp(s_right, "utf8") || - !strcmp(s_right, "utf-8") || - !strcmp(s_right, "UTF-8") || - !strcmp(s_right, "ASCII") || - !strcmp(s_right, "ascii")) { - encoding = CTF_ENCODING_UTF8; - } else if (!strcmp(s_right, "none")) { - encoding = CTF_ENCODING_NONE; - } else { - _BT_LOGE_NODE(right, - "Invalid `encoding` attribute in integer field class: " - "unknown encoding: encoding=\"%s\"", - s_right); - g_free(s_right); - ret = -EINVAL; - goto error; - } - - g_free(s_right); - _SET(&set, _INTEGER_ENCODING_SET); - } else if (!strcmp(left->u.unary_expression.u.string, "map")) { - const char *clock_name; - - if (_IS_SET(&set, _INTEGER_MAP_SET)) { - _BT_LOGE_DUP_ATTR(left, "map", - "integer field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(right, - "Invalid `map` attribute in integer field class: " - "expecting unary string."); - ret = -EINVAL; - goto error; - } - - clock_name = - get_map_clock_name_value( - &expression->u.ctf_expression.right); - if (!clock_name) { - char *s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - - if (!s_right) { - _BT_LOGE_NODE(right, - "Unexpected unary expression for integer field class's `map` attribute."); - ret = -EINVAL; - goto error; - } - - _BT_LOGE_NODE(right, - "Invalid `map` attribute in integer field class: " - "cannot find clock class at this point: name=\"%s\"", - s_right); - _SET(&set, _INTEGER_MAP_SET); - g_free(s_right); - continue; - } - - mapped_clock_class = - ctf_trace_class_borrow_clock_class_by_name( - ctx->ctf_tc, clock_name); - if (!mapped_clock_class) { - _BT_LOGE_NODE(right, - "Invalid `map` attribute in integer field class: " - "cannot find clock class at this point: name=\"%s\"", - clock_name); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_MAP_SET); - } else { - _BT_LOGW_NODE(left, - "Unknown attribute in integer field class: " - "attr-name=\"%s\"", - left->u.unary_expression.u.string); - } - } - - if (!_IS_SET(&set, _INTEGER_SIZE_SET)) { - BT_LOGE_STR("Missing `size` attribute in integer field class."); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) { - if (size % CHAR_BIT) { - /* Bit-packed alignment */ - alignment = 1; - } else { - /* Byte-packed alignment */ - alignment = CHAR_BIT; - } - } - - *integer_decl = ctf_field_class_int_create(); - BT_ASSERT(*integer_decl); - (*integer_decl)->base.base.alignment = alignment; - (*integer_decl)->base.byte_order = byte_order; - (*integer_decl)->base.size = size; - (*integer_decl)->is_signed = (signedness > 0); - (*integer_decl)->disp_base = base; - (*integer_decl)->encoding = encoding; - (*integer_decl)->mapped_clock_class = mapped_clock_class; - return 0; - -error: - ctf_field_class_destroy((void *) *integer_decl); - *integer_decl = NULL; - return ret; -} - -static -int visit_floating_point_number_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct ctf_field_class_float **float_decl) -{ - int set = 0; - int ret = 0; - struct ctf_node *expression; - uint64_t alignment = 1, exp_dig = 0, mant_dig = 0; - enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order; - - *float_decl = NULL; - - bt_list_for_each_entry(expression, expressions, siblings) { - struct ctf_node *left, *right; - - left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, - struct ctf_node, siblings); - right = _BT_LIST_FIRST_ENTRY( - &expression->u.ctf_expression.right, struct ctf_node, - siblings); - - if (left->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(left, - "Unexpected unary expression type: type=%d", - left->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "byte_order")) { - if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) { - _BT_LOGE_DUP_ATTR(left, "byte_order", - "floating point number field class"); - ret = -EPERM; - goto error; - } - - byte_order = get_real_byte_order(ctx, right); - if (byte_order == -1) { - _BT_LOGE_NODE(right, - "Invalid `byte_order` attribute in floating point number field class: " - "ret=%d", ret); - ret = -EINVAL; - goto error; - } - - _SET(&set, _FLOAT_BYTE_ORDER_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "exp_dig")) { - if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { - _BT_LOGE_DUP_ATTR(left, "exp_dig", - "floating point number field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _BT_LOGE_NODE(right, - "Invalid `exp_dig` attribute in floating point number field class: " - "expecting unsigned constant integer: " - "node-type=%d", - right->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - exp_dig = right->u.unary_expression.u.unsigned_constant; - _SET(&set, _FLOAT_EXP_DIG_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "mant_dig")) { - if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { - _BT_LOGE_DUP_ATTR(left, "mant_dig", - "floating point number field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _BT_LOGE_NODE(right, - "Invalid `mant_dig` attribute in floating point number field class: " - "expecting unsigned constant integer: " - "node-type=%d", - right->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - mant_dig = right->u.unary_expression.u. - unsigned_constant; - _SET(&set, _FLOAT_MANT_DIG_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "align")) { - if (_IS_SET(&set, _FLOAT_ALIGN_SET)) { - _BT_LOGE_DUP_ATTR(left, "align", - "floating point number field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _BT_LOGE_NODE(right, - "Invalid `align` attribute in floating point number field class: " - "expecting unsigned constant integer: " - "node-type=%d", - right->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - alignment = right->u.unary_expression.u. - unsigned_constant; - - if (!is_align_valid(alignment)) { - _BT_LOGE_NODE(right, - "Invalid `align` attribute in floating point number field class: " - "expecting power of two: " - "align=%" PRIu64, alignment); - ret = -EINVAL; - goto error; - } - - _SET(&set, _FLOAT_ALIGN_SET); - } else { - _BT_LOGW_NODE(left, - "Unknown attribute in floating point number field class: " - "attr-name=\"%s\"", - left->u.unary_expression.u.string); - } - } - - if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { - BT_LOGE_STR("Missing `mant_dig` attribute in floating point number field class."); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { - BT_LOGE_STR("Missing `exp_dig` attribute in floating point number field class."); - ret = -EPERM; - goto error; - } - - if (mant_dig != 24 && mant_dig != 53) { - BT_LOGE_STR("`mant_dig` attribute: expecting 24 or 53."); - ret = -EPERM; - goto error; - } - - if (mant_dig == 24 && exp_dig != 8) { - BT_LOGE_STR("`exp_dig` attribute: expecting 8 because `mant_dig` is 24."); - ret = -EPERM; - goto error; - } - - if (mant_dig == 53 && exp_dig != 11) { - BT_LOGE_STR("`exp_dig` attribute: expecting 11 because `mant_dig` is 53."); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) { - if ((mant_dig + exp_dig) % CHAR_BIT) { - /* Bit-packed alignment */ - alignment = 1; - } else { - /* Byte-packed alignment */ - alignment = CHAR_BIT; - } - } - - *float_decl = ctf_field_class_float_create(); - BT_ASSERT(*float_decl); - (*float_decl)->base.base.alignment = alignment; - (*float_decl)->base.byte_order = byte_order; - (*float_decl)->base.size = mant_dig + exp_dig; - return 0; - -error: - ctf_field_class_destroy((void *) *float_decl); - *float_decl = NULL; - return ret; -} - -static -int visit_string_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct ctf_field_class_string **string_decl) -{ - int set = 0; - int ret = 0; - struct ctf_node *expression; - enum ctf_encoding encoding = CTF_ENCODING_UTF8; - - *string_decl = NULL; - - bt_list_for_each_entry(expression, expressions, siblings) { - struct ctf_node *left, *right; - - left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, - struct ctf_node, siblings); - right = _BT_LIST_FIRST_ENTRY( - &expression->u.ctf_expression.right, struct ctf_node, - siblings); - - if (left->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(left, - "Unexpected unary expression type: type=%d", - left->u.unary_expression.type); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "encoding")) { - char *s_right; - - if (_IS_SET(&set, _STRING_ENCODING_SET)) { - _BT_LOGE_DUP_ATTR(left, "encoding", - "string field class"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_NODE(right, - "Invalid `encoding` attribute in string field class: " - "expecting unary string."); - ret = -EINVAL; - goto error; - } - - s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _BT_LOGE_NODE(right, - "Unexpected unary expression for string field class's `encoding` attribute."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "UTF8") || - !strcmp(s_right, "utf8") || - !strcmp(s_right, "utf-8") || - !strcmp(s_right, "UTF-8") || - !strcmp(s_right, "ASCII") || - !strcmp(s_right, "ascii")) { - encoding = CTF_ENCODING_UTF8; - } else if (!strcmp(s_right, "none")) { - encoding = CTF_ENCODING_NONE; - } else { - _BT_LOGE_NODE(right, - "Invalid `encoding` attribute in string field class: " - "unknown encoding: encoding=\"%s\"", - s_right); - g_free(s_right); - ret = -EINVAL; - goto error; - } - - g_free(s_right); - _SET(&set, _STRING_ENCODING_SET); - } else { - _BT_LOGW_NODE(left, - "Unknown attribute in string field class: " - "attr-name=\"%s\"", - left->u.unary_expression.u.string); - } - } - - *string_decl = ctf_field_class_string_create(); - BT_ASSERT(*string_decl); - (*string_decl)->encoding = encoding; - return 0; - -error: - ctf_field_class_destroy((void *) *string_decl); - *string_decl = NULL; - return ret; -} - -static -int visit_field_class_specifier_list(struct ctx *ctx, - struct ctf_node *ts_list, struct ctf_field_class **decl) -{ - int ret = 0; - struct ctf_node *first, *node; - - *decl = NULL; - - if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) { - _BT_LOGE_NODE(ts_list, - "Unexpected node type: node-type=%d", ts_list->type); - ret = -EINVAL; - goto error; - } - - first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head, - struct ctf_node, siblings); - if (first->type != NODE_TYPE_SPECIFIER) { - _BT_LOGE_NODE(first, - "Unexpected node type: node-type=%d", first->type); - ret = -EINVAL; - goto error; - } - - node = first->u.field_class_specifier.node; - - switch (first->u.field_class_specifier.type) { - case TYPESPEC_INTEGER: - ret = visit_integer_decl(ctx, &node->u.integer.expressions, - (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_FLOATING_POINT: - ret = visit_floating_point_number_decl(ctx, - &node->u.floating_point.expressions, (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_STRING: - ret = visit_string_decl(ctx, - &node->u.string.expressions, (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_STRUCT: - ret = visit_struct_decl(ctx, node->u._struct.name, - &node->u._struct.declaration_list, - node->u._struct.has_body, - &node->u._struct.min_align, (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_VARIANT: - ret = visit_variant_decl(ctx, node->u.variant.name, - node->u.variant.choice, - &node->u.variant.declaration_list, - node->u.variant.has_body, (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_ENUM: - ret = visit_enum_decl(ctx, node->u._enum.enum_id, - node->u._enum.container_field_class, - &node->u._enum.enumerator_list, - node->u._enum.has_body, (void *) decl); - if (ret) { - BT_ASSERT(!*decl); - goto error; - } - break; - case TYPESPEC_VOID: - case TYPESPEC_CHAR: - case TYPESPEC_SHORT: - case TYPESPEC_INT: - case TYPESPEC_LONG: - case TYPESPEC_FLOAT: - case TYPESPEC_DOUBLE: - case TYPESPEC_SIGNED: - case TYPESPEC_UNSIGNED: - case TYPESPEC_BOOL: - case TYPESPEC_COMPLEX: - case TYPESPEC_IMAGINARY: - case TYPESPEC_CONST: - case TYPESPEC_ID_TYPE: - ret = visit_field_class_specifier(ctx, ts_list, decl); - if (ret) { - _BT_LOGE_NODE(first, - "Cannot visit field class specifier: ret=%d", - ret); - BT_ASSERT(!*decl); - goto error; - } - break; - default: - _BT_LOGE_NODE(first, - "Unexpected field class specifier type: node-type=%d", - first->u.field_class_specifier.type); - ret = -EINVAL; - goto error; - } - - BT_ASSERT(*decl); - return 0; - -error: - ctf_field_class_destroy((void *) *decl); - *decl = NULL; - return ret; -} - -static -int visit_event_decl_entry(struct ctx *ctx, struct ctf_node *node, - struct ctf_event_class *event_class, uint64_t *stream_id, - int *set) -{ - int ret = 0; - char *left = NULL; - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, - &node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class found in event class."); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, - node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class alias found in event class."); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "name")) { - /* This is already known at this stage */ - if (_IS_SET(set, _EVENT_NAME_SET)) { - _BT_LOGE_DUP_ATTR(node, "name", "event class"); - ret = -EPERM; - goto error; - } - - _SET(set, _EVENT_NAME_SET); - } else if (!strcmp(left, "id")) { - int64_t id = -1; - - if (_IS_SET(set, _EVENT_ID_SET)) { - _BT_LOGE_DUP_ATTR(node, "id", "event class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - (uint64_t *) &id); - /* Only read "id" if get_unary_unsigned() succeeded. */ - if (ret || (!ret && id < 0)) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for event class's `id` attribute."); - ret = -EINVAL; - goto error; - } - - event_class->id = id; - _SET(set, _EVENT_ID_SET); - } else if (!strcmp(left, "stream_id")) { - if (_IS_SET(set, _EVENT_STREAM_ID_SET)) { - _BT_LOGE_DUP_ATTR(node, "stream_id", - "event class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - stream_id); - - /* - * Only read "stream_id" if get_unary_unsigned() - * succeeded. - */ - if (ret) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for event class's `stream_id` attribute."); - ret = -EINVAL; - goto error; - } - - _SET(set, _EVENT_STREAM_ID_SET); - } else if (!strcmp(left, "context")) { - if (_IS_SET(set, _EVENT_CONTEXT_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `context` entry in event class."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &event_class->spec_context_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create event class's context field class."); - goto error; - } - - BT_ASSERT(event_class->spec_context_fc); - _SET(set, _EVENT_CONTEXT_SET); - } else if (!strcmp(left, "fields")) { - if (_IS_SET(set, _EVENT_FIELDS_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `fields` entry in event class."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &event_class->payload_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create event class's payload field class."); - goto error; - } - - BT_ASSERT(event_class->payload_fc); - _SET(set, _EVENT_FIELDS_SET); - } else if (!strcmp(left, "loglevel")) { - uint64_t loglevel_value; - bt_event_class_log_level log_level = -1; - - if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) { - _BT_LOGE_DUP_ATTR(node, "loglevel", - "event class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &loglevel_value); - if (ret) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for event class's `loglevel` attribute."); - ret = -EINVAL; - goto error; - } - - switch (loglevel_value) { - case 0: - log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY; - break; - case 1: - log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT; - break; - case 2: - log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL; - break; - case 3: - log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR; - break; - case 4: - log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING; - break; - case 5: - log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE; - break; - case 6: - log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO; - break; - case 7: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM; - break; - case 8: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM; - break; - case 9: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS; - break; - case 10: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE; - break; - case 11: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT; - break; - case 12: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION; - break; - case 13: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE; - break; - case 14: - log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG; - break; - default: - _BT_LOGW_NODE(node, "Not setting event class's log level because its value is unknown: " - "log-level=%" PRIu64, loglevel_value); - } - - if (log_level != -1) { - event_class->log_level = log_level; - } - - _SET(set, _EVENT_LOG_LEVEL_SET); - } else if (!strcmp(left, "model.emf.uri")) { - char *right; - - if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) { - _BT_LOGE_DUP_ATTR(node, "model.emf.uri", - "event class"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &node->u.ctf_expression.right); - if (!right) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for event class's `model.emf.uri` attribute."); - ret = -EINVAL; - goto error; - } - - if (strlen(right) == 0) { - _BT_LOGW_NODE(node, - "Not setting event class's EMF URI because it's empty."); - } else { - g_string_assign(event_class->emf_uri, - right); - } - - g_free(right); - _SET(set, _EVENT_MODEL_EMF_URI_SET); - } else { - _BT_LOGW_NODE(node, - "Unknown attribute in event class: " - "attr-name=\"%s\"", left); - } - - g_free(left); - left = NULL; - break; - } - default: - ret = -EPERM; - goto error; - } - - goto end; - -error: - if (left) { - g_free(left); - } - -end: - return ret; -} - -static -char *get_event_decl_name(struct ctx *ctx, struct ctf_node *node) -{ - char *left = NULL; - char *name = NULL; - struct ctf_node *iter; - struct bt_list_head *decl_list = &node->u.event.declaration_list; - - bt_list_for_each_entry(iter, decl_list, siblings) { - if (iter->type != NODE_CTF_EXPRESSION) { - continue; - } - - left = concatenate_unary_strings(&iter->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(iter, - "Cannot concatenate unary strings."); - goto error; - } - - if (!strcmp(left, "name")) { - name = concatenate_unary_strings( - &iter->u.ctf_expression.right); - if (!name) { - _BT_LOGE_NODE(iter, - "Unexpected unary expression for event class's `name` attribute."); - goto error; - } - } - - g_free(left); - left = NULL; - - if (name) { - break; - } - } - - return name; - -error: - g_free(left); - return NULL; -} - -static -int visit_event_decl(struct ctx *ctx, struct ctf_node *node) -{ - int ret = 0; - int set = 0; - struct ctf_node *iter; - uint64_t stream_id = 0; - char *event_name = NULL; - struct ctf_event_class *event_class = NULL; - struct ctf_stream_class *stream_class = NULL; - struct bt_list_head *decl_list = &node->u.event.declaration_list; - bool pop_scope = false; - - if (node->visited) { - goto end; - } - - node->visited = TRUE; - event_name = get_event_decl_name(ctx, node); - if (!event_name) { - _BT_LOGE_NODE(node, - "Missing `name` attribute in event class."); - ret = -EPERM; - goto error; - } - - event_class = ctf_event_class_create(); - BT_ASSERT(event_class); - g_string_assign(event_class->name, event_name); - _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); - pop_scope = true; - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_event_decl_entry(ctx, iter, event_class, - &stream_id, &set); - if (ret) { - _BT_LOGE_NODE(iter, "Cannot visit event class's entry: " - "ret=%d", ret); - goto error; - } - } - - if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) { - /* - * Allow missing stream_id if there is only a single - * stream class. - */ - switch (ctx->ctf_tc->stream_classes->len) { - case 0: - /* Create implicit stream class if there's none */ - stream_id = 0; - stream_class = ctf_stream_class_create(); - BT_ASSERT(stream_class); - stream_class->id = stream_id; - g_ptr_array_add(ctx->ctf_tc->stream_classes, - stream_class); - break; - case 1: - /* Single stream class: get its ID */ - stream_class = ctx->ctf_tc->stream_classes->pdata[0]; - stream_id = stream_class->id; - break; - default: - _BT_LOGE_NODE(node, - "Missing `stream_id` attribute in event class."); - ret = -EPERM; - goto error; - } - } - - /* We have the stream ID now; get the stream class if found */ - if (!stream_class) { - stream_class = ctf_trace_class_borrow_stream_class_by_id( - ctx->ctf_tc, stream_id); - if (!stream_class) { - _BT_LOGE_NODE(node, - "Cannot find stream class at this point: " - "id=%" PRId64, stream_id); - ret = -EINVAL; - goto error; - } - } - - BT_ASSERT(stream_class); - - if (!_IS_SET(&set, _EVENT_ID_SET)) { - /* Allow only one event without ID per stream */ - if (stream_class->event_classes->len != 0) { - _BT_LOGE_NODE(node, - "Missing `id` attribute in event class."); - ret = -EPERM; - goto error; - } - - /* Automatic ID */ - event_class->id = 0; - } - - if (ctf_stream_class_borrow_event_class_by_id(stream_class, - event_class->id)) { - _BT_LOGE_NODE(node, - "Duplicate event class (same ID) in the same stream class: " - "id=%" PRId64, event_class->id); - ret = -EEXIST; - goto error; - } - - ctf_stream_class_append_event_class(stream_class, event_class); - event_class = NULL; - goto end; - -error: - ctf_event_class_destroy(event_class); - event_class = NULL; - - if (ret >= 0) { - ret = -1; - } - -end: - if (pop_scope) { - ctx_pop_scope(ctx); - } - - if (event_name) { - g_free(event_name); - } - - return ret; -} - -static -int auto_map_field_to_trace_clock_class(struct ctx *ctx, - struct ctf_field_class *fc) -{ - struct ctf_clock_class *clock_class_to_map_to = NULL; - struct ctf_field_class_int *int_fc = (void *) fc; - int ret = 0; - uint64_t clock_class_count; - - if (!fc) { - goto end; - } - - if (fc->type != CTF_FIELD_CLASS_TYPE_INT && - fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - goto end; - } - - if (int_fc->mapped_clock_class) { - /* Already mapped */ - goto end; - } - - clock_class_count = ctx->ctf_tc->clock_classes->len; - - switch (clock_class_count) { - case 0: - /* - * No clock class exists in the trace at this point. Create an - * implicit one at 1 GHz, named `default`, and use this clock - * class. - */ - clock_class_to_map_to = ctf_clock_class_create(); - BT_ASSERT(clock_class_to_map_to); - clock_class_to_map_to->frequency = UINT64_C(1000000000); - g_string_assign(clock_class_to_map_to->name, "default"); - BT_ASSERT(ret == 0); - g_ptr_array_add(ctx->ctf_tc->clock_classes, - clock_class_to_map_to); - break; - case 1: - /* - * Only one clock class exists in the trace at this point: use - * this one. - */ - clock_class_to_map_to = ctx->ctf_tc->clock_classes->pdata[0]; - break; - default: - /* - * Timestamp field not mapped to a clock class and there's more - * than one clock class in the trace: this is an error. - */ - BT_LOGE_STR("Timestamp field found with no mapped clock class, " - "but there's more than one clock class in the trace at this point."); - ret = -1; - goto end; - } - - BT_ASSERT(clock_class_to_map_to); - int_fc->mapped_clock_class = clock_class_to_map_to; - -end: - return ret; -} - -static -int auto_map_fields_to_trace_clock_class(struct ctx *ctx, - struct ctf_field_class *root_fc, const char *field_name) -{ - int ret = 0; - uint64_t i, count; - struct ctf_field_class_struct *struct_fc = (void *) root_fc; - struct ctf_field_class_variant *var_fc = (void *) root_fc; - - if (!root_fc) { - goto end; - } - - if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT && - root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) { - goto end; - } - - if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) { - count = struct_fc->members->len; - } else { - count = var_fc->options->len; - } - - for (i = 0; i < count; i++) { - struct ctf_named_field_class *named_fc = NULL; - - if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) { - named_fc = ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) { - named_fc = ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - } - - if (strcmp(named_fc->name->str, field_name) == 0) { - ret = auto_map_field_to_trace_clock_class(ctx, - named_fc->fc); - if (ret) { - BT_LOGE("Cannot automatically map field to trace's clock class: " - "field-name=\"%s\"", field_name); - goto end; - } - } - - ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc, - field_name); - if (ret) { - BT_LOGE("Cannot automatically map structure or variant field class's fields to trace's clock class: " - "field-name=\"%s\", root-field-name=\"%s\"", - field_name, named_fc->name->str); - goto end; - } - } - -end: - return ret; -} - -static -int visit_stream_decl_entry(struct ctx *ctx, struct ctf_node *node, - struct ctf_stream_class *stream_class, int *set) -{ - int ret = 0; - char *left = NULL; - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, - &node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class found in stream class."); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, - node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class alias found in stream class."); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "id")) { - int64_t id; - - if (_IS_SET(set, _STREAM_ID_SET)) { - _BT_LOGE_DUP_ATTR(node, "id", - "stream declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - (uint64_t *) &id); - - /* Only read "id" if get_unary_unsigned() succeeded. */ - if (ret || (!ret && id < 0)) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for stream class's `id` attribute."); - ret = -EINVAL; - goto error; - } - - if (ctf_trace_class_borrow_stream_class_by_id( - ctx->ctf_tc, id)) { - _BT_LOGE_NODE(node, - "Duplicate stream class (same ID): id=%" PRId64, - id); - ret = -EEXIST; - goto error; - } - - stream_class->id = id; - _SET(set, _STREAM_ID_SET); - } else if (!strcmp(left, "event.header")) { - if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `event.header` entry in stream class."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &stream_class->event_header_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create stream class's event header field class."); - goto error; - } - - BT_ASSERT(stream_class->event_header_fc); - ret = auto_map_fields_to_trace_clock_class(ctx, - stream_class->event_header_fc, "timestamp"); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class."); - goto error; - } - - _SET(set, _STREAM_EVENT_HEADER_SET); - } else if (!strcmp(left, "event.context")) { - if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `event.context` entry in stream class."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &stream_class->event_common_context_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create stream class's event context field class."); - goto error; - } - - BT_ASSERT(stream_class->event_common_context_fc); - _SET(set, _STREAM_EVENT_CONTEXT_SET); - } else if (!strcmp(left, "packet.context")) { - if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `packet.context` entry in stream class."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &stream_class->packet_context_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create stream class's packet context field class."); - goto error; - } - - BT_ASSERT(stream_class->packet_context_fc); - ret = auto_map_fields_to_trace_clock_class(ctx, - stream_class->packet_context_fc, - "timestamp_begin"); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class."); - goto error; - } - - ret = auto_map_fields_to_trace_clock_class(ctx, - stream_class->packet_context_fc, - "timestamp_end"); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class."); - goto error; - } - - _SET(set, _STREAM_PACKET_CONTEXT_SET); - } else { - _BT_LOGW_NODE(node, - "Unknown attribute in stream class: " - "attr-name=\"%s\"", left); - } - - g_free(left); - left = NULL; - break; - } - - default: - ret = -EPERM; - goto error; - } - - return 0; - -error: - g_free(left); - return ret; -} - -static -int visit_stream_decl(struct ctx *ctx, struct ctf_node *node) -{ - int set = 0; - int ret = 0; - struct ctf_node *iter; - struct ctf_stream_class *stream_class = NULL; - struct bt_list_head *decl_list = &node->u.stream.declaration_list; - - if (node->visited) { - goto end; - } - - node->visited = TRUE; - stream_class = ctf_stream_class_create(); - BT_ASSERT(stream_class); - _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_stream_decl_entry(ctx, iter, stream_class, &set); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit stream class's entry: " - "ret=%d", ret); - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (_IS_SET(&set, _STREAM_ID_SET)) { - /* Check that packet header has `stream_id` field */ - struct ctf_named_field_class *named_fc = NULL; - - if (!ctx->ctf_tc->packet_header_fc) { - _BT_LOGE_NODE(node, - "Stream class has a `id` attribute, " - "but trace has no packet header field class."); - goto error; - } - - named_fc = ctf_field_class_struct_borrow_member_by_name( - (void *) ctx->ctf_tc->packet_header_fc, "stream_id"); - if (!named_fc) { - _BT_LOGE_NODE(node, - "Stream class has a `id` attribute, " - "but trace's packet header field class has no `stream_id` field."); - goto error; - } - - if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT && - named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { - _BT_LOGE_NODE(node, - "Stream class has a `id` attribute, " - "but trace's packet header field class's `stream_id` field is not an integer field class."); - goto error; - } - } else { - /* Allow only _one_ ID-less stream */ - if (ctx->ctf_tc->stream_classes->len != 0) { - _BT_LOGE_NODE(node, - "Missing `id` attribute in stream class as there's more than one stream class in the trace."); - ret = -EPERM; - goto error; - } - - /* Automatic ID: 0 */ - stream_class->id = 0; - } - - /* - * Make sure that this stream class's ID is currently unique in - * the trace. - */ - if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, - stream_class->id)) { - _BT_LOGE_NODE(node, - "Duplicate stream class (same ID): id=%" PRId64, - stream_class->id); - ret = -EINVAL; - goto error; - } - - g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class); - stream_class = NULL; - goto end; - -error: - ctf_stream_class_destroy(stream_class); - stream_class = NULL; - -end: - return ret; -} - -static -int visit_trace_decl_entry(struct ctx *ctx, struct ctf_node *node, int *set) -{ - int ret = 0; - char *left = NULL; - uint64_t val; - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, - &node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class found in trace (`trace` block)."); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, - node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot add field class alias found in trace (`trace` block)."); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "major")) { - if (_IS_SET(set, _TRACE_MAJOR_SET)) { - _BT_LOGE_DUP_ATTR(node, "major", "trace"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &val); - if (ret) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for trace's `major` attribute."); - ret = -EINVAL; - goto error; - } - - if (val != 1) { - _BT_LOGE_NODE(node, - "Invalid trace's `minor` attribute: expecting 1."); - goto error; - } - - ctx->ctf_tc->major = val; - _SET(set, _TRACE_MAJOR_SET); - } else if (!strcmp(left, "minor")) { - if (_IS_SET(set, _TRACE_MINOR_SET)) { - _BT_LOGE_DUP_ATTR(node, "minor", "trace"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &val); - if (ret) { - _BT_LOGE_NODE(node, - "Unexpected unary expression for trace's `minor` attribute."); - ret = -EINVAL; - goto error; - } - - if (val != 8) { - _BT_LOGE_NODE(node, - "Invalid trace's `minor` attribute: expecting 8."); - goto error; - } - - ctx->ctf_tc->minor = val; - _SET(set, _TRACE_MINOR_SET); - } else if (!strcmp(left, "uuid")) { - if (_IS_SET(set, _TRACE_UUID_SET)) { - _BT_LOGE_DUP_ATTR(node, "uuid", "trace"); - ret = -EPERM; - goto error; - } - - ret = get_unary_uuid(&node->u.ctf_expression.right, - ctx->ctf_tc->uuid); - if (ret) { - _BT_LOGE_NODE(node, - "Invalid trace's `uuid` attribute."); - goto error; - } - - ctx->ctf_tc->is_uuid_set = true; - _SET(set, _TRACE_UUID_SET); - } else if (!strcmp(left, "byte_order")) { - /* Default byte order is already known at this stage */ - if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) { - _BT_LOGE_DUP_ATTR(node, "byte_order", - "trace"); - ret = -EPERM; - goto error; - } - - BT_ASSERT(ctx->ctf_tc->default_byte_order != -1); - _SET(set, _TRACE_BYTE_ORDER_SET); - } else if (!strcmp(left, "packet.header")) { - if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) { - _BT_LOGE_NODE(node, - "Duplicate `packet.header` entry in trace."); - ret = -EPERM; - goto error; - } - - ret = visit_field_class_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &ctx->ctf_tc->packet_header_fc); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot create trace's packet header field class."); - goto error; - } - - BT_ASSERT(ctx->ctf_tc->packet_header_fc); - _SET(set, _TRACE_PACKET_HEADER_SET); - } else { - _BT_LOGW_NODE(node, - "Unknown attribute in stream class: " - "attr-name=\"%s\"", left); - } - - g_free(left); - left = NULL; - break; - } - default: - _BT_LOGE_NODE(node, "Unknown expression in trace."); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - g_free(left); - return ret; -} - -static -int visit_trace_decl(struct ctx *ctx, struct ctf_node *node) -{ - int ret = 0; - int set = 0; - struct ctf_node *iter; - struct bt_list_head *decl_list = &node->u.trace.declaration_list; - - if (node->visited) { - goto end; - } - - node->visited = TRUE; - - if (ctx->is_trace_visited) { - _BT_LOGE_NODE(node, "Duplicate trace (`trace` block)."); - ret = -EEXIST; - goto error; - } - - _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_trace_decl_entry(ctx, iter, &set); - if (ret) { - _BT_LOGE_NODE(iter, "Cannot visit trace's entry (`trace` block): " - "ret=%d", ret); - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (!_IS_SET(&set, _TRACE_MAJOR_SET)) { - _BT_LOGE_NODE(node, - "Missing `major` attribute in trace (`trace` block)."); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _TRACE_MINOR_SET)) { - _BT_LOGE_NODE(node, - "Missing `minor` attribute in trace (`trace` block)."); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _BT_LOGE_NODE(node, - "Missing `byte_order` attribute in trace (`trace` block)."); - ret = -EPERM; - goto error; - } - - ctx->is_trace_visited = true; - -end: - return 0; - -error: - return ret; -} - -static -int visit_env(struct ctx *ctx, struct ctf_node *node) -{ - int ret = 0; - char *left = NULL; - struct ctf_node *entry_node; - struct bt_list_head *decl_list = &node->u.env.declaration_list; - - if (node->visited) { - goto end; - } - - node->visited = TRUE; - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - struct bt_list_head *right_head = - &entry_node->u.ctf_expression.right; - - if (entry_node->type != NODE_CTF_EXPRESSION) { - _BT_LOGE_NODE(entry_node, - "Wrong expression in environment entry: " - "node-type=%d", entry_node->type); - ret = -EPERM; - goto error; - } - - left = concatenate_unary_strings( - &entry_node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(entry_node, - "Cannot get environment entry's name."); - ret = -EINVAL; - goto error; - } - - if (is_unary_string(right_head)) { - char *right = concatenate_unary_strings(right_head); - - if (!right) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for environment entry's value: " - "name=\"%s\"", left); - ret = -EINVAL; - goto error; - } - - if (strcmp(left, "tracer_name") == 0) { - if (strncmp(right, "lttng", 5) == 0) { - BT_LOGI("Detected LTTng trace from `%s` environment value: " - "tracer-name=\"%s\"", - left, right); - ctx->is_lttng = true; - } - } - - ctf_trace_class_append_env_entry(ctx->ctf_tc, - left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR, - right, 0); - g_free(right); - } else if (is_unary_unsigned(right_head) || - is_unary_signed(right_head)) { - int64_t v; - - if (is_unary_unsigned(right_head)) { - ret = get_unary_unsigned(right_head, - (uint64_t *) &v); - } else { - ret = get_unary_signed(right_head, &v); - } - if (ret) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for environment entry's value: " - "name=\"%s\"", left); - ret = -EINVAL; - goto error; - } - - ctf_trace_class_append_env_entry(ctx->ctf_tc, - left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT, - NULL, v); - } else { - _BT_LOGW_NODE(entry_node, - "Environment entry has unknown type: " - "name=\"%s\"", left); - } - - g_free(left); - left = NULL; - } - -end: - return 0; - -error: - g_free(left); - return ret; -} - -static -int set_trace_byte_order(struct ctx *ctx, struct ctf_node *trace_node) -{ - int ret = 0; - int set = 0; - char *left = NULL; - struct ctf_node *node; - struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list; - - bt_list_for_each_entry(node, decl_list, siblings) { - if (node->type == NODE_CTF_EXPRESSION) { - struct ctf_node *right_node; - - left = concatenate_unary_strings( - &node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(node, - "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "byte_order")) { - enum ctf_byte_order bo; - - if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _BT_LOGE_DUP_ATTR(node, "byte_order", - "trace"); - ret = -EPERM; - goto error; - } - - _SET(&set, _TRACE_BYTE_ORDER_SET); - right_node = _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings); - bo = byte_order_from_unary_expr(right_node); - if (bo == -1) { - _BT_LOGE_NODE(node, - "Invalid `byte_order` attribute in trace (`trace` block): " - "expecting `le`, `be`, or `network`."); - ret = -EINVAL; - goto error; - } else if (bo == CTF_BYTE_ORDER_DEFAULT) { - _BT_LOGE_NODE(node, - "Invalid `byte_order` attribute in trace (`trace` block): " - "cannot be set to `native` here."); - ret = -EPERM; - goto error; - } - - ctx->ctf_tc->default_byte_order = bo; - } - - g_free(left); - left = NULL; - } - } - - if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _BT_LOGE_NODE(trace_node, - "Missing `byte_order` attribute in trace (`trace` block)."); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - g_free(left); - return ret; -} - -static -int visit_clock_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct ctf_clock_class *clock, int *set, int64_t *offset_seconds, - uint64_t *offset_cycles) -{ - int ret = 0; - char *left = NULL; - - if (entry_node->type != NODE_CTF_EXPRESSION) { - _BT_LOGE_NODE(entry_node, - "Unexpected node type: node-type=%d", - entry_node->type); - ret = -EPERM; - goto error; - } - - left = concatenate_unary_strings(&entry_node->u.ctf_expression.left); - if (!left) { - _BT_LOGE_NODE(entry_node, "Cannot concatenate unary strings."); - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "name")) { - char *right; - - if (_IS_SET(set, _CLOCK_NAME_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "name", "clock class"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &entry_node->u.ctf_expression.right); - if (!right) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `name` attribute."); - ret = -EINVAL; - goto error; - } - - g_string_assign(clock->name, right); - g_free(right); - _SET(set, _CLOCK_NAME_SET); - } else if (!strcmp(left, "uuid")) { - uint8_t uuid[BABELTRACE_UUID_LEN]; - - if (_IS_SET(set, _CLOCK_UUID_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "uuid", "clock class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_uuid(&entry_node->u.ctf_expression.right, uuid); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Invalid clock class's `uuid` attribute."); - goto error; - } - - clock->has_uuid = true; - memcpy(&clock->uuid[0], uuid, 16); - _SET(set, _CLOCK_UUID_SET); - } else if (!strcmp(left, "description")) { - char *right; - - if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "description", - "clock class"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &entry_node->u.ctf_expression.right); - if (!right) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `description` attribute."); - ret = -EINVAL; - goto error; - } - - g_string_assign(clock->description, right); - g_free(right); - _SET(set, _CLOCK_DESCRIPTION_SET); - } else if (!strcmp(left, "freq")) { - uint64_t freq = UINT64_C(-1); - - if (_IS_SET(set, _CLOCK_FREQ_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "freq", "clock class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &freq); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `freq` attribute."); - ret = -EINVAL; - goto error; - } - - if (freq == UINT64_C(-1) || freq == 0) { - _BT_LOGE_NODE(entry_node, - "Invalid clock class frequency: freq=%" PRIu64, - freq); - ret = -EINVAL; - goto error; - } - - clock->frequency = freq; - _SET(set, _CLOCK_FREQ_SET); - } else if (!strcmp(left, "precision")) { - uint64_t precision; - - if (_IS_SET(set, _CLOCK_PRECISION_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "precision", - "clock class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &precision); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `precision` attribute."); - ret = -EINVAL; - goto error; - } - - clock->precision = precision; - _SET(set, _CLOCK_PRECISION_SET); - } else if (!strcmp(left, "offset_s")) { - if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "offset_s", - "clock class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_signed( - &entry_node->u.ctf_expression.right, offset_seconds); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `offset_s` attribute."); - ret = -EINVAL; - goto error; - } - - _SET(set, _CLOCK_OFFSET_S_SET); - } else if (!strcmp(left, "offset")) { - if (_IS_SET(set, _CLOCK_OFFSET_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "offset", "clock class"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, offset_cycles); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `offset` attribute."); - ret = -EINVAL; - goto error; - } - - _SET(set, _CLOCK_OFFSET_SET); - } else if (!strcmp(left, "absolute")) { - struct ctf_node *right; - - if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) { - _BT_LOGE_DUP_ATTR(entry_node, "absolute", - "clock class"); - ret = -EPERM; - goto error; - } - - right = _BT_LIST_FIRST_ENTRY( - &entry_node->u.ctf_expression.right, - struct ctf_node, siblings); - ret = get_boolean(right); - if (ret < 0) { - _BT_LOGE_NODE(entry_node, - "Unexpected unary expression for clock class's `absolute` attribute."); - ret = -EINVAL; - goto error; - } - - clock->is_absolute = ret; - _SET(set, _CLOCK_ABSOLUTE_SET); - } else { - _BT_LOGW_NODE(entry_node, - "Unknown attribute in clock class: attr-name=\"%s\"", - left); - } - - g_free(left); - left = NULL; - return 0; - -error: - g_free(left); - return ret; -} - -static inline -uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns) -{ - uint64_t cycles; - - /* 1GHz */ - if (frequency == UINT64_C(1000000000)) { - cycles = ns; - } else { - cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9); - } - - return cycles; -} - -static -void calibrate_clock_class_offsets(int64_t *offset_seconds, - uint64_t *offset_cycles, uint64_t freq) -{ - if (*offset_cycles >= freq) { - const uint64_t s_in_offset_cycles = *offset_cycles / freq; - - *offset_seconds += (int64_t) s_in_offset_cycles; - *offset_cycles -= (s_in_offset_cycles * freq); - } -} - -static -void apply_clock_class_offset(struct ctx *ctx, - struct ctf_clock_class *clock) -{ - uint64_t freq; - int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s; - uint64_t offset_ns_to_apply; - int64_t cur_offset_s; - uint64_t cur_offset_cycles; - - if (ctx->decoder_config.clock_class_offset_s == 0 && - ctx->decoder_config.clock_class_offset_ns == 0) { - goto end; - } - - /* Transfer nanoseconds to seconds as much as possible */ - if (ctx->decoder_config.clock_class_offset_ns < 0) { - const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns; - const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1; - const int64_t extra_s = -abs_extra_s; - const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns - - (extra_s * INT64_C(1000000000)); - - BT_ASSERT(offset_ns > 0); - offset_ns_to_apply = (uint64_t) offset_ns; - offset_s_to_apply += extra_s; - } else { - const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns / - INT64_C(1000000000); - const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns - - (extra_s * INT64_C(1000000000)); - - BT_ASSERT(offset_ns >= 0); - offset_ns_to_apply = (uint64_t) offset_ns; - offset_s_to_apply += extra_s; - } - - freq = clock->frequency; - cur_offset_s = clock->offset_seconds; - cur_offset_cycles = clock->offset_cycles; - - /* Apply offsets */ - cur_offset_s += offset_s_to_apply; - cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply); - - /* - * Recalibrate offsets because the part in cycles can be greater - * than the frequency at this point. - */ - calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq); - - /* Set final offsets */ - clock->offset_seconds = cur_offset_s; - clock->offset_cycles = cur_offset_cycles; - -end: - return; -} - -static -int visit_clock_decl(struct ctx *ctx, struct ctf_node *clock_node) -{ - int ret = 0; - int set = 0; - struct ctf_clock_class *clock; - struct ctf_node *entry_node; - struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list; - const char *clock_class_name; - int64_t offset_seconds = 0; - uint64_t offset_cycles = 0; - uint64_t freq; - - if (clock_node->visited) { - return 0; - } - - clock_node->visited = TRUE; - - /* CTF 1.8's default frequency for a clock class is 1 GHz */ - clock = ctf_clock_class_create(); - if (!clock) { - _BT_LOGE_NODE(clock_node, - "Cannot create default clock class."); - ret = -ENOMEM; - goto end; - } - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - ret = visit_clock_decl_entry(ctx, entry_node, clock, &set, - &offset_seconds, &offset_cycles); - if (ret) { - _BT_LOGE_NODE(entry_node, - "Cannot visit clock class's entry: ret=%d", - ret); - goto end; - } - } - - if (!_IS_SET(&set, _CLOCK_NAME_SET)) { - _BT_LOGE_NODE(clock_node, - "Missing `name` attribute in clock class."); - ret = -EPERM; - goto end; - } - - clock_class_name = clock->name->str; - BT_ASSERT(clock_class_name); - if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) { - /* - * Old versions of LTTng forgot to set its clock class - * as absolute, even if it is. This is important because - * it's a condition to be able to sort messages - * from different sources. - */ - clock->is_absolute = true; - } - - /* - * Adjust offsets so that the part in cycles is less than the - * frequency (move to the part in seconds). - */ - freq = clock->frequency; - calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq); - BT_ASSERT(offset_cycles < clock->frequency); - clock->offset_seconds = offset_seconds; - clock->offset_cycles = offset_cycles; - apply_clock_class_offset(ctx, clock); - g_ptr_array_add(ctx->ctf_tc->clock_classes, clock); - clock = NULL; - -end: - if (clock) { - ctf_clock_class_destroy(clock); - } - - return ret; -} - -static -int visit_root_decl(struct ctx *ctx, struct ctf_node *root_decl_node) -{ - int ret = 0; - - if (root_decl_node->visited) { - goto end; - } - - root_decl_node->visited = TRUE; - - switch (root_decl_node->type) { - case NODE_TYPEDEF: - ret = visit_field_class_def(ctx, - root_decl_node->u.field_class_def.field_class_specifier_list, - &root_decl_node->u.field_class_def.field_class_declarators); - if (ret) { - _BT_LOGE_NODE(root_decl_node, - "Cannot add field class found in root scope."); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target, - root_decl_node->u.field_class_alias.alias); - if (ret) { - _BT_LOGE_NODE(root_decl_node, - "Cannot add field class alias found in root scope."); - goto end; - } - break; - case NODE_TYPE_SPECIFIER_LIST: - { - struct ctf_field_class *decl = NULL; - - /* - * Just add the field class specifier to the root - * declaration scope. Put local reference. - */ - ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl); - if (ret) { - _BT_LOGE_NODE(root_decl_node, - "Cannot visit root scope's field class: " - "ret=%d", ret); - BT_ASSERT(!decl); - goto end; - } - - ctf_field_class_destroy(decl); - decl = NULL; - break; - } - default: - _BT_LOGE_NODE(root_decl_node, - "Unexpected node type: node-type=%d", - root_decl_node->type); - ret = -EPERM; - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create( - bt_self_component_source *self_comp, - const struct ctf_metadata_decoder_config *decoder_config) -{ - struct ctx *ctx = NULL; - - /* Create visitor's context */ - ctx = ctx_create(self_comp, decoder_config); - if (!ctx) { - BT_LOGE_STR("Cannot create visitor's context."); - goto error; - } - - goto end; - -error: - ctx_destroy(ctx); - ctx = NULL; - -end: - return (void *) ctx; -} - -BT_HIDDEN -void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor) -{ - ctx_destroy((void *) visitor); -} - -BT_HIDDEN -bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class( - struct ctf_visitor_generate_ir *visitor) -{ - struct ctx *ctx = (void *) visitor; - - BT_ASSERT(ctx); - - if (ctx->trace_class) { - bt_trace_class_get_ref(ctx->trace_class); - } - - return ctx->trace_class; -} - -BT_HIDDEN -struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class( - struct ctf_visitor_generate_ir *visitor) -{ - struct ctx *ctx = (void *) visitor; - - BT_ASSERT(ctx); - BT_ASSERT(ctx->ctf_tc); - return ctx->ctf_tc; -} - -BT_HIDDEN -int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor, - struct ctf_node *node) -{ - int ret = 0; - struct ctx *ctx = (void *) visitor; - - BT_LOGI_STR("Visiting metadata's AST to generate CTF IR objects."); - - switch (node->type) { - case NODE_ROOT: - { - struct ctf_node *iter; - bool got_trace_decl = false; - - /* - * The first thing we need is the native byte order of - * the trace block, because early class aliases can have - * a `byte_order` attribute set to `native`. If we don't - * have the native byte order yet, and we don't have any - * trace block yet, then fail with EINCOMPLETE. - */ - if (ctx->ctf_tc->default_byte_order == -1) { - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - if (got_trace_decl) { - _BT_LOGE_NODE(node, - "Duplicate trace (`trace` block)."); - ret = -1; - goto end; - } - - ret = set_trace_byte_order(ctx, iter); - if (ret) { - _BT_LOGE_NODE(node, - "Cannot set trace's native byte order: " - "ret=%d", ret); - goto end; - } - - got_trace_decl = true; - } - - if (!got_trace_decl) { - BT_LOGD_STR("Incomplete AST: need trace (`trace` block)."); - ret = -EINCOMPLETE; - goto end; - } - } - - BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE || - ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG); - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* Environment */ - bt_list_for_each_entry(iter, &node->u.root.env, siblings) { - ret = visit_env(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit trace's environment (`env` block) entry: " - "ret=%d", ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* - * Visit clock blocks. - */ - bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { - ret = visit_clock_decl(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit clock class: ret=%d", - ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* - * Visit root declarations next, as they can be used by any - * following entity. - */ - bt_list_for_each_entry(iter, &node->u.root.declaration_list, - siblings) { - ret = visit_root_decl(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit root entry: ret=%d", - ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* Callsite blocks are not supported */ - bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { - _BT_LOGW_NODE(iter, - "\"callsite\" blocks are not supported as of this version."); - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* Trace */ - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - ret = visit_trace_decl(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit trace (`trace` block): " - "ret=%d", ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* Streams */ - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - ret = visit_stream_decl(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit stream class: ret=%d", - ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - - /* Events */ - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - ret = visit_event_decl(ctx, iter); - if (ret) { - _BT_LOGE_NODE(iter, - "Cannot visit event class: ret=%d", - ret); - goto end; - } - } - - BT_ASSERT(ctx->current_scope && - ctx->current_scope->parent_scope == NULL); - break; - } - default: - _BT_LOGE_NODE(node, - "Unexpected node type: node-type=%d", - node->type); - ret = -EINVAL; - goto end; - } - - /* Update default clock classes */ - ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* Update trace class meanings */ - ret = ctf_trace_class_update_meanings(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* Update stream class configuration */ - ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* Update text arrays and sequences */ - ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* Resolve sequence lengths and variant tags */ - ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - if (ctx->trace_class) { - /* - * Update "in IR" for field classes. - * - * If we have no IR trace class, then we'll have no way - * to create IR fields anyway, so we leave all the - * `in_ir` members false. - */ - ret = ctf_trace_class_update_in_ir(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - } - - /* Update saved value indexes */ - ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* Validate what we have so far */ - ret = ctf_trace_class_validate(ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - - /* - * If there are fields which are not related to the CTF format - * itself in the packet header and in event header field - * classes, warn about it because they are never translated. - */ - ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc); - - if (ctx->trace_class) { - /* Copy new CTF metadata -> new IR metadata */ - ret = ctf_trace_class_translate(ctx->self_comp, - ctx->trace_class, ctx->ctf_tc); - if (ret) { - ret = -EINVAL; - goto end; - } - } - -end: - return ret; -} diff --git a/plugins/ctf/common/metadata/visitor-parent-links.c b/plugins/ctf/common/metadata/visitor-parent-links.c deleted file mode 100644 index c163dc12..00000000 --- a/plugins/ctf/common/metadata/visitor-parent-links.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * ctf-visitor-parent-links.c - * - * Common Trace Format Metadata Parent Link Creator. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-PARENT-LINKS-VISITOR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scanner.h" -#include "parser.h" -#include "ast.h" - -static -int ctf_visitor_unary_expression(int depth, struct ctf_node *node) -{ - int ret = 0; - - switch (node->u.unary_expression.link) { - case UNARY_LINK_UNKNOWN: - case UNARY_DOTLINK: - case UNARY_ARROWLINK: - case UNARY_DOTDOTDOT: - break; - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown expression link type: type=%d\n", - node->u.unary_expression.link); - return -EINVAL; - } - - switch (node->u.unary_expression.type) { - case UNARY_STRING: - case UNARY_SIGNED_CONSTANT: - case UNARY_UNSIGNED_CONSTANT: - break; - case UNARY_SBRAC: - node->u.unary_expression.u.sbrac_exp->parent = node; - ret = ctf_visitor_unary_expression(depth + 1, - node->u.unary_expression.u.sbrac_exp); - if (ret) - return ret; - break; - - case UNARY_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown expression link type: type=%d\n", - node->u.unary_expression.link); - return -EINVAL; - } - return 0; -} - -static -int ctf_visitor_type_specifier(int depth, struct ctf_node *node) -{ - int ret; - - switch (node->u.field_class_specifier.type) { - case TYPESPEC_VOID: - case TYPESPEC_CHAR: - case TYPESPEC_SHORT: - case TYPESPEC_INT: - case TYPESPEC_LONG: - case TYPESPEC_FLOAT: - case TYPESPEC_DOUBLE: - case TYPESPEC_SIGNED: - case TYPESPEC_UNSIGNED: - case TYPESPEC_BOOL: - case TYPESPEC_COMPLEX: - case TYPESPEC_IMAGINARY: - case TYPESPEC_CONST: - case TYPESPEC_ID_TYPE: - break; - case TYPESPEC_FLOATING_POINT: - case TYPESPEC_INTEGER: - case TYPESPEC_STRING: - case TYPESPEC_STRUCT: - case TYPESPEC_VARIANT: - case TYPESPEC_ENUM: - node->u.field_class_specifier.node->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_specifier.node); - if (ret) - return ret; - break; - - case TYPESPEC_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown type specifier: type=%d\n", - node->u.field_class_specifier.type); - return -EINVAL; - } - return 0; -} - -static -int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - depth++; - - bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - - switch (node->u.field_class_declarator.type) { - case TYPEDEC_ID: - break; - case TYPEDEC_NESTED: - if (node->u.field_class_declarator.u.nested.field_class_declarator) { - node->u.field_class_declarator.u.nested.field_class_declarator->parent = node; - ret = ctf_visitor_parent_links(depth + 1, - node->u.field_class_declarator.u.nested.field_class_declarator); - if (ret) - return ret; - } - if (!node->u.field_class_declarator.u.nested.abstract_array) { - bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - } - if (node->u.field_class_declarator.bitfield_len) { - node->u.field_class_declarator.bitfield_len = node; - ret = ctf_visitor_parent_links(depth + 1, - node->u.field_class_declarator.bitfield_len); - if (ret) - return ret; - } - break; - case TYPEDEC_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown type declarator: type=%d\n", - node->u.field_class_declarator.type); - return -EINVAL; - } - depth--; - return 0; -} - -int ctf_visitor_parent_links(int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - if (node->visited) - return 0; - - switch (node->type) { - case NODE_ROOT: - bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_EVENT: - bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_STREAM: - bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENV: - bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_TRACE: - bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_CLOCK: - bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_CALLSITE: - bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_CTF_EXPRESSION: - depth++; - bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_UNARY_EXPRESSION: - return ctf_visitor_unary_expression(depth, node); - - case NODE_TYPEDEF: - depth++; - node->u.field_class_def.field_class_specifier_list->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_def.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS_TARGET: - depth++; - node->u.field_class_alias_target.field_class_specifier_list->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias_target.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS_ALIAS: - depth++; - node->u.field_class_alias_name.field_class_specifier_list->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias_name.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS: - node->u.field_class_alias.target->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.target); - if (ret) - return ret; - node->u.field_class_alias.alias->parent = node; - ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.alias); - if (ret) - return ret; - break; - - case NODE_TYPE_SPECIFIER_LIST: - bt_list_for_each_entry(iter, &node->u.field_class_specifier_list.head, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_TYPE_SPECIFIER: - ret = ctf_visitor_type_specifier(depth, node); - if (ret) - return ret; - break; - case NODE_POINTER: - break; - case NODE_TYPE_DECLARATOR: - ret = ctf_visitor_field_class_declarator(depth, node); - if (ret) - return ret; - break; - - case NODE_FLOATING_POINT: - bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_INTEGER: - bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_STRING: - bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENUMERATOR: - bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENUM: - depth++; - if (node->u._enum.container_field_class) { - ret = ctf_visitor_parent_links(depth + 1, node->u._enum.container_field_class); - if (ret) - return ret; - } - - bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node; - ret = ctf_visitor_parent_links(depth + 1, - node->u.struct_or_variant_declaration.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_VARIANT: - bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_STRUCT: - bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u._struct.min_align, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown node type: type=%d\n", node->type); - return -EINVAL; - } - return ret; -} diff --git a/plugins/ctf/common/metadata/visitor-semantic-validator.c b/plugins/ctf/common/metadata/visitor-semantic-validator.c deleted file mode 100644 index 0149d615..00000000 --- a/plugins/ctf/common/metadata/visitor-semantic-validator.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * ctf-visitor-semantic-validator.c - * - * Common Trace Format Metadata Semantic Validator. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-METADATA-SEMANTIC-VALIDATOR-VISITOR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scanner.h" -#include "parser.h" -#include "ast.h" - -#define _bt_list_first_entry(ptr, type, member) \ - bt_list_entry((ptr)->next, type, member) - -static -int _ctf_visitor_semantic_check(int depth, struct ctf_node *node); - -static -int ctf_visitor_unary_expression(int depth, struct ctf_node *node) -{ - struct ctf_node *iter; - int is_ctf_exp = 0, is_ctf_exp_left = 0; - - switch (node->parent->type) { - case NODE_CTF_EXPRESSION: - is_ctf_exp = 1; - bt_list_for_each_entry(iter, &node->parent->u.ctf_expression.left, - siblings) { - if (iter == node) { - is_ctf_exp_left = 1; - /* - * We are a left child of a ctf expression. - * We are only allowed to be a string. - */ - if (node->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_LINENO(node->lineno, - "Left child of a CTF expression is only allowed to be a string."); - goto errperm; - } - break; - } - } - /* Right child of a ctf expression can be any type of unary exp. */ - break; /* OK */ - case NODE_TYPE_DECLARATOR: - /* - * We are the length of a type declarator. - */ - switch (node->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - case UNARY_STRING: - break; - default: - _BT_LOGE_LINENO(node->lineno, - "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`)."); - goto errperm; - } - break; /* OK */ - - case NODE_STRUCT: - /* - * We are the size of a struct align attribute. - */ - switch (node->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - break; - default: - _BT_LOGE_LINENO(node->lineno, - "Structure alignment attribute can only be an unsigned numeric constant."); - goto errperm; - } - break; - - case NODE_ENUMERATOR: - /* The enumerator's parent has validated its validity already. */ - break; /* OK */ - - case NODE_UNARY_EXPRESSION: - /* - * We disallow nested unary expressions and "sbrac" unary - * expressions. - */ - _BT_LOGE_LINENO(node->lineno, - "Nested unary expressions not allowed (`()` and `[]`)."); - goto errperm; - - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_POINTER: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_VARIANT: - default: - goto errinval; - } - - switch (node->u.unary_expression.link) { - case UNARY_LINK_UNKNOWN: - /* We don't allow empty link except on the first node of the list */ - if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ? - &node->parent->u.ctf_expression.left : - &node->parent->u.ctf_expression.right, - struct ctf_node, - siblings) != node) { - _BT_LOGE_LINENO(node->lineno, - "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`)."); - goto errperm; - } - break; /* OK */ - case UNARY_DOTLINK: - case UNARY_ARROWLINK: - /* We only allow -> and . links between children of ctf_expression. */ - if (node->parent->type != NODE_CTF_EXPRESSION) { - _BT_LOGE_LINENO(node->lineno, - "Links `.` and `->` are only allowed as children of CTF expression."); - goto errperm; - } - /* - * Only strings can be separated linked by . or ->. - * This includes "", '' and non-quoted identifiers. - */ - if (node->u.unary_expression.type != UNARY_STRING) { - _BT_LOGE_LINENO(node->lineno, - "Links `.` and `->` are only allowed to separate strings and identifiers."); - goto errperm; - } - /* We don't allow link on the first node of the list */ - if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ? - &node->parent->u.ctf_expression.left : - &node->parent->u.ctf_expression.right, - struct ctf_node, - siblings) == node) { - _BT_LOGE_LINENO(node->lineno, - "Links `.` and `->` are not allowed before first node of the unary expression list."); - goto errperm; - } - break; - case UNARY_DOTDOTDOT: - /* We only allow ... link between children of enumerator. */ - if (node->parent->type != NODE_ENUMERATOR) { - _BT_LOGE_LINENO(node->lineno, - "Link `...` is only allowed within enumerator."); - goto errperm; - } - /* We don't allow link on the first node of the list */ - if (_bt_list_first_entry(&node->parent->u.enumerator.values, - struct ctf_node, - siblings) == node) { - _BT_LOGE_LINENO(node->lineno, - "Link `...` is not allowed on the first node of the unary expression list."); - goto errperm; - } - break; - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown expression link type: type=%d", - node->u.unary_expression.link); - return -EINVAL; - } - return 0; - -errinval: - _BT_LOGE_LINENO(node->lineno, - "Incoherent parent node's type: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EINVAL; /* Incoherent structure */ - -errperm: - _BT_LOGE_LINENO(node->lineno, - "Semantic error: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EPERM; /* Structure not allowed */ -} - -static -int ctf_visitor_field_class_specifier_list(int depth, struct ctf_node *node) -{ - switch (node->parent->type) { - case NODE_CTF_EXPRESSION: - case NODE_TYPE_DECLARATOR: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_ROOT: - break; /* OK */ - - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUMERATOR: - case NODE_VARIANT: - case NODE_STRUCT: - default: - goto errinval; - } - return 0; -errinval: - _BT_LOGE_LINENO(node->lineno, - "Incoherent parent node's type: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EINVAL; /* Incoherent structure */ -} - -static -int ctf_visitor_field_class_specifier(int depth, struct ctf_node *node) -{ - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER_LIST: - break; /* OK */ - - case NODE_CTF_EXPRESSION: - case NODE_TYPE_DECLARATOR: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_ENUM: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_POINTER: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUMERATOR: - case NODE_VARIANT: - case NODE_STRUCT: - default: - goto errinval; - } - return 0; -errinval: - _BT_LOGE_LINENO(node->lineno, - "Incoherent parent node's type: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EINVAL; /* Incoherent structure */ -} - -static -int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - depth++; - - switch (node->parent->type) { - case NODE_TYPE_DECLARATOR: - /* - * A nested field class declarator is not allowed to - * contain pointers. - */ - if (!bt_list_empty(&node->u.field_class_declarator.pointers)) - goto errperm; - break; /* OK */ - case NODE_TYPEALIAS_TARGET: - break; /* OK */ - case NODE_TYPEALIAS_ALIAS: - /* - * Only accept alias name containing: - * - identifier - * - identifier * (any number of pointers) - * NOT accepting alias names containing [] (would otherwise - * cause semantic clash for later declarations of - * arrays/sequences of elements, where elements could be - * arrays/sequences themselves (if allowed in field class alias). - * NOT accepting alias with identifier. The declarator should - * be either empty or contain pointer(s). - */ - if (node->u.field_class_declarator.type == TYPEDEC_NESTED) - goto errperm; - bt_list_for_each_entry(iter, &node->parent->u.field_class_alias_name.field_class_specifier_list->u.field_class_specifier_list.head, - siblings) { - switch (iter->u.field_class_specifier.type) { - case TYPESPEC_FLOATING_POINT: - case TYPESPEC_INTEGER: - case TYPESPEC_STRING: - case TYPESPEC_STRUCT: - case TYPESPEC_VARIANT: - case TYPESPEC_ENUM: - if (bt_list_empty(&node->u.field_class_declarator.pointers)) - goto errperm; - break; - default: - break; - } - } - if (node->u.field_class_declarator.type == TYPEDEC_ID && - node->u.field_class_declarator.u.id != NULL) - goto errperm; - break; /* OK */ - case NODE_TYPEDEF: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - break; /* OK */ - - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_CTF_EXPRESSION: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_POINTER: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_VARIANT: - case NODE_STRUCT: - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers, - siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - - switch (node->u.field_class_declarator.type) { - case TYPEDEC_ID: - break; - case TYPEDEC_NESTED: - { - if (node->u.field_class_declarator.u.nested.field_class_declarator) { - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.field_class_declarator.u.nested.field_class_declarator); - if (ret) - return ret; - } - if (!node->u.field_class_declarator.u.nested.abstract_array) { - bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length, - siblings) { - if (iter->type != NODE_UNARY_EXPRESSION) { - _BT_LOGE_LINENO(node->lineno, - "Expecting unary expression as length: node-type=%s", - node_type(iter)); - return -EINVAL; - } - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - } else { - if (node->parent->type == NODE_TYPEALIAS_TARGET) { - _BT_LOGE_LINENO(node->lineno, - "Abstract array declarator not permitted as target of field class alias."); - return -EINVAL; - } - } - if (node->u.field_class_declarator.bitfield_len) { - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.field_class_declarator.bitfield_len); - if (ret) - return ret; - } - break; - } - case TYPEDEC_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown field class declarator: type=%d", - node->u.field_class_declarator.type); - return -EINVAL; - } - depth--; - return 0; - -errinval: - _BT_LOGE_LINENO(node->lineno, - "Incoherent parent node's type: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EINVAL; /* Incoherent structure */ - -errperm: - _BT_LOGE_LINENO(node->lineno, - "Semantic error: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EPERM; /* Structure not allowed */ -} - -static -int _ctf_visitor_semantic_check(int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - if (node->visited) - return 0; - - switch (node->type) { - case NODE_ROOT: - bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_EVENT: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_STREAM: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENV: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_TRACE: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_CLOCK: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_CALLSITE: - switch (node->parent->type) { - case NODE_ROOT: - break; /* OK */ - default: - goto errinval; - } - - bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_CTF_EXPRESSION: - switch (node->parent->type) { - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_ENV: - case NODE_TRACE: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - break; /* OK */ - - case NODE_CTF_EXPRESSION: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_TYPEALIAS: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_VARIANT: - case NODE_STRUCT: - default: - goto errinval; - } - - depth++; - bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_UNARY_EXPRESSION: - return ctf_visitor_unary_expression(depth, node); - - case NODE_TYPEDEF: - switch (node->parent->type) { - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_TRACE: - case NODE_VARIANT: - case NODE_STRUCT: - break; /* OK */ - - case NODE_CTF_EXPRESSION: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_ENV: - default: - goto errinval; - } - - depth++; - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.field_class_def.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS_TARGET: - { - int nr_declarators; - - switch (node->parent->type) { - case NODE_TYPEALIAS: - break; /* OK */ - default: - goto errinval; - } - - depth++; - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.field_class_alias_target.field_class_specifier_list); - if (ret) - return ret; - nr_declarators = 0; - bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - nr_declarators++; - } - if (nr_declarators > 1) { - _BT_LOGE_LINENO(node->lineno, - "Too many declarators in field class alias's name (maximum is 1): count=%d", - nr_declarators); - return -EINVAL; - } - depth--; - break; - } - case NODE_TYPEALIAS_ALIAS: - { - int nr_declarators; - - switch (node->parent->type) { - case NODE_TYPEALIAS: - break; /* OK */ - default: - goto errinval; - } - - depth++; - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.field_class_alias_name.field_class_specifier_list); - if (ret) - return ret; - nr_declarators = 0; - bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - nr_declarators++; - } - if (nr_declarators > 1) { - _BT_LOGE_LINENO(node->lineno, - "Too many declarators in field class alias's name (maximum is 1): count=%d", - nr_declarators); - return -EINVAL; - } - depth--; - break; - } - case NODE_TYPEALIAS: - switch (node->parent->type) { - case NODE_ROOT: - case NODE_EVENT: - case NODE_STREAM: - case NODE_TRACE: - case NODE_VARIANT: - case NODE_STRUCT: - break; /* OK */ - - case NODE_CTF_EXPRESSION: - case NODE_UNARY_EXPRESSION: - case NODE_TYPEDEF: - case NODE_TYPEALIAS_TARGET: - case NODE_TYPEALIAS_ALIAS: - case NODE_TYPEALIAS: - case NODE_STRUCT_OR_VARIANT_DECLARATION: - case NODE_TYPE_SPECIFIER: - case NODE_TYPE_SPECIFIER_LIST: - case NODE_POINTER: - case NODE_TYPE_DECLARATOR: - case NODE_FLOATING_POINT: - case NODE_INTEGER: - case NODE_STRING: - case NODE_ENUMERATOR: - case NODE_ENUM: - case NODE_CLOCK: - case NODE_CALLSITE: - case NODE_ENV: - default: - goto errinval; - } - - ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.target); - if (ret) - return ret; - ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.alias); - if (ret) - return ret; - break; - - case NODE_TYPE_SPECIFIER_LIST: - ret = ctf_visitor_field_class_specifier_list(depth, node); - if (ret) - return ret; - break; - case NODE_TYPE_SPECIFIER: - ret = ctf_visitor_field_class_specifier(depth, node); - if (ret) - return ret; - break; - case NODE_POINTER: - switch (node->parent->type) { - case NODE_TYPE_DECLARATOR: - break; /* OK */ - default: - goto errinval; - } - break; - case NODE_TYPE_DECLARATOR: - ret = ctf_visitor_field_class_declarator(depth, node); - if (ret) - return ret; - break; - - case NODE_FLOATING_POINT: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - case NODE_UNARY_EXPRESSION: - goto errperm; - } - bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_INTEGER: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - } - - bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_STRING: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - case NODE_UNARY_EXPRESSION: - goto errperm; - } - - bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENUMERATOR: - switch (node->parent->type) { - case NODE_ENUM: - break; - default: - goto errinval; - } - /* - * Enumerators are only allows to contain: - * numeric unary expression - * or num. unary exp. ... num. unary exp - */ - { - int count = 0; - - bt_list_for_each_entry(iter, &node->u.enumerator.values, - siblings) { - switch (count++) { - case 0: if (iter->type != NODE_UNARY_EXPRESSION - || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT - && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) - || iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) { - _BT_LOGE_LINENO(iter->lineno, - "First unary expression of enumerator is unexpected."); - goto errperm; - } - break; - case 1: if (iter->type != NODE_UNARY_EXPRESSION - || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT - && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) - || iter->u.unary_expression.link != UNARY_DOTDOTDOT) { - _BT_LOGE_LINENO(iter->lineno, - "Second unary expression of enumerator is unexpected."); - goto errperm; - } - break; - default: - goto errperm; - } - } - } - - bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENUM: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - case NODE_UNARY_EXPRESSION: - goto errperm; - } - - depth++; - ret = _ctf_visitor_semantic_check(depth + 1, node->u._enum.container_field_class); - if (ret) - return ret; - - bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - switch (node->parent->type) { - case NODE_STRUCT: - case NODE_VARIANT: - break; - default: - goto errinval; - } - ret = _ctf_visitor_semantic_check(depth + 1, - node->u.struct_or_variant_declaration.field_class_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_VARIANT: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - case NODE_UNARY_EXPRESSION: - goto errperm; - } - bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_STRUCT: - switch (node->parent->type) { - case NODE_TYPE_SPECIFIER: - break; /* OK */ - default: - goto errinval; - - case NODE_UNARY_EXPRESSION: - goto errperm; - } - bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) { - ret = _ctf_visitor_semantic_check(depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_UNKNOWN: - default: - _BT_LOGE_LINENO(node->lineno, - "Unknown node type: type=%d", node->type); - return -EINVAL; - } - return ret; - -errinval: - _BT_LOGE_LINENO(node->lineno, - "Incoherent parent node's type: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EINVAL; /* Incoherent structure */ - -errperm: - _BT_LOGE_LINENO(node->lineno, - "Semantic error: node-type=%s, parent-node-type=%s", - node_type(node), node_type(node->parent)); - return -EPERM; /* Structure not allowed */ -} - -int ctf_visitor_semantic_check(int depth, struct ctf_node *node) -{ - int ret = 0; - - /* - * First make sure we create the parent links for all children. Let's - * take the safe route and recreate them at each validation, just in - * case the structure has changed. - */ - ret = ctf_visitor_parent_links(depth, node); - if (ret) { - _BT_LOGE_LINENO(node->lineno, - "Cannot create parent links in metadata's AST: " - "ret=%d", ret); - goto end; - } - - ret = _ctf_visitor_semantic_check(depth, node); - if (ret) { - _BT_LOGE_LINENO(node->lineno, - "Cannot check metadata's AST semantics: " - "ret=%d", ret); - goto end; - } - -end: - return ret; -} diff --git a/plugins/ctf/common/msg-iter/Makefile.am b/plugins/ctf/common/msg-iter/Makefile.am deleted file mode 100644 index 7c3c2087..00000000 --- a/plugins/ctf/common/msg-iter/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -noinst_LTLIBRARIES = libctf-msg-iter.la - -libctf_msg_iter_la_SOURCES = \ - msg-iter.c \ - msg-iter.h \ - logging.c \ - logging.h diff --git a/plugins/ctf/common/msg-iter/logging.c b/plugins/ctf/common/msg-iter/logging.c deleted file mode 100644 index 3b9b2850..00000000 --- a/plugins/ctf/common/msg-iter/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL ctf_msg_iter_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(ctf_msg_iter_log_level, - "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL"); diff --git a/plugins/ctf/common/msg-iter/logging.h b/plugins/ctf/common/msg-iter/logging.h deleted file mode 100644 index 7e41dd98..00000000 --- a/plugins/ctf/common/msg-iter/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CTF_MSG_ITER_LOGGING_H -#define CTF_MSG_ITER_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL ctf_msg_iter_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_msg_iter_log_level); - -#endif /* CTF_MSG_ITER_LOGGING_H */ diff --git a/plugins/ctf/common/msg-iter/msg-iter.c b/plugins/ctf/common/msg-iter/msg-iter.c deleted file mode 100644 index 0671d9c3..00000000 --- a/plugins/ctf/common/msg-iter/msg-iter.c +++ /dev/null @@ -1,3046 +0,0 @@ -/* - * Babeltrace - CTF message iterator - * - * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2018 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-MSG-ITER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "msg-iter.h" -#include "../bfcr/bfcr.h" - -struct bt_msg_iter; - -/* A visit stack entry */ -struct stack_entry { - /* - * Current base field, one of: - * - * * string - * * structure - * * array - * * sequence - * * variant - * - * Field is borrowed. - */ - bt_field *base; - - /* Index of next field to set */ - size_t index; -}; - -/* Visit stack */ -struct stack { - /* Entries (struct stack_entry) */ - GArray *entries; - - /* Number of active entries */ - size_t size; -}; - -/* State */ -enum state { - STATE_INIT, - STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN, - STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, - STATE_AFTER_TRACE_PACKET_HEADER, - STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN, - STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, - STATE_AFTER_STREAM_PACKET_CONTEXT, - STATE_CHECK_EMIT_MSG_STREAM_BEGINNING, - STATE_EMIT_MSG_STREAM_BEGINNING, - STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING, - STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS, - STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS, - STATE_EMIT_MSG_DISCARDED_EVENTS, - STATE_EMIT_MSG_DISCARDED_PACKETS, - STATE_EMIT_MSG_PACKET_BEGINNING, - STATE_DSCOPE_EVENT_HEADER_BEGIN, - STATE_DSCOPE_EVENT_HEADER_CONTINUE, - STATE_AFTER_EVENT_HEADER, - STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN, - STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, - STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN, - STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, - STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, - STATE_EMIT_MSG_EVENT, - STATE_SKIP_PACKET_PADDING, - STATE_EMIT_MSG_PACKET_END_MULTI, - STATE_EMIT_MSG_PACKET_END_SINGLE, - STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END, - STATE_EMIT_MSG_STREAM_ACTIVITY_END, - STATE_EMIT_MSG_STREAM_END, - STATE_DONE, -}; - -struct end_of_packet_snapshots { - uint64_t discarded_events; - uint64_t packets; - uint64_t beginning_clock; - uint64_t end_clock; -}; - -/* CTF message iterator */ -struct bt_msg_iter { - /* Visit stack */ - struct stack *stack; - - /* Current message iterator to create messages (weak) */ - bt_self_message_iterator *msg_iter; - - /* - * True to emit stream beginning and stream activity beginning - * messages. - */ - bool emit_stream_begin_msg; - - /* True to emit stream end and stream activity end messages */ - bool emit_stream_end_msg; - - /* True to set the stream */ - bool set_stream; - - /* - * Current dynamic scope field pointer. - * - * This is set by read_dscope_begin_state() and contains the - * value of one of the pointers in `dscopes` below. - */ - bt_field *cur_dscope_field; - - /* - * True if we're done filling a string field from a text - * array/sequence payload. - */ - bool done_filling_string; - - /* Trace and classes */ - /* True to set IR fields */ - bool set_ir_fields; - - struct { - struct ctf_trace_class *tc; - struct ctf_stream_class *sc; - struct ctf_event_class *ec; - } meta; - - /* Current packet context field wrapper (NULL if not created yet) */ - bt_packet_context_field *packet_context_field; - - /* Current packet (NULL if not created yet) */ - bt_packet *packet; - - /* Current stream (NULL if not set yet) */ - bt_stream *stream; - - /* Current event (NULL if not created yet) */ - bt_event *event; - - /* Current event message (NULL if not created yet) */ - bt_message *event_msg; - - /* Database of current dynamic scopes */ - struct { - bt_field *stream_packet_context; - bt_field *event_common_context; - bt_field *event_spec_context; - bt_field *event_payload; - } dscopes; - - /* Current state */ - enum state state; - - /* Current medium buffer data */ - struct { - /* Last address provided by medium */ - const uint8_t *addr; - - /* Buffer size provided by medium (bytes) */ - size_t sz; - - /* Offset within whole packet of addr (bits) */ - size_t packet_offset; - - /* Current position from addr (bits) */ - size_t at; - - /* Position of the last event header from addr (bits) */ - size_t last_eh_at; - } buf; - - /* Binary type reader */ - struct bt_bfcr *bfcr; - - /* Current medium data */ - struct { - struct bt_msg_iter_medium_ops medops; - size_t max_request_sz; - void *data; - } medium; - - /* Current packet size (bits) (-1 if unknown) */ - int64_t cur_exp_packet_total_size; - - /* Current content size (bits) (-1 if unknown) */ - int64_t cur_exp_packet_content_size; - - /* Current stream class ID */ - int64_t cur_stream_class_id; - - /* Current event class ID */ - int64_t cur_event_class_id; - - /* Current data stream ID */ - int64_t cur_data_stream_id; - - /* - * Offset, in the underlying media, of the current packet's - * start (-1 if unknown). - */ - off_t cur_packet_offset; - - /* Default clock's current value */ - uint64_t default_clock_snapshot; - - /* End of current packet snapshots */ - struct end_of_packet_snapshots snapshots; - - /* End of previous packet snapshots */ - struct end_of_packet_snapshots prev_packet_snapshots; - - /* Stored values (for sequence lengths, variant tags) */ - GArray *stored_values; -}; - -static inline -const char *state_string(enum state state) -{ - switch (state) { - case STATE_INIT: - return "STATE_INIT"; - case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: - return "STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN"; - case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: - return "STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE"; - case STATE_AFTER_TRACE_PACKET_HEADER: - return "STATE_AFTER_TRACE_PACKET_HEADER"; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: - return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN"; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: - return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE"; - case STATE_AFTER_STREAM_PACKET_CONTEXT: - return "STATE_AFTER_STREAM_PACKET_CONTEXT"; - case STATE_EMIT_MSG_STREAM_BEGINNING: - return "STATE_EMIT_MSG_STREAM_BEGINNING"; - case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: - return "STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING"; - case STATE_EMIT_MSG_PACKET_BEGINNING: - return "STATE_EMIT_MSG_PACKET_BEGINNING"; - case STATE_EMIT_MSG_DISCARDED_EVENTS: - return "STATE_EMIT_MSG_DISCARDED_EVENTS"; - case STATE_EMIT_MSG_DISCARDED_PACKETS: - return "STATE_EMIT_MSG_DISCARDED_PACKETS"; - case STATE_DSCOPE_EVENT_HEADER_BEGIN: - return "STATE_DSCOPE_EVENT_HEADER_BEGIN"; - case STATE_DSCOPE_EVENT_HEADER_CONTINUE: - return "STATE_DSCOPE_EVENT_HEADER_CONTINUE"; - case STATE_AFTER_EVENT_HEADER: - return "STATE_AFTER_EVENT_HEADER"; - case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN: - return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN"; - case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE: - return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE"; - case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN: - return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN"; - case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE: - return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE"; - case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: - return "STATE_DSCOPE_EVENT_PAYLOAD_BEGIN"; - case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: - return "STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE"; - case STATE_EMIT_MSG_EVENT: - return "STATE_EMIT_MSG_EVENT"; - case STATE_SKIP_PACKET_PADDING: - return "STATE_SKIP_PACKET_PADDING"; - case STATE_EMIT_MSG_PACKET_END_MULTI: - return "STATE_EMIT_MSG_PACKET_END_MULTI"; - case STATE_EMIT_MSG_PACKET_END_SINGLE: - return "STATE_EMIT_MSG_PACKET_END_SINGLE"; - case STATE_EMIT_MSG_STREAM_ACTIVITY_END: - return "STATE_EMIT_MSG_STREAM_ACTIVITY_END"; - case STATE_EMIT_MSG_STREAM_END: - return "STATE_EMIT_MSG_STREAM_END"; - case STATE_DONE: - return "STATE_DONE"; - default: - return "(unknown)"; - } -} - -static -int bt_msg_iter_switch_packet(struct bt_msg_iter *notit); - -static -struct stack *stack_new(struct bt_msg_iter *notit) -{ - struct stack *stack = NULL; - - stack = g_new0(struct stack, 1); - if (!stack) { - BT_LOGE_STR("Failed to allocate one stack."); - goto error; - } - - stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry)); - if (!stack->entries) { - BT_LOGE_STR("Failed to allocate a GArray."); - goto error; - } - - BT_LOGD("Created stack: notit-addr=%p, stack-addr=%p", notit, stack); - goto end; - -error: - g_free(stack); - stack = NULL; - -end: - return stack; -} - -static -void stack_destroy(struct stack *stack) -{ - BT_ASSERT(stack); - BT_LOGD("Destroying stack: addr=%p", stack); - - if (stack->entries) { - g_array_free(stack->entries, TRUE); - } - - g_free(stack); -} - -static -void stack_push(struct stack *stack, bt_field *base) -{ - struct stack_entry *entry; - - BT_ASSERT(stack); - BT_ASSERT(base); - BT_LOGV("Pushing base field on stack: stack-addr=%p, " - "stack-size-before=%zu, stack-size-after=%zu", - stack, stack->size, stack->size + 1); - - if (stack->entries->len == stack->size) { - g_array_set_size(stack->entries, stack->size + 1); - } - - entry = &g_array_index(stack->entries, struct stack_entry, stack->size); - entry->base = base; - entry->index = 0; - stack->size++; -} - -static inline -unsigned int stack_size(struct stack *stack) -{ - BT_ASSERT(stack); - return stack->size; -} - -static -void stack_pop(struct stack *stack) -{ - BT_ASSERT(stack); - BT_ASSERT(stack_size(stack)); - BT_LOGV("Popping from stack: " - "stack-addr=%p, stack-size-before=%zu, stack-size-after=%zu", - stack, stack->size, stack->size - 1); - stack->size--; -} - -static inline -struct stack_entry *stack_top(struct stack *stack) -{ - BT_ASSERT(stack); - BT_ASSERT(stack_size(stack)); - return &g_array_index(stack->entries, struct stack_entry, - stack->size - 1); -} - -static inline -bool stack_empty(struct stack *stack) -{ - return stack_size(stack) == 0; -} - -static -void stack_clear(struct stack *stack) -{ - BT_ASSERT(stack); - stack->size = 0; -} - -static inline -enum bt_msg_iter_status msg_iter_status_from_m_status( - enum bt_msg_iter_medium_status m_status) -{ - /* They are the same */ - return (int) m_status; -} - -static inline -size_t buf_size_bits(struct bt_msg_iter *notit) -{ - return notit->buf.sz * 8; -} - -static inline -size_t buf_available_bits(struct bt_msg_iter *notit) -{ - return buf_size_bits(notit) - notit->buf.at; -} - -static inline -size_t packet_at(struct bt_msg_iter *notit) -{ - return notit->buf.packet_offset + notit->buf.at; -} - -static inline -void buf_consume_bits(struct bt_msg_iter *notit, size_t incr) -{ - BT_LOGV("Advancing cursor: notit-addr=%p, cur-before=%zu, cur-after=%zu", - notit, notit->buf.at, notit->buf.at + incr); - notit->buf.at += incr; -} - -static -enum bt_msg_iter_status request_medium_bytes( - struct bt_msg_iter *notit) -{ - uint8_t *buffer_addr = NULL; - size_t buffer_sz = 0; - enum bt_msg_iter_medium_status m_status; - - BT_LOGV("Calling user function (request bytes): notit-addr=%p, " - "request-size=%zu", notit, notit->medium.max_request_sz); - m_status = notit->medium.medops.request_bytes( - notit->medium.max_request_sz, &buffer_addr, - &buffer_sz, notit->medium.data); - BT_LOGV("User function returned: status=%s, buf-addr=%p, buf-size=%zu", - bt_msg_iter_medium_status_string(m_status), - buffer_addr, buffer_sz); - if (m_status == BT_MSG_ITER_MEDIUM_STATUS_OK) { - BT_ASSERT(buffer_sz != 0); - - /* New packet offset is old one + old size (in bits) */ - notit->buf.packet_offset += buf_size_bits(notit); - - /* Restart at the beginning of the new medium buffer */ - notit->buf.at = 0; - notit->buf.last_eh_at = SIZE_MAX; - - /* New medium buffer size */ - notit->buf.sz = buffer_sz; - - /* New medium buffer address */ - notit->buf.addr = buffer_addr; - - BT_LOGV("User function returned new bytes: " - "packet-offset=%zu, cur=%zu, size=%zu, addr=%p", - notit->buf.packet_offset, notit->buf.at, - notit->buf.sz, notit->buf.addr); - BT_LOGV_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:", - buffer_addr); - } else if (m_status == BT_MSG_ITER_MEDIUM_STATUS_EOF) { - /* - * User returned end of stream: validate that we're not - * in the middle of a packet header, packet context, or - * event. - */ - if (notit->cur_exp_packet_total_size >= 0) { - if (packet_at(notit) == - notit->cur_exp_packet_total_size) { - goto end; - } - } else { - if (packet_at(notit) == 0) { - goto end; - } - - if (notit->buf.last_eh_at != SIZE_MAX && - notit->buf.at == notit->buf.last_eh_at) { - goto end; - } - } - - /* All other states are invalid */ - BT_LOGW("User function returned %s, but message iterator is in an unexpected state: " - "state=%s, cur-packet-size=%" PRId64 ", cur=%zu, " - "packet-cur=%zu, last-eh-at=%zu", - bt_msg_iter_medium_status_string(m_status), - state_string(notit->state), - notit->cur_exp_packet_total_size, - notit->buf.at, packet_at(notit), - notit->buf.last_eh_at); - m_status = BT_MSG_ITER_MEDIUM_STATUS_ERROR; - } else if (m_status < 0) { - BT_LOGW("User function failed: status=%s", - bt_msg_iter_medium_status_string(m_status)); - } - -end: - return msg_iter_status_from_m_status(m_status); -} - -static inline -enum bt_msg_iter_status buf_ensure_available_bits( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - if (unlikely(buf_available_bits(notit) == 0)) { - /* - * This _cannot_ return BT_MSG_ITER_STATUS_OK - * _and_ no bits. - */ - status = request_medium_bytes(notit); - } - - return status; -} - -static -enum bt_msg_iter_status read_dscope_begin_state( - struct bt_msg_iter *notit, - struct ctf_field_class *dscope_fc, - enum state done_state, enum state continue_state, - bt_field *dscope_field) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - enum bt_bfcr_status bfcr_status; - size_t consumed_bits; - - notit->cur_dscope_field = dscope_field; - BT_LOGV("Starting BFCR: notit-addr=%p, bfcr-addr=%p, fc-addr=%p", - notit, notit->bfcr, dscope_fc); - consumed_bits = bt_bfcr_start(notit->bfcr, dscope_fc, - notit->buf.addr, notit->buf.at, packet_at(notit), - notit->buf.sz, &bfcr_status); - BT_LOGV("BFCR consumed bits: size=%zu", consumed_bits); - - switch (bfcr_status) { - case BT_BFCR_STATUS_OK: - /* Field class was read completely */ - BT_LOGV_STR("Field was completely decoded."); - notit->state = done_state; - break; - case BT_BFCR_STATUS_EOF: - BT_LOGV_STR("BFCR needs more data to decode field completely."); - notit->state = continue_state; - break; - default: - BT_LOGW("BFCR failed to start: notit-addr=%p, bfcr-addr=%p, " - "status=%s", notit, notit->bfcr, - bt_bfcr_status_string(bfcr_status)); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - /* Consume bits now since we know we're not in an error state */ - buf_consume_bits(notit, consumed_bits); - -end: - return status; -} - -static -enum bt_msg_iter_status read_dscope_continue_state( - struct bt_msg_iter *notit, enum state done_state) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - enum bt_bfcr_status bfcr_status; - size_t consumed_bits; - - BT_LOGV("Continuing BFCR: notit-addr=%p, bfcr-addr=%p", - notit, notit->bfcr); - - status = buf_ensure_available_bits(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - if (status < 0) { - BT_LOGW("Cannot ensure that buffer has at least one byte: " - "msg-addr=%p, status=%s", - notit, bt_msg_iter_status_string(status)); - } else { - BT_LOGV("Cannot ensure that buffer has at least one byte: " - "msg-addr=%p, status=%s", - notit, bt_msg_iter_status_string(status)); - } - - goto end; - } - - consumed_bits = bt_bfcr_continue(notit->bfcr, notit->buf.addr, - notit->buf.sz, &bfcr_status); - BT_LOGV("BFCR consumed bits: size=%zu", consumed_bits); - - switch (bfcr_status) { - case BT_BFCR_STATUS_OK: - /* Type was read completely. */ - BT_LOGV_STR("Field was completely decoded."); - notit->state = done_state; - break; - case BT_BFCR_STATUS_EOF: - /* Stay in this continue state. */ - BT_LOGV_STR("BFCR needs more data to decode field completely."); - break; - default: - BT_LOGW("BFCR failed to continue: notit-addr=%p, bfcr-addr=%p, " - "status=%s", notit, notit->bfcr, - bt_bfcr_status_string(bfcr_status)); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - /* Consume bits now since we know we're not in an error state. */ - buf_consume_bits(notit, consumed_bits); -end: - return status; -} - -static -void release_event_dscopes(struct bt_msg_iter *notit) -{ - notit->dscopes.event_common_context = NULL; - notit->dscopes.event_spec_context = NULL; - notit->dscopes.event_payload = NULL; -} - -static -void release_all_dscopes(struct bt_msg_iter *notit) -{ - notit->dscopes.stream_packet_context = NULL; - - if (notit->packet_context_field) { - bt_packet_context_field_release(notit->packet_context_field); - notit->packet_context_field = NULL; - } - - release_event_dscopes(notit); -} - -static -enum bt_msg_iter_status read_packet_header_begin_state( - struct bt_msg_iter *notit) -{ - struct ctf_field_class *packet_header_fc = NULL; - enum bt_msg_iter_status ret = BT_MSG_ITER_STATUS_OK; - - if (bt_msg_iter_switch_packet(notit)) { - BT_LOGW("Cannot switch packet: notit-addr=%p", notit); - ret = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - /* - * Make sure at least one bit is available for this packet. An - * empty packet is impossible. If we reach the end of the medium - * at this point, then it's considered the end of the stream. - */ - ret = buf_ensure_available_bits(notit); - switch (ret) { - case BT_MSG_ITER_STATUS_OK: - break; - case BT_MSG_ITER_STATUS_EOF: - ret = BT_MSG_ITER_STATUS_OK; - notit->state = STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END; - goto end; - default: - goto end; - } - - /* Packet header class is common to the whole trace class. */ - packet_header_fc = notit->meta.tc->packet_header_fc; - if (!packet_header_fc) { - notit->state = STATE_AFTER_TRACE_PACKET_HEADER; - goto end; - } - - notit->cur_stream_class_id = -1; - notit->cur_event_class_id = -1; - notit->cur_data_stream_id = -1; - BT_LOGV("Decoding packet header field:" - "notit-addr=%p, trace-class-addr=%p, fc-addr=%p", - notit, notit->meta.tc, packet_header_fc); - ret = read_dscope_begin_state(notit, packet_header_fc, - STATE_AFTER_TRACE_PACKET_HEADER, - STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL); - if (ret < 0) { - BT_LOGW("Cannot decode packet header field: " - "notit-addr=%p, trace-class-addr=%p, " - "fc-addr=%p", - notit, notit->meta.tc, packet_header_fc); - } - -end: - return ret; -} - -static -enum bt_msg_iter_status read_packet_header_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_TRACE_PACKET_HEADER); -} - -static inline -enum bt_msg_iter_status set_current_stream_class(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_stream_class *new_stream_class = NULL; - - if (notit->cur_stream_class_id == -1) { - /* - * No current stream class ID field, therefore only one - * stream class. - */ - if (notit->meta.tc->stream_classes->len != 1) { - BT_LOGW("Need exactly one stream class since there's " - "no stream class ID field: " - "notit-addr=%p", notit); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - new_stream_class = notit->meta.tc->stream_classes->pdata[0]; - notit->cur_stream_class_id = new_stream_class->id; - } - - new_stream_class = ctf_trace_class_borrow_stream_class_by_id( - notit->meta.tc, notit->cur_stream_class_id); - if (!new_stream_class) { - BT_LOGW("No stream class with ID of stream class ID to use in trace class: " - "notit-addr=%p, stream-class-id=%" PRIu64 ", " - "trace-class-addr=%p", - notit, notit->cur_stream_class_id, notit->meta.tc); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - if (notit->meta.sc) { - if (new_stream_class != notit->meta.sc) { - BT_LOGW("Two packets refer to two different stream classes within the same packet sequence: " - "notit-addr=%p, prev-stream-class-addr=%p, " - "prev-stream-class-id=%" PRId64 ", " - "next-stream-class-addr=%p, " - "next-stream-class-id=%" PRId64 ", " - "trace-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - new_stream_class, - new_stream_class->id, - notit->meta.tc); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - } else { - notit->meta.sc = new_stream_class; - } - - BT_LOGV("Set current stream class: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64, - notit, notit->meta.sc, notit->meta.sc->id); - -end: - return status; -} - -static inline -enum bt_msg_iter_status set_current_stream(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - bt_stream *stream = NULL; - - BT_LOGV("Calling user function (get stream): notit-addr=%p, " - "stream-class-addr=%p, stream-class-id=%" PRId64, - notit, notit->meta.sc, - notit->meta.sc->id); - stream = notit->medium.medops.borrow_stream( - notit->meta.sc->ir_sc, notit->cur_data_stream_id, - notit->medium.data); - bt_stream_get_ref(stream); - BT_LOGV("User function returned: stream-addr=%p", stream); - if (!stream) { - BT_LOGW_STR("User function failed to return a stream object " - "for the given stream class."); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - if (notit->stream && stream != notit->stream) { - BT_LOGW("User function returned a different stream than the " - "previous one for the same sequence of packets."); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - BT_STREAM_MOVE_REF(notit->stream, stream); - -end: - bt_stream_put_ref(stream); - return status; -} - -static inline -enum bt_msg_iter_status set_current_packet(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - bt_packet *packet = NULL; - - BT_LOGV("Creating packet for packet message: " - "notit-addr=%p", notit); - BT_LOGV("Creating packet from stream: " - "notit-addr=%p, stream-addr=%p, " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64, - notit, notit->stream, notit->meta.sc, - notit->meta.sc->id); - - /* Create packet */ - BT_ASSERT(notit->stream); - packet = bt_packet_create(notit->stream); - if (!packet) { - BT_LOGE("Cannot create packet from stream: " - "notit-addr=%p, stream-addr=%p, " - "stream-class-addr=%p, " - "stream-class-id=%" PRId64, - notit, notit->stream, notit->meta.sc, - notit->meta.sc->id); - goto error; - } - - goto end; - -error: - BT_PACKET_PUT_REF_AND_RESET(packet); - status = BT_MSG_ITER_STATUS_ERROR; - -end: - BT_PACKET_MOVE_REF(notit->packet, packet); - return status; -} - -static -enum bt_msg_iter_status after_packet_header_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status; - - status = set_current_stream_class(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - notit->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN; - -end: - return status; -} - -static -enum bt_msg_iter_status read_packet_context_begin_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_field_class *packet_context_fc; - - BT_ASSERT(notit->meta.sc); - packet_context_fc = notit->meta.sc->packet_context_fc; - if (!packet_context_fc) { - BT_LOGV("No packet packet context field class in stream class: continuing: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64, - notit, notit->meta.sc, - notit->meta.sc->id); - notit->state = STATE_AFTER_STREAM_PACKET_CONTEXT; - goto end; - } - - BT_ASSERT(!notit->packet_context_field); - - if (packet_context_fc->in_ir) { - /* - * Create free packet context field from stream class. - * This field is going to be moved to the packet once we - * create it. We cannot create the packet now because a - * packet is created from a stream, and this API must be - * able to return the packet context properties without - * creating a stream - * (bt_msg_iter_get_packet_properties()). - */ - notit->packet_context_field = - bt_packet_context_field_create( - notit->meta.sc->ir_sc); - if (!notit->packet_context_field) { - BT_LOGE_STR("Cannot create packet context field wrapper from stream class."); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - notit->dscopes.stream_packet_context = - bt_packet_context_field_borrow_field( - notit->packet_context_field); - BT_ASSERT(notit->dscopes.stream_packet_context); - } - - BT_LOGV("Decoding packet context field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, packet_context_fc); - status = read_dscope_begin_state(notit, packet_context_fc, - STATE_AFTER_STREAM_PACKET_CONTEXT, - STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, - notit->dscopes.stream_packet_context); - if (status < 0) { - BT_LOGW("Cannot decode packet context field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - packet_context_fc); - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_packet_context_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_STREAM_PACKET_CONTEXT); -} - -static -enum bt_msg_iter_status set_current_packet_content_sizes( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - if (notit->cur_exp_packet_total_size == -1) { - if (notit->cur_exp_packet_content_size != -1) { - notit->cur_exp_packet_total_size = - notit->cur_exp_packet_content_size; - } - } else { - if (notit->cur_exp_packet_content_size == -1) { - notit->cur_exp_packet_content_size = - notit->cur_exp_packet_total_size; - } - } - - BT_ASSERT((notit->cur_exp_packet_total_size >= 0 && - notit->cur_exp_packet_content_size >= 0) || - (notit->cur_exp_packet_total_size < 0 && - notit->cur_exp_packet_content_size < 0)); - - if (notit->cur_exp_packet_content_size > - notit->cur_exp_packet_total_size) { - BT_LOGW("Invalid packet or content size: " - "content size is greater than packet size: " - "notit-addr=%p, packet-context-field-addr=%p, " - "packet-size=%" PRId64 ", content-size=%" PRId64, - notit, notit->dscopes.stream_packet_context, - notit->cur_exp_packet_total_size, - notit->cur_exp_packet_content_size); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - BT_LOGV("Set current packet and content sizes: " - "notit-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64, - notit, notit->cur_exp_packet_total_size, - notit->cur_exp_packet_content_size); - -end: - return status; -} - -static -enum bt_msg_iter_status after_packet_context_state(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status; - - status = set_current_packet_content_sizes(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - if (notit->stream) { - /* - * Stream exists, which means we already emitted at - * least one packet beginning message, so the initial - * stream beginning message was also emitted. - */ - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; - } else { - notit->state = STATE_CHECK_EMIT_MSG_STREAM_BEGINNING; - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_header_begin_state(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_field_class *event_header_fc = NULL; - - /* Reset the position of the last event header */ - notit->buf.last_eh_at = notit->buf.at; - notit->cur_event_class_id = -1; - - /* Check if we have some content left */ - if (notit->cur_exp_packet_content_size >= 0) { - if (unlikely(packet_at(notit) == - notit->cur_exp_packet_content_size)) { - /* No more events! */ - BT_LOGV("Reached end of packet: notit-addr=%p, " - "cur=%zu", notit, packet_at(notit)); - notit->state = STATE_EMIT_MSG_PACKET_END_MULTI; - goto end; - } else if (unlikely(packet_at(notit) > - notit->cur_exp_packet_content_size)) { - /* That's not supposed to happen */ - BT_LOGV("Before decoding event header field: cursor is passed the packet's content: " - "notit-addr=%p, content-size=%" PRId64 ", " - "cur=%zu", notit, - notit->cur_exp_packet_content_size, - packet_at(notit)); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - } else { - /* - * "Infinite" content: we're done when the medium has - * nothing else for us. - */ - status = buf_ensure_available_bits(notit); - switch (status) { - case BT_MSG_ITER_STATUS_OK: - break; - case BT_MSG_ITER_STATUS_EOF: - status = BT_MSG_ITER_STATUS_OK; - notit->state = STATE_EMIT_MSG_PACKET_END_SINGLE; - goto end; - default: - goto end; - } - } - - release_event_dscopes(notit); - BT_ASSERT(notit->meta.sc); - event_header_fc = notit->meta.sc->event_header_fc; - if (!event_header_fc) { - notit->state = STATE_AFTER_EVENT_HEADER; - goto end; - } - - BT_LOGV("Decoding event header field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - event_header_fc); - status = read_dscope_begin_state(notit, event_header_fc, - STATE_AFTER_EVENT_HEADER, - STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL); - if (status < 0) { - BT_LOGW("Cannot decode event header field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - event_header_fc); - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_header_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_EVENT_HEADER); -} - -static inline -enum bt_msg_iter_status set_current_event_class(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - struct ctf_event_class *new_event_class = NULL; - - if (notit->cur_event_class_id == -1) { - /* - * No current event class ID field, therefore only one - * event class. - */ - if (notit->meta.sc->event_classes->len != 1) { - BT_LOGW("Need exactly one event class since there's " - "no event class ID field: " - "notit-addr=%p", notit); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - new_event_class = notit->meta.sc->event_classes->pdata[0]; - notit->cur_event_class_id = new_event_class->id; - } - - new_event_class = ctf_stream_class_borrow_event_class_by_id( - notit->meta.sc, notit->cur_event_class_id); - if (!new_event_class) { - BT_LOGW("No event class with ID of event class ID to use in stream class: " - "notit-addr=%p, stream-class-id=%" PRIu64 ", " - "event-class-id=%" PRIu64 ", " - "trace-class-addr=%p", - notit, notit->meta.sc->id, notit->cur_event_class_id, - notit->meta.tc); - status = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - - notit->meta.ec = new_event_class; - BT_LOGV("Set current event class: " - "notit-addr=%p, event-class-addr=%p, " - "event-class-id=%" PRId64 ", " - "event-class-name=\"%s\"", - notit, notit->meta.ec, notit->meta.ec->id, - notit->meta.ec->name->str); - -end: - return status; -} - -static inline -enum bt_msg_iter_status set_current_event_message( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - bt_message *msg = NULL; - - BT_ASSERT(notit->meta.ec); - BT_ASSERT(notit->packet); - BT_LOGV("Creating event message from event class and packet: " - "notit-addr=%p, ec-addr=%p, ec-name=\"%s\", packet-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->packet); - BT_ASSERT(notit->msg_iter); - BT_ASSERT(notit->meta.sc); - - if (bt_stream_class_borrow_default_clock_class(notit->meta.sc->ir_sc)) { - msg = bt_message_event_create_with_default_clock_snapshot( - notit->msg_iter, notit->meta.ec->ir_ec, - notit->packet, notit->default_clock_snapshot); - } else { - msg = bt_message_event_create(notit->msg_iter, - notit->meta.ec->ir_ec, notit->packet); - } - - if (!msg) { - BT_LOGE("Cannot create event message: " - "notit-addr=%p, ec-addr=%p, ec-name=\"%s\", " - "packet-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->packet); - goto error; - } - - goto end; - -error: - BT_MESSAGE_PUT_REF_AND_RESET(msg); - status = BT_MSG_ITER_STATUS_ERROR; - -end: - BT_MESSAGE_MOVE_REF(notit->event_msg, msg); - return status; -} - -static -enum bt_msg_iter_status after_event_header_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status; - - status = set_current_event_class(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - status = set_current_event_message(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - notit->event = bt_message_event_borrow_event( - notit->event_msg); - BT_ASSERT(notit->event); - notit->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN; - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_common_context_begin_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_field_class *event_common_context_fc; - - event_common_context_fc = notit->meta.sc->event_common_context_fc; - if (!event_common_context_fc) { - notit->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN; - goto end; - } - - if (event_common_context_fc->in_ir) { - BT_ASSERT(!notit->dscopes.event_common_context); - notit->dscopes.event_common_context = - bt_event_borrow_common_context_field( - notit->event); - BT_ASSERT(notit->dscopes.event_common_context); - } - - BT_LOGV("Decoding event common context field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", " - "fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - event_common_context_fc); - status = read_dscope_begin_state(notit, event_common_context_fc, - STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN, - STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, - notit->dscopes.event_common_context); - if (status < 0) { - BT_LOGW("Cannot decode event common context field: " - "notit-addr=%p, stream-class-addr=%p, " - "stream-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.sc, - notit->meta.sc->id, - event_common_context_fc); - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_common_context_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN); -} - -static -enum bt_msg_iter_status read_event_spec_context_begin_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_field_class *event_spec_context_fc; - - event_spec_context_fc = notit->meta.ec->spec_context_fc; - if (!event_spec_context_fc) { - notit->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN; - goto end; - } - - if (event_spec_context_fc->in_ir) { - BT_ASSERT(!notit->dscopes.event_spec_context); - notit->dscopes.event_spec_context = - bt_event_borrow_specific_context_field( - notit->event); - BT_ASSERT(notit->dscopes.event_spec_context); - } - - BT_LOGV("Decoding event specific context field: " - "notit-addr=%p, event-class-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "fc-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->meta.ec->id, - event_spec_context_fc); - status = read_dscope_begin_state(notit, event_spec_context_fc, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, - STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, - notit->dscopes.event_spec_context); - if (status < 0) { - BT_LOGW("Cannot decode event specific context field: " - "notit-addr=%p, event-class-addr=%p, " - "event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->meta.ec->id, - event_spec_context_fc); - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_spec_context_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN); -} - -static -enum bt_msg_iter_status read_event_payload_begin_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - struct ctf_field_class *event_payload_fc; - - event_payload_fc = notit->meta.ec->payload_fc; - if (!event_payload_fc) { - notit->state = STATE_EMIT_MSG_EVENT; - goto end; - } - - if (event_payload_fc->in_ir) { - BT_ASSERT(!notit->dscopes.event_payload); - notit->dscopes.event_payload = - bt_event_borrow_payload_field( - notit->event); - BT_ASSERT(notit->dscopes.event_payload); - } - - BT_LOGV("Decoding event payload field: " - "notit-addr=%p, event-class-addr=%p, " - "event-class-name=\"%s\", event-class-id=%" PRId64 ", " - "fc-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->meta.ec->id, - event_payload_fc); - status = read_dscope_begin_state(notit, event_payload_fc, - STATE_EMIT_MSG_EVENT, - STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, - notit->dscopes.event_payload); - if (status < 0) { - BT_LOGW("Cannot decode event payload field: " - "notit-addr=%p, event-class-addr=%p, " - "event-class-name=\"%s\", " - "event-class-id=%" PRId64 ", fc-addr=%p", - notit, notit->meta.ec, - notit->meta.ec->name->str, - notit->meta.ec->id, - event_payload_fc); - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_event_payload_continue_state( - struct bt_msg_iter *notit) -{ - return read_dscope_continue_state(notit, STATE_EMIT_MSG_EVENT); -} - -static -enum bt_msg_iter_status skip_packet_padding_state(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - size_t bits_to_skip; - - BT_ASSERT(notit->cur_exp_packet_total_size > 0); - bits_to_skip = notit->cur_exp_packet_total_size - packet_at(notit); - if (bits_to_skip == 0) { - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - goto end; - } else { - size_t bits_to_consume; - - BT_LOGV("Trying to skip %zu bits of padding: notit-addr=%p, size=%zu", - bits_to_skip, notit, bits_to_skip); - status = buf_ensure_available_bits(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - bits_to_consume = MIN(buf_available_bits(notit), bits_to_skip); - BT_LOGV("Skipping %zu bits of padding: notit-addr=%p, size=%zu", - bits_to_consume, notit, bits_to_consume); - buf_consume_bits(notit, bits_to_consume); - bits_to_skip = notit->cur_exp_packet_total_size - - packet_at(notit); - if (bits_to_skip == 0) { - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - goto end; - } - } - -end: - return status; -} - -static -enum bt_msg_iter_status check_emit_msg_stream_beginning_state( - struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - if (notit->set_stream) { - status = set_current_stream(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - } - - if (notit->emit_stream_begin_msg) { - notit->state = STATE_EMIT_MSG_STREAM_BEGINNING; - } else { - /* Stream's first packet */ - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; - } - -end: - return status; -} - -static -enum bt_msg_iter_status check_emit_msg_discarded_events( - struct bt_msg_iter *notit) -{ - notit->state = STATE_EMIT_MSG_DISCARDED_EVENTS; - - if (!notit->meta.sc->has_discarded_events) { - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; - goto end; - } - - if (notit->prev_packet_snapshots.discarded_events == UINT64_C(-1)) { - if (notit->snapshots.discarded_events == 0 || - notit->snapshots.discarded_events == UINT64_C(-1)) { - /* - * Stream's first packet with no discarded - * events or no information about discarded - * events: do not emit. - */ - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; - } - } else { - /* - * If the previous packet has a value for this counter, - * then this counter is defined for the whole stream. - */ - BT_ASSERT(notit->snapshots.discarded_events != UINT64_C(-1)); - - if (notit->snapshots.discarded_events - - notit->prev_packet_snapshots.discarded_events == 0) { - /* - * No discarded events since previous packet: do - * not emit. - */ - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; - } - } - -end: - return BT_MSG_ITER_STATUS_OK; -} - -static -enum bt_msg_iter_status check_emit_msg_discarded_packets( - struct bt_msg_iter *notit) -{ - notit->state = STATE_EMIT_MSG_DISCARDED_PACKETS; - - if (!notit->meta.sc->has_discarded_packets) { - notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; - goto end; - } - - if (notit->prev_packet_snapshots.packets == UINT64_C(-1)) { - /* - * Stream's first packet or no information about - * discarded packets: do not emit. In other words, if - * this is the first packet and its sequence number is - * not 0, do not consider that packets were previously - * lost: we might be reading a partial stream (LTTng - * snapshot for example). - */ - notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; - } else { - /* - * If the previous packet has a value for this counter, - * then this counter is defined for the whole stream. - */ - BT_ASSERT(notit->snapshots.packets != UINT64_C(-1)); - - if (notit->snapshots.packets - - notit->prev_packet_snapshots.packets <= 1) { - /* - * No discarded packets since previous packet: - * do not emit. - */ - notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; - } - } - -end: - return BT_MSG_ITER_STATUS_OK; -} - -static -enum bt_msg_iter_status check_emit_msg_stream_activity_end( - struct bt_msg_iter *notit) -{ - if (notit->emit_stream_end_msg) { - notit->state = STATE_EMIT_MSG_STREAM_ACTIVITY_END; - } else { - notit->state = STATE_DONE; - } - - return BT_MSG_ITER_STATUS_OK; -} - -static inline -enum bt_msg_iter_status handle_state(struct bt_msg_iter *notit) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - const enum state state = notit->state; - - BT_LOGV("Handling state: notit-addr=%p, state=%s", - notit, state_string(state)); - - // TODO: optimalize! - switch (state) { - case STATE_INIT: - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - break; - case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: - status = read_packet_header_begin_state(notit); - break; - case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: - status = read_packet_header_continue_state(notit); - break; - case STATE_AFTER_TRACE_PACKET_HEADER: - status = after_packet_header_state(notit); - break; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: - status = read_packet_context_begin_state(notit); - break; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: - status = read_packet_context_continue_state(notit); - break; - case STATE_AFTER_STREAM_PACKET_CONTEXT: - status = after_packet_context_state(notit); - break; - case STATE_CHECK_EMIT_MSG_STREAM_BEGINNING: - status = check_emit_msg_stream_beginning_state(notit); - break; - case STATE_EMIT_MSG_STREAM_BEGINNING: - notit->state = STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING; - break; - case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; - break; - case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS: - status = check_emit_msg_discarded_events(notit); - break; - case STATE_EMIT_MSG_DISCARDED_EVENTS: - notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; - break; - case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS: - status = check_emit_msg_discarded_packets(notit); - break; - case STATE_EMIT_MSG_DISCARDED_PACKETS: - notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; - break; - case STATE_EMIT_MSG_PACKET_BEGINNING: - notit->state = STATE_DSCOPE_EVENT_HEADER_BEGIN; - break; - case STATE_DSCOPE_EVENT_HEADER_BEGIN: - status = read_event_header_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_HEADER_CONTINUE: - status = read_event_header_continue_state(notit); - break; - case STATE_AFTER_EVENT_HEADER: - status = after_event_header_state(notit); - break; - case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN: - status = read_event_common_context_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE: - status = read_event_common_context_continue_state(notit); - break; - case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN: - status = read_event_spec_context_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE: - status = read_event_spec_context_continue_state(notit); - break; - case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: - status = read_event_payload_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: - status = read_event_payload_continue_state(notit); - break; - case STATE_EMIT_MSG_EVENT: - notit->state = STATE_DSCOPE_EVENT_HEADER_BEGIN; - break; - case STATE_SKIP_PACKET_PADDING: - status = skip_packet_padding_state(notit); - break; - case STATE_EMIT_MSG_PACKET_END_MULTI: - notit->state = STATE_SKIP_PACKET_PADDING; - break; - case STATE_EMIT_MSG_PACKET_END_SINGLE: - notit->state = STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END; - break; - case STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END: - status = check_emit_msg_stream_activity_end(notit); - break; - case STATE_EMIT_MSG_STREAM_ACTIVITY_END: - notit->state = STATE_EMIT_MSG_STREAM_END; - break; - case STATE_EMIT_MSG_STREAM_END: - notit->state = STATE_DONE; - break; - case STATE_DONE: - break; - default: - BT_LOGD("Unknown CTF plugin message iterator state: " - "notit-addr=%p, state=%d", notit, notit->state); - abort(); - } - - BT_LOGV("Handled state: notit-addr=%p, status=%s, " - "prev-state=%s, cur-state=%s", - notit, bt_msg_iter_status_string(status), - state_string(state), state_string(notit->state)); - return status; -} - -BT_HIDDEN -void bt_msg_iter_reset_for_next_stream_file(struct bt_msg_iter *notit) -{ - BT_ASSERT(notit); - BT_LOGD("Resetting message iterator: addr=%p", notit); - stack_clear(notit->stack); - notit->meta.sc = NULL; - notit->meta.ec = NULL; - BT_PACKET_PUT_REF_AND_RESET(notit->packet); - BT_STREAM_PUT_REF_AND_RESET(notit->stream); - BT_MESSAGE_PUT_REF_AND_RESET(notit->event_msg); - release_all_dscopes(notit); - notit->cur_dscope_field = NULL; - - if (notit->packet_context_field) { - bt_packet_context_field_release(notit->packet_context_field); - notit->packet_context_field = NULL; - } - - notit->buf.addr = NULL; - notit->buf.sz = 0; - notit->buf.at = 0; - notit->buf.last_eh_at = SIZE_MAX; - notit->buf.packet_offset = 0; - notit->state = STATE_INIT; - notit->cur_exp_packet_content_size = -1; - notit->cur_exp_packet_total_size = -1; - notit->cur_packet_offset = -1; - notit->cur_event_class_id = -1; - notit->snapshots.beginning_clock = UINT64_C(-1); - notit->snapshots.end_clock = UINT64_C(-1); -} - -/** - * Resets the internal state of a CTF message iterator. - */ -BT_HIDDEN -void bt_msg_iter_reset(struct bt_msg_iter *notit) -{ - bt_msg_iter_reset_for_next_stream_file(notit); - notit->cur_stream_class_id = -1; - notit->cur_data_stream_id = -1; - notit->emit_stream_begin_msg = true; - notit->emit_stream_end_msg = true; - notit->snapshots.discarded_events = UINT64_C(-1); - notit->snapshots.packets = UINT64_C(-1); - notit->prev_packet_snapshots.discarded_events = UINT64_C(-1); - notit->prev_packet_snapshots.packets = UINT64_C(-1); - notit->prev_packet_snapshots.beginning_clock = UINT64_C(-1); - notit->prev_packet_snapshots.end_clock = UINT64_C(-1); -} - -static -int bt_msg_iter_switch_packet(struct bt_msg_iter *notit) -{ - int ret = 0; - - /* - * We don't put the stream class here because we need to make - * sure that all the packets processed by the same message - * iterator refer to the same stream class (the first one). - */ - BT_ASSERT(notit); - - if (notit->cur_exp_packet_total_size != -1) { - notit->cur_packet_offset += notit->cur_exp_packet_total_size; - } - - BT_LOGV("Switching packet: notit-addr=%p, cur=%zu, " - "packet-offset=%" PRId64, notit, notit->buf.at, - notit->cur_packet_offset); - stack_clear(notit->stack); - notit->meta.ec = NULL; - BT_PACKET_PUT_REF_AND_RESET(notit->packet); - BT_MESSAGE_PUT_REF_AND_RESET(notit->event_msg); - release_all_dscopes(notit); - notit->cur_dscope_field = NULL; - - /* - * Adjust current buffer so that addr points to the beginning of the new - * packet. - */ - if (notit->buf.addr) { - size_t consumed_bytes = (size_t) (notit->buf.at / CHAR_BIT); - - /* Packets are assumed to start on a byte frontier. */ - if (notit->buf.at % CHAR_BIT) { - BT_LOGW("Cannot switch packet: current position is not a multiple of 8: " - "notit-addr=%p, cur=%zu", notit, notit->buf.at); - ret = -1; - goto end; - } - - notit->buf.addr += consumed_bytes; - notit->buf.sz -= consumed_bytes; - notit->buf.at = 0; - notit->buf.packet_offset = 0; - BT_LOGV("Adjusted buffer: addr=%p, size=%zu", - notit->buf.addr, notit->buf.sz); - } - - notit->cur_exp_packet_content_size = -1; - notit->cur_exp_packet_total_size = -1; - notit->cur_stream_class_id = -1; - notit->cur_event_class_id = -1; - notit->cur_data_stream_id = -1; - notit->prev_packet_snapshots = notit->snapshots; - notit->snapshots.discarded_events = UINT64_C(-1); - notit->snapshots.packets = UINT64_C(-1); - notit->snapshots.beginning_clock = UINT64_C(-1); - notit->snapshots.end_clock = UINT64_C(-1); - -end: - return ret; -} - -static -bt_field *borrow_next_field(struct bt_msg_iter *notit) -{ - bt_field *next_field = NULL; - bt_field *base_field; - const bt_field_class *base_fc; - size_t index; - - BT_ASSERT(!stack_empty(notit->stack)); - index = stack_top(notit->stack)->index; - base_field = stack_top(notit->stack)->base; - BT_ASSERT(base_field); - base_fc = bt_field_borrow_class_const(base_field); - BT_ASSERT(base_fc); - - switch (bt_field_class_get_type(base_fc)) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - { - BT_ASSERT(index < - bt_field_class_structure_get_member_count( - bt_field_borrow_class_const( - base_field))); - next_field = - bt_field_structure_borrow_member_field_by_index( - base_field, index); - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - BT_ASSERT(index < bt_field_array_get_length(base_field)); - next_field = bt_field_array_borrow_element_field_by_index( - base_field, index); - break; - case BT_FIELD_CLASS_TYPE_VARIANT: - BT_ASSERT(index == 0); - next_field = bt_field_variant_borrow_selected_option_field( - base_field); - break; - default: - abort(); - } - - BT_ASSERT(next_field); - return next_field; -} - -static -void update_default_clock(struct bt_msg_iter *notit, uint64_t new_val, - uint64_t new_val_size) -{ - uint64_t new_val_mask; - uint64_t cur_value_masked; - - BT_ASSERT(new_val_size > 0); - - /* - * Special case for a 64-bit new value, which is the limit - * of a clock value as of this version: overwrite the - * current value directly. - */ - if (new_val_size == 64) { - notit->default_clock_snapshot = new_val; - goto end; - } - - new_val_mask = (1ULL << new_val_size) - 1; - cur_value_masked = notit->default_clock_snapshot & new_val_mask; - - if (new_val < cur_value_masked) { - /* - * It looks like a wrap happened on the number of bits - * of the requested new value. Assume that the clock - * value wrapped only one time. - */ - notit->default_clock_snapshot += new_val_mask + 1; - } - - /* Clear the low bits of the current clock value. */ - notit->default_clock_snapshot &= ~new_val_mask; - - /* Set the low bits of the current clock value. */ - notit->default_clock_snapshot |= new_val; - -end: - BT_LOGV("Updated default clock's value from integer field's value: " - "value=%" PRIu64, notit->default_clock_snapshot); -} - -static -enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value, - struct ctf_field_class *fc, void *data) -{ - struct bt_msg_iter *notit = data; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - bt_field *field = NULL; - struct ctf_field_class_int *int_fc = (void *) fc; - - BT_LOGV("Unsigned integer function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d, value=%" PRIu64, - notit, notit->bfcr, fc, fc->type, fc->in_ir, value); - - if (likely(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) { - goto update_def_clock; - } - - switch (int_fc->meaning) { - case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID: - notit->cur_event_class_id = value; - break; - case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID: - notit->cur_data_stream_id = value; - break; - case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME: - notit->snapshots.beginning_clock = value; - break; - case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME: - notit->snapshots.end_clock = value; - break; - case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID: - notit->cur_stream_class_id = value; - break; - case CTF_FIELD_CLASS_MEANING_MAGIC: - if (value != 0xc1fc1fc1) { - BT_LOGW("Invalid CTF magic number: notit-addr=%p, " - "magic=%" PRIx64, notit, value); - status = BT_BFCR_STATUS_ERROR; - goto end; - } - - break; - case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT: - notit->snapshots.packets = value; - break; - case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT: - notit->snapshots.discarded_events = value; - break; - case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE: - notit->cur_exp_packet_total_size = value; - break; - case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE: - notit->cur_exp_packet_content_size = value; - break; - default: - abort(); - } - -update_def_clock: - if (unlikely(int_fc->mapped_clock_class)) { - update_default_clock(notit, value, int_fc->base.size); - } - - if (unlikely(int_fc->storing_index >= 0)) { - g_array_index(notit->stored_values, uint64_t, - (uint64_t) int_fc->storing_index) = value; - } - - if (unlikely(!fc->in_ir)) { - goto end; - } - - field = borrow_next_field(notit); - BT_ASSERT(field); - BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); - BT_ASSERT(bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || - bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION); - bt_field_unsigned_integer_set_value(field, value); - stack_top(notit->stack)->index++; - -end: - return status; -} - -static -enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value, - struct ctf_field_class *fc, void *data) -{ - int ret; - struct bt_msg_iter *notit = data; - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - bt_field *string_field = NULL; - struct ctf_field_class_int *int_fc = (void *) fc; - char str[2] = {'\0', '\0'}; - - BT_LOGV("Unsigned integer character function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d, value=%" PRIu64, - notit, notit->bfcr, fc, fc->type, fc->in_ir, value); - BT_ASSERT(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE); - BT_ASSERT(!int_fc->mapped_clock_class); - BT_ASSERT(int_fc->storing_index < 0); - - if (unlikely(!fc->in_ir)) { - goto end; - } - - if (notit->done_filling_string) { - goto end; - } - - if (value == 0) { - notit->done_filling_string = true; - goto end; - } - - string_field = stack_top(notit->stack)->base; - BT_ASSERT(bt_field_get_class_type(string_field) == - BT_FIELD_CLASS_TYPE_STRING); - - /* Append character */ - str[0] = (char) value; - ret = bt_field_string_append_with_length(string_field, str, 1); - if (ret) { - BT_LOGE("Cannot append character to string field's value: " - "notit-addr=%p, field-addr=%p, ret=%d", - notit, string_field, ret); - status = BT_BFCR_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static -enum bt_bfcr_status bfcr_signed_int_cb(int64_t value, - struct ctf_field_class *fc, void *data) -{ - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - bt_field *field = NULL; - struct bt_msg_iter *notit = data; - struct ctf_field_class_int *int_fc = (void *) fc; - - BT_LOGV("Signed integer function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d, value=%" PRId64, - notit, notit->bfcr, fc, fc->type, fc->in_ir, value); - BT_ASSERT(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE); - - if (unlikely(int_fc->storing_index >= 0)) { - g_array_index(notit->stored_values, uint64_t, - (uint64_t) int_fc->storing_index) = (uint64_t) value; - } - - if (unlikely(!fc->in_ir)) { - goto end; - } - - field = borrow_next_field(notit); - BT_ASSERT(field); - BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); - BT_ASSERT(bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || - bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); - bt_field_signed_integer_set_value(field, value); - stack_top(notit->stack)->index++; - -end: - return status; -} - -static -enum bt_bfcr_status bfcr_floating_point_cb(double value, - struct ctf_field_class *fc, void *data) -{ - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - bt_field *field = NULL; - struct bt_msg_iter *notit = data; - - BT_LOGV("Floating point number function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d, value=%f", - notit, notit->bfcr, fc, fc->type, fc->in_ir, value); - - if (unlikely(!fc->in_ir)) { - goto end; - } - - field = borrow_next_field(notit); - BT_ASSERT(field); - BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); - BT_ASSERT(bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_REAL); - bt_field_real_set_value(field, value); - stack_top(notit->stack)->index++; - -end: - return status; -} - -static -enum bt_bfcr_status bfcr_string_begin_cb( - struct ctf_field_class *fc, void *data) -{ - bt_field *field = NULL; - struct bt_msg_iter *notit = data; - int ret; - - BT_LOGV("String (beginning) function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d", - notit, notit->bfcr, fc, fc->type, fc->in_ir); - - if (unlikely(!fc->in_ir)) { - goto end; - } - - field = borrow_next_field(notit); - BT_ASSERT(field); - BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); - BT_ASSERT(bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_STRING); - ret = bt_field_string_clear(field); - BT_ASSERT(ret == 0); - - /* - * Push on stack. Not a compound class per se, but we know that - * only bfcr_string_cb() may be called between this call and a - * subsequent call to bfcr_string_end_cb(). - */ - stack_push(notit->stack, field); - -end: - return BT_BFCR_STATUS_OK; -} - -static -enum bt_bfcr_status bfcr_string_cb(const char *value, - size_t len, struct ctf_field_class *fc, void *data) -{ - enum bt_bfcr_status status = BT_BFCR_STATUS_OK; - bt_field *field = NULL; - struct bt_msg_iter *notit = data; - int ret; - - BT_LOGV("String (substring) function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d, string-length=%zu", - notit, notit->bfcr, fc, fc->type, fc->in_ir, - len); - - if (unlikely(!fc->in_ir)) { - goto end; - } - - field = stack_top(notit->stack)->base; - BT_ASSERT(field); - - /* Append current substring */ - ret = bt_field_string_append_with_length(field, value, len); - if (ret) { - BT_LOGE("Cannot append substring to string field's value: " - "notit-addr=%p, field-addr=%p, string-length=%zu, " - "ret=%d", notit, field, len, ret); - status = BT_BFCR_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static -enum bt_bfcr_status bfcr_string_end_cb( - struct ctf_field_class *fc, void *data) -{ - struct bt_msg_iter *notit = data; - - BT_LOGV("String (end) function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d", - notit, notit->bfcr, fc, fc->type, fc->in_ir); - - if (unlikely(!fc->in_ir)) { - goto end; - } - - /* Pop string field */ - stack_pop(notit->stack); - - /* Go to next field */ - stack_top(notit->stack)->index++; - -end: - return BT_BFCR_STATUS_OK; -} - -enum bt_bfcr_status bfcr_compound_begin_cb( - struct ctf_field_class *fc, void *data) -{ - struct bt_msg_iter *notit = data; - bt_field *field; - - BT_LOGV("Compound (beginning) function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d", - notit, notit->bfcr, fc, fc->type, fc->in_ir); - - if (!fc->in_ir) { - goto end; - } - - /* Borrow field */ - if (stack_empty(notit->stack)) { - /* Root: already set by read_dscope_begin_state() */ - field = notit->cur_dscope_field; - } else { - field = borrow_next_field(notit); - BT_ASSERT(field); - } - - /* Push field */ - BT_ASSERT(field); - BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); - stack_push(notit->stack, field); - - /* - * Change BFCR "unsigned int" callback if it's a text - * array/sequence. - */ - if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || - fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - if (array_fc->is_text) { - int ret; - - BT_ASSERT(bt_field_get_class_type(field) == - BT_FIELD_CLASS_TYPE_STRING); - notit->done_filling_string = false; - ret = bt_field_string_clear(field); - BT_ASSERT(ret == 0); - bt_bfcr_set_unsigned_int_cb(notit->bfcr, - bfcr_unsigned_int_char_cb); - } - } - -end: - return BT_BFCR_STATUS_OK; -} - -enum bt_bfcr_status bfcr_compound_end_cb( - struct ctf_field_class *fc, void *data) -{ - struct bt_msg_iter *notit = data; - - BT_LOGV("Compound (end) function called from BFCR: " - "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " - "fc-type=%d, fc-in-ir=%d", - notit, notit->bfcr, fc, fc->type, fc->in_ir); - - if (!fc->in_ir) { - goto end; - } - - BT_ASSERT(!stack_empty(notit->stack)); - BT_ASSERT(bt_field_borrow_class_const(stack_top(notit->stack)->base) == - fc->ir_fc); - - /* - * Reset BFCR "unsigned int" callback if it's a text - * array/sequence. - */ - if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || - fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { - struct ctf_field_class_array_base *array_fc = (void *) fc; - - if (array_fc->is_text) { - BT_ASSERT(bt_field_get_class_type( - stack_top(notit->stack)->base) == - BT_FIELD_CLASS_TYPE_STRING); - bt_bfcr_set_unsigned_int_cb(notit->bfcr, - bfcr_unsigned_int_cb); - } - } - - /* Pop stack */ - stack_pop(notit->stack); - - /* If the stack is not empty, increment the base's index */ - if (!stack_empty(notit->stack)) { - stack_top(notit->stack)->index++; - } - -end: - return BT_BFCR_STATUS_OK; -} - -static -int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data) -{ - bt_field *seq_field; - struct bt_msg_iter *notit = data; - struct ctf_field_class_sequence *seq_fc = (void *) fc; - int64_t length = -1; - int ret; - - length = (uint64_t) g_array_index(notit->stored_values, uint64_t, - seq_fc->stored_length_index); - seq_field = stack_top(notit->stack)->base; - BT_ASSERT(seq_field); - - /* - * bfcr_get_sequence_length_cb() also gets called back for a - * text sequence, but the destination field is a string field. - * Only set the field's sequence length if the destination field - * is a sequence field. - */ - if (!seq_fc->base.is_text) { - BT_ASSERT(bt_field_get_class_type(seq_field) == - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); - ret = bt_field_dynamic_array_set_length(seq_field, - (uint64_t) length); - if (ret) { - BT_LOGE("Cannot set dynamic array field's length field: " - "notit-addr=%p, field-addr=%p, " - "length=%" PRIu64, notit, seq_field, length); - } - } - - return length; -} - -static -struct ctf_field_class *bfcr_borrow_variant_selected_field_class_cb( - struct ctf_field_class *fc, void *data) -{ - int ret; - uint64_t i; - int64_t option_index = -1; - struct bt_msg_iter *notit = data; - struct ctf_field_class_variant *var_fc = (void *) fc; - struct ctf_named_field_class *selected_option = NULL; - struct ctf_field_class *ret_fc = NULL; - union { - uint64_t u; - int64_t i; - } tag; - - /* Get variant's tag */ - tag.u = g_array_index(notit->stored_values, uint64_t, - var_fc->stored_tag_index); - - /* - * Check each range to find the selected option's index. - */ - if (var_fc->tag_fc->base.is_signed) { - for (i = 0; i < var_fc->ranges->len; i++) { - struct ctf_field_class_variant_range *range = - ctf_field_class_variant_borrow_range_by_index( - var_fc, i); - - if (tag.i >= range->range.lower.i && - tag.i <= range->range.upper.i) { - option_index = (int64_t) range->option_index; - break; - } - } - } else { - for (i = 0; i < var_fc->ranges->len; i++) { - struct ctf_field_class_variant_range *range = - ctf_field_class_variant_borrow_range_by_index( - var_fc, i); - - if (tag.u >= range->range.lower.u && - tag.u <= range->range.upper.u) { - option_index = (int64_t) range->option_index; - break; - } - } - } - - if (option_index < 0) { - BT_LOGW("Cannot find variant field class's option: " - "notit-addr=%p, var-fc-addr=%p, u-tag=%" PRIu64 ", " - "i-tag=%" PRId64, notit, var_fc, tag.u, tag.i); - goto end; - } - - selected_option = ctf_field_class_variant_borrow_option_by_index( - var_fc, (uint64_t) option_index); - - if (selected_option->fc->in_ir) { - bt_field *var_field = stack_top(notit->stack)->base; - - ret = bt_field_variant_select_option_field( - var_field, option_index); - if (ret) { - BT_LOGW("Cannot select variant field's option field: " - "notit-addr=%p, var-field-addr=%p, " - "opt-index=%" PRId64, notit, var_field, - option_index); - goto end; - } - } - - ret_fc = selected_option->fc; - -end: - return ret_fc; -} - -static -void create_msg_stream_beginning(struct bt_msg_iter *notit, - bt_message **message) -{ - bt_message *ret = NULL; - - BT_ASSERT(notit->stream); - BT_ASSERT(notit->msg_iter); - ret = bt_message_stream_beginning_create(notit->msg_iter, - notit->stream); - if (!ret) { - BT_LOGE("Cannot create stream beginning message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - *message = ret; -} - -static -void create_msg_stream_activity_beginning(struct bt_msg_iter *notit, - bt_message **message) -{ - bt_message *ret = NULL; - - BT_ASSERT(notit->stream); - BT_ASSERT(notit->msg_iter); - ret = bt_message_stream_activity_beginning_create(notit->msg_iter, - notit->stream); - if (!ret) { - BT_LOGE("Cannot create stream activity beginning message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - *message = ret; -} - -static -void create_msg_stream_activity_end(struct bt_msg_iter *notit, - bt_message **message) -{ - bt_message *ret = NULL; - - if (!notit->stream) { - BT_LOGE("Cannot create stream for stream message: " - "notit-addr=%p", notit); - return; - } - - BT_ASSERT(notit->stream); - BT_ASSERT(notit->msg_iter); - ret = bt_message_stream_activity_end_create(notit->msg_iter, - notit->stream); - if (!ret) { - BT_LOGE("Cannot create stream activity end message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - *message = ret; -} - -static -void create_msg_stream_end(struct bt_msg_iter *notit, bt_message **message) -{ - bt_message *ret; - - if (!notit->stream) { - BT_LOGE("Cannot create stream for stream message: " - "notit-addr=%p", notit); - return; - } - - BT_ASSERT(notit->msg_iter); - ret = bt_message_stream_end_create(notit->msg_iter, - notit->stream); - if (!ret) { - BT_LOGE("Cannot create stream end message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - *message = ret; -} - -static -void create_msg_packet_beginning(struct bt_msg_iter *notit, - bt_message **message) -{ - int ret; - enum bt_msg_iter_status status; - bt_message *msg = NULL; - const bt_stream_class *sc; - - status = set_current_packet(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - BT_ASSERT(notit->packet); - sc = notit->meta.sc->ir_sc; - BT_ASSERT(sc); - - if (notit->packet_context_field) { - ret = bt_packet_move_context_field( - notit->packet, notit->packet_context_field); - if (ret) { - goto end; - } - - notit->packet_context_field = NULL; - - /* - * At this point notit->dscopes.stream_packet_context - * has the same value as the packet context field within - * notit->packet. - */ - BT_ASSERT(bt_packet_borrow_context_field( - notit->packet) == - notit->dscopes.stream_packet_context); - } - - BT_ASSERT(notit->msg_iter); - - if (notit->meta.sc->packets_have_ts_begin) { - BT_ASSERT(notit->snapshots.beginning_clock != UINT64_C(-1)); - msg = bt_message_packet_beginning_create_with_default_clock_snapshot( - notit->msg_iter, notit->packet, - notit->snapshots.beginning_clock); - } else { - msg = bt_message_packet_beginning_create(notit->msg_iter, - notit->packet); - } - - if (!msg) { - BT_LOGE("Cannot create packet beginning message: " - "notit-addr=%p, packet-addr=%p", - notit, notit->packet); - goto end; - } - - *message = msg; - -end: - return; -} - -static -void create_msg_packet_end(struct bt_msg_iter *notit, bt_message **message) -{ - bt_message *msg; - - if (!notit->packet) { - return; - } - - /* Update default clock from packet's end time */ - if (notit->snapshots.end_clock != UINT64_C(-1)) { - notit->default_clock_snapshot = notit->snapshots.end_clock; - } - - BT_ASSERT(notit->msg_iter); - - if (notit->meta.sc->packets_have_ts_end) { - BT_ASSERT(notit->snapshots.end_clock != UINT64_C(-1)); - msg = bt_message_packet_end_create_with_default_clock_snapshot( - notit->msg_iter, notit->packet, - notit->snapshots.end_clock); - } else { - msg = bt_message_packet_end_create(notit->msg_iter, - notit->packet); - } - - if (!msg) { - BT_LOGE("Cannot create packet end message: " - "notit-addr=%p, packet-addr=%p", - notit, notit->packet); - return; - - } - - BT_PACKET_PUT_REF_AND_RESET(notit->packet); - *message = msg; -} - -static -void create_msg_discarded_events(struct bt_msg_iter *notit, - bt_message **message) -{ - bt_message *msg; - uint64_t beginning_raw_value = UINT64_C(-1); - uint64_t end_raw_value = UINT64_C(-1); - - BT_ASSERT(notit->msg_iter); - BT_ASSERT(notit->stream); - BT_ASSERT(notit->meta.sc->has_discarded_events); - - if (notit->meta.sc->discarded_events_have_default_cs) { - if (notit->prev_packet_snapshots.discarded_events == UINT64_C(-1)) { - /* - * We discarded events, but before (and possibly - * including) the current packet: use this packet's time - * range, and do not have a specific count. - */ - beginning_raw_value = notit->snapshots.beginning_clock; - end_raw_value = notit->snapshots.end_clock; - } else { - beginning_raw_value = notit->prev_packet_snapshots.end_clock; - end_raw_value = notit->snapshots.end_clock; - } - - BT_ASSERT(beginning_raw_value != UINT64_C(-1)); - BT_ASSERT(end_raw_value != UINT64_C(-1)); - msg = bt_message_discarded_events_create_with_default_clock_snapshots( - notit->msg_iter, notit->stream, beginning_raw_value, - end_raw_value); - } else { - msg = bt_message_discarded_events_create(notit->msg_iter, - notit->stream); - } - - if (!msg) { - BT_LOGE("Cannot create discarded events message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - if (notit->prev_packet_snapshots.discarded_events != UINT64_C(-1)) { - bt_message_discarded_events_set_count(msg, - notit->snapshots.discarded_events - - notit->prev_packet_snapshots.discarded_events); - } - - *message = msg; -} - -static -void create_msg_discarded_packets(struct bt_msg_iter *notit, - bt_message **message) -{ - bt_message *msg; - - BT_ASSERT(notit->msg_iter); - BT_ASSERT(notit->stream); - BT_ASSERT(notit->meta.sc->has_discarded_packets); - BT_ASSERT(notit->prev_packet_snapshots.packets != - UINT64_C(-1)); - - if (notit->meta.sc->discarded_packets_have_default_cs) { - BT_ASSERT(notit->prev_packet_snapshots.end_clock != UINT64_C(-1)); - BT_ASSERT(notit->snapshots.beginning_clock != UINT64_C(-1)); - msg = bt_message_discarded_packets_create_with_default_clock_snapshots( - notit->msg_iter, notit->stream, - notit->prev_packet_snapshots.end_clock, - notit->snapshots.beginning_clock); - } else { - msg = bt_message_discarded_packets_create(notit->msg_iter, - notit->stream); - } - - if (!msg) { - BT_LOGE("Cannot create discarded packets message: " - "notit-addr=%p, stream-addr=%p", - notit, notit->stream); - return; - } - - bt_message_discarded_packets_set_count(msg, - notit->snapshots.packets - - notit->prev_packet_snapshots.packets - 1); - *message = msg; -} - -BT_HIDDEN -struct bt_msg_iter *bt_msg_iter_create(struct ctf_trace_class *tc, - size_t max_request_sz, - struct bt_msg_iter_medium_ops medops, void *data) -{ - struct bt_msg_iter *notit = NULL; - struct bt_bfcr_cbs cbs = { - .classes = { - .signed_int = bfcr_signed_int_cb, - .unsigned_int = bfcr_unsigned_int_cb, - .floating_point = bfcr_floating_point_cb, - .string_begin = bfcr_string_begin_cb, - .string = bfcr_string_cb, - .string_end = bfcr_string_end_cb, - .compound_begin = bfcr_compound_begin_cb, - .compound_end = bfcr_compound_end_cb, - }, - .query = { - .get_sequence_length = bfcr_get_sequence_length_cb, - .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb, - }, - }; - - BT_ASSERT(tc); - BT_ASSERT(medops.request_bytes); - BT_ASSERT(medops.borrow_stream); - BT_LOGD("Creating CTF plugin message iterator: " - "trace-addr=%p, max-request-size=%zu, " - "data=%p", tc, max_request_sz, data); - notit = g_new0(struct bt_msg_iter, 1); - if (!notit) { - BT_LOGE_STR("Failed to allocate one CTF plugin message iterator."); - goto end; - } - notit->meta.tc = tc; - notit->medium.medops = medops; - notit->medium.max_request_sz = max_request_sz; - notit->medium.data = data; - notit->stack = stack_new(notit); - notit->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t)); - g_array_set_size(notit->stored_values, tc->stored_value_count); - - if (!notit->stack) { - BT_LOGE_STR("Failed to create field stack."); - goto error; - } - - notit->bfcr = bt_bfcr_create(cbs, notit); - if (!notit->bfcr) { - BT_LOGE_STR("Failed to create binary class reader (BFCR)."); - goto error; - } - - bt_msg_iter_reset(notit); - BT_LOGD("Created CTF plugin message iterator: " - "trace-addr=%p, max-request-size=%zu, " - "data=%p, notit-addr=%p", - tc, max_request_sz, data, notit); - notit->cur_packet_offset = 0; - -end: - return notit; - -error: - bt_msg_iter_destroy(notit); - notit = NULL; - goto end; -} - -void bt_msg_iter_destroy(struct bt_msg_iter *notit) -{ - BT_PACKET_PUT_REF_AND_RESET(notit->packet); - BT_STREAM_PUT_REF_AND_RESET(notit->stream); - release_all_dscopes(notit); - - BT_LOGD("Destroying CTF plugin message iterator: addr=%p", notit); - - if (notit->stack) { - BT_LOGD_STR("Destroying field stack."); - stack_destroy(notit->stack); - } - - if (notit->bfcr) { - BT_LOGD("Destroying BFCR: bfcr-addr=%p", notit->bfcr); - bt_bfcr_destroy(notit->bfcr); - } - - if (notit->stored_values) { - g_array_free(notit->stored_values, TRUE); - } - - g_free(notit); -} - -enum bt_msg_iter_status bt_msg_iter_get_next_message( - struct bt_msg_iter *notit, - bt_self_message_iterator *msg_iter, bt_message **message) -{ - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - BT_ASSERT(notit); - BT_ASSERT(message); - notit->msg_iter = msg_iter; - notit->set_stream = true; - BT_LOGV("Getting next message: notit-addr=%p", notit); - - while (true) { - status = handle_state(notit); - if (unlikely(status == BT_MSG_ITER_STATUS_AGAIN)) { - BT_LOGV_STR("Medium returned BT_MSG_ITER_STATUS_AGAIN."); - goto end; - } else if (unlikely(status != BT_MSG_ITER_STATUS_OK)) { - BT_LOGW("Cannot handle state: notit-addr=%p, state=%s", - notit, state_string(notit->state)); - goto end; - } - - switch (notit->state) { - case STATE_EMIT_MSG_EVENT: - BT_ASSERT(notit->event_msg); - *message = notit->event_msg; - notit->event_msg = NULL; - goto end; - case STATE_EMIT_MSG_DISCARDED_EVENTS: - /* create_msg_discared_events() logs errors */ - create_msg_discarded_events(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_DISCARDED_PACKETS: - /* create_msg_discared_packets() logs errors */ - create_msg_discarded_packets(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_PACKET_BEGINNING: - /* create_msg_packet_beginning() logs errors */ - create_msg_packet_beginning(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_PACKET_END_SINGLE: - case STATE_EMIT_MSG_PACKET_END_MULTI: - /* create_msg_packet_end() logs errors */ - create_msg_packet_end(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: - /* create_msg_stream_activity_beginning() logs errors */ - create_msg_stream_activity_beginning(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_STREAM_ACTIVITY_END: - /* create_msg_stream_activity_end() logs errors */ - create_msg_stream_activity_end(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_STREAM_BEGINNING: - /* create_msg_stream_beginning() logs errors */ - create_msg_stream_beginning(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_EMIT_MSG_STREAM_END: - /* create_msg_stream_end() logs errors */ - create_msg_stream_end(notit, message); - - if (!*message) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - goto end; - case STATE_DONE: - status = BT_MSG_ITER_STATUS_EOF; - goto end; - default: - /* Non-emitting state: continue */ - break; - } - } - -end: - return status; -} - -static -enum bt_msg_iter_status read_packet_header_context_fields( - struct bt_msg_iter *notit) -{ - int ret; - enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; - - BT_ASSERT(notit); - notit->set_stream = false; - - if (notit->state == STATE_EMIT_MSG_PACKET_BEGINNING) { - /* We're already there */ - goto end; - } - - while (true) { - status = handle_state(notit); - if (unlikely(status == BT_MSG_ITER_STATUS_AGAIN)) { - BT_LOGV_STR("Medium returned BT_MSG_ITER_STATUS_AGAIN."); - goto end; - } else if (unlikely(status != BT_MSG_ITER_STATUS_OK)) { - BT_LOGW("Cannot handle state: notit-addr=%p, state=%s", - notit, state_string(notit->state)); - goto end; - } - - switch (notit->state) { - case STATE_EMIT_MSG_PACKET_BEGINNING: - /* - * Packet header and context fields are - * potentially decoded (or they don't exist). - */ - goto end; - case STATE_INIT: - case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: - case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: - case STATE_AFTER_TRACE_PACKET_HEADER: - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: - case STATE_AFTER_STREAM_PACKET_CONTEXT: - case STATE_CHECK_EMIT_MSG_STREAM_BEGINNING: - case STATE_EMIT_MSG_STREAM_BEGINNING: - case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: - case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS: - case STATE_EMIT_MSG_DISCARDED_EVENTS: - case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS: - case STATE_EMIT_MSG_DISCARDED_PACKETS: - /* Non-emitting state: continue */ - break; - default: - /* - * We should never get past the - * STATE_EMIT_MSG_PACKET_BEGINNING state. - */ - BT_LOGF("Unexpected state: notit-addr=%p, state=%s", - notit, state_string(notit->state)); - abort(); - } - } - -end: - ret = set_current_packet_content_sizes(notit); - if (ret) { - status = BT_MSG_ITER_STATUS_ERROR; - } - - return status; -} - -BT_HIDDEN -void bt_msg_iter_set_medops_data(struct bt_msg_iter *notit, - void *medops_data) -{ - BT_ASSERT(notit); - notit->medium.data = medops_data; -} - -BT_HIDDEN -enum bt_msg_iter_status bt_msg_iter_seek(struct bt_msg_iter *notit, - off_t offset) -{ - enum bt_msg_iter_status ret = BT_MSG_ITER_STATUS_OK; - enum bt_msg_iter_medium_status medium_status; - - BT_ASSERT(notit); - if (offset < 0) { - BT_LOGE("Cannot seek to negative offset: offset=%jd", offset); - ret = BT_MSG_ITER_STATUS_INVAL; - goto end; - } - - if (!notit->medium.medops.seek) { - ret = BT_MSG_ITER_STATUS_UNSUPPORTED; - BT_LOGD("Aborting seek as the iterator's underlying media does not implement seek support."); - goto end; - } - - medium_status = notit->medium.medops.seek( - BT_MSG_ITER_SEEK_WHENCE_SET, offset, notit->medium.data); - if (medium_status != BT_MSG_ITER_MEDIUM_STATUS_OK) { - if (medium_status == BT_MSG_ITER_MEDIUM_STATUS_EOF) { - ret = BT_MSG_ITER_STATUS_EOF; - } else { - ret = BT_MSG_ITER_STATUS_ERROR; - goto end; - } - } - - bt_msg_iter_reset(notit); - notit->cur_packet_offset = offset; - -end: - return ret; -} - -BT_HIDDEN -enum bt_msg_iter_status bt_msg_iter_get_packet_properties( - struct bt_msg_iter *notit, - struct bt_msg_iter_packet_properties *props) -{ - enum bt_msg_iter_status status; - - BT_ASSERT(notit); - BT_ASSERT(props); - status = read_packet_header_context_fields(notit); - if (status != BT_MSG_ITER_STATUS_OK) { - goto end; - } - - props->exp_packet_total_size = notit->cur_exp_packet_total_size; - props->exp_packet_content_size = notit->cur_exp_packet_content_size; - props->stream_class_id = (uint64_t) notit->cur_stream_class_id; - props->data_stream_id = notit->cur_data_stream_id; - props->snapshots.discarded_events = notit->snapshots.discarded_events; - props->snapshots.packets = notit->snapshots.packets; - props->snapshots.beginning_clock = notit->snapshots.beginning_clock; - props->snapshots.end_clock = notit->snapshots.end_clock; - -end: - return status; -} - -BT_HIDDEN -void bt_msg_iter_set_emit_stream_beginning_message(struct bt_msg_iter *notit, - bool val) -{ - notit->emit_stream_begin_msg = val; -} - -BT_HIDDEN -void bt_msg_iter_set_emit_stream_end_message(struct bt_msg_iter *notit, - bool val) -{ - notit->emit_stream_end_msg = val; -} diff --git a/plugins/ctf/common/msg-iter/msg-iter.h b/plugins/ctf/common/msg-iter/msg-iter.h deleted file mode 100644 index c893fda1..00000000 --- a/plugins/ctf/common/msg-iter/msg-iter.h +++ /dev/null @@ -1,382 +0,0 @@ -#ifndef CTF_MSG_ITER_H -#define CTF_MSG_ITER_H - -/* - * Babeltrace - CTF message iterator - * - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "../metadata/ctf-meta.h" - -/** - * @file ctf-msg-iter.h - * - * CTF message iterator - * - * This is a common internal API used by CTF source plugins. It allows - * one to get messages from a user-provided medium. - */ - -/** - * Medium operations status codes. - */ -enum bt_msg_iter_medium_status { - /** - * End of file. - * - * The medium function called by the message iterator - * function reached the end of the file. - */ - BT_MSG_ITER_MEDIUM_STATUS_EOF = 1, - - /** - * There is no data available right now, try again later. - */ - BT_MSG_ITER_MEDIUM_STATUS_AGAIN = 11, - - /** Unsupported operation. */ - BT_MSG_ITER_MEDIUM_STATUS_UNSUPPORTED = -3, - - /** Invalid argument. */ - BT_MSG_ITER_MEDIUM_STATUS_INVAL = -2, - - /** General error. */ - BT_MSG_ITER_MEDIUM_STATUS_ERROR = -1, - - /** Everything okay. */ - BT_MSG_ITER_MEDIUM_STATUS_OK = 0, -}; - -/** - * CTF message iterator API status code. - */ -enum bt_msg_iter_status { - /** - * End of file. - * - * The medium function called by the message iterator - * function reached the end of the file. - */ - BT_MSG_ITER_STATUS_EOF = BT_MSG_ITER_MEDIUM_STATUS_EOF, - - /** - * There is no data available right now, try again later. - * - * Some condition resulted in the - * bt_msg_iter_medium_ops::request_bytes() user function not - * having access to any data now. You should retry calling the - * last called message iterator function once the situation - * is resolved. - */ - BT_MSG_ITER_STATUS_AGAIN = BT_MSG_ITER_MEDIUM_STATUS_AGAIN, - - /** Invalid argument. */ - BT_MSG_ITER_STATUS_INVAL = BT_MSG_ITER_MEDIUM_STATUS_INVAL, - - /** Unsupported operation. */ - BT_MSG_ITER_STATUS_UNSUPPORTED = BT_MSG_ITER_MEDIUM_STATUS_UNSUPPORTED, - - /** General error. */ - BT_MSG_ITER_STATUS_ERROR = BT_MSG_ITER_MEDIUM_STATUS_ERROR, - - /** Everything okay. */ - BT_MSG_ITER_STATUS_OK = 0, -}; - -/** - * CTF message iterator seek operation directives. - */ -enum bt_msg_iter_seek_whence { - /** - * Set the iterator's position to an absolute offset in the underlying - * medium. - */ - BT_MSG_ITER_SEEK_WHENCE_SET, -}; - -/** - * Medium operations. - * - * Those user functions are called by the message iterator - * functions to request medium actions. - */ -struct bt_msg_iter_medium_ops { - /** - * Returns the next byte buffer to be used by the binary file - * reader to deserialize binary data. - * - * This function \em must be defined. - * - * The purpose of this function is to return a buffer of bytes - * to the message iterator, of a maximum of \p request_sz - * bytes. If this function cannot return a buffer of at least - * \p request_sz bytes, it may return a smaller buffer. In - * either cases, \p buffer_sz must be set to the returned buffer - * size (in bytes). - * - * The returned buffer's ownership remains the medium, in that - * it won't be freed by the message iterator functions. The - * returned buffer won't be modified by the message - * iterator functions either. - * - * When this function is called for the first time for a given - * file, the offset within the file is considered to be 0. The - * next times this function is called, the returned buffer's - * byte offset within the complete file must be the previous - * offset plus the last returned value of \p buffer_sz by this - * medium. - * - * This function must return one of the following statuses: - * - * - #BT_MSG_ITER_MEDIUM_STATUS_OK: Everything - * is okay, i.e. \p buffer_sz is set to a positive value - * reflecting the number of available bytes in the buffer - * starting at the address written in \p buffer_addr. - * - #BT_MSG_ITER_MEDIUM_STATUS_AGAIN: No data is - * available right now. In this case, the message - * iterator function called by the user returns - * #BT_MSG_ITER_STATUS_AGAIN, and it is the user's - * responsibility to make sure enough data becomes available - * before calling the \em same message iterator - * function again to continue the decoding process. - * - #BT_MSG_ITER_MEDIUM_STATUS_EOF: The end of - * the file was reached, and no more data will ever be - * available for this file. In this case, the message - * iterator function called by the user returns - * #BT_MSG_ITER_STATUS_EOF. This must \em not be - * returned when returning at least one byte of data to the - * caller, i.e. this must be returned when there's - * absolutely nothing left; should the request size be - * larger than what's left in the file, this function must - * return what's left, setting \p buffer_sz to the number of - * remaining bytes, and return - * #BT_MSG_ITER_MEDIUM_STATUS_EOF on the \em following - * call. - * - #BT_MSG_ITER_MEDIUM_STATUS_ERROR: A fatal - * error occured during this operation. In this case, the - * message iterator function called by the user returns - * #BT_MSG_ITER_STATUS_ERROR. - * - * If #BT_MSG_ITER_MEDIUM_STATUS_OK is not returned, the - * values of \p buffer_sz and \p buffer_addr are \em ignored by - * the caller. - * - * @param request_sz Requested buffer size (bytes) - * @param buffer_addr Returned buffer address - * @param buffer_sz Returned buffer's size (bytes) - * @param data User data - * @returns Status code (see description above) - */ - enum bt_msg_iter_medium_status (* request_bytes)(size_t request_sz, - uint8_t **buffer_addr, size_t *buffer_sz, void *data); - - /** - * Repositions the underlying stream's position. - * - * This *optional* method repositions the underlying stream - * to a given absolute or relative position, as indicated by - * the whence directive. - * - * @param whence One of #bt_msg_iter_seek_whence values - * @param offset Offset to use for the given directive - * @param data User data - * @returns One of #bt_msg_iter_medium_status values - */ - enum bt_msg_iter_medium_status (* seek)( - enum bt_msg_iter_seek_whence whence, - off_t offset, void *data); - - /** - * Returns a stream instance (weak reference) for the given - * stream class. - * - * This is called after a packet header is read, and the - * corresponding stream class is found by the message - * iterator. - * - * @param stream_class Stream class of the stream to get - * @param stream_id Stream (instance) ID of the stream - * to get (-1ULL if not available) - * @param data User data - * @returns Stream instance (weak reference) or - * \c NULL on error - */ - bt_stream * (* borrow_stream)(bt_stream_class *stream_class, - int64_t stream_id, void *data); -}; - -/** CTF message iterator. */ -struct bt_msg_iter; - -/** - * Creates a CTF message iterator. - * - * Upon successful completion, the reference count of \p trace is - * incremented. - * - * @param trace Trace to read - * @param max_request_sz Maximum buffer size, in bytes, to - * request to - * bt_msg_iter_medium_ops::request_bytes() - * at a time - * @param medops Medium operations - * @param medops_data User data (passed to medium operations) - * @returns New CTF message iterator on - * success, or \c NULL on error - */ -BT_HIDDEN -struct bt_msg_iter *bt_msg_iter_create(struct ctf_trace_class *tc, - size_t max_request_sz, struct bt_msg_iter_medium_ops medops, - void *medops_data); - -/** - * Destroys a CTF message iterator, freeing all internal resources. - * - * The registered trace's reference count is decremented. - * - * @param msg_iter CTF message iterator - */ -BT_HIDDEN -void bt_msg_iter_destroy(struct bt_msg_iter *msg_iter); - -/** - * Returns the next message from a CTF message iterator. - * - * Upon successful completion, #BT_MSG_ITER_STATUS_OK is - * returned, and the next message is written to \p msg. - * In this case, the caller is responsible for calling - * bt_message_put() on the returned message. - * - * If this function returns #BT_MSG_ITER_STATUS_AGAIN, the caller - * should make sure that data becomes available to its medium, and - * call this function again, until another status is returned. - * - * @param msg_iter CTF message iterator - * @param message Returned message if the function's - * return value is #BT_MSG_ITER_STATUS_OK - * @returns One of #bt_msg_iter_status values - */ -BT_HIDDEN -enum bt_msg_iter_status bt_msg_iter_get_next_message( - struct bt_msg_iter *notit, - bt_self_message_iterator *msg_iter, - bt_message **message); - -struct bt_msg_iter_packet_properties { - int64_t exp_packet_total_size; - int64_t exp_packet_content_size; - uint64_t stream_class_id; - int64_t data_stream_id; - - struct { - uint64_t discarded_events; - uint64_t packets; - uint64_t beginning_clock; - uint64_t end_clock; - } snapshots; -}; - -BT_HIDDEN -enum bt_msg_iter_status bt_msg_iter_get_packet_properties( - struct bt_msg_iter *notit, - struct bt_msg_iter_packet_properties *props); - -BT_HIDDEN -void bt_msg_iter_set_medops_data(struct bt_msg_iter *notit, - void *medops_data); - -BT_HIDDEN -enum bt_msg_iter_status bt_msg_iter_seek( - struct bt_msg_iter *notit, off_t offset); - -/* - * Resets the iterator so that the next requested medium bytes are - * assumed to be the first bytes of a new stream. Depending on - * bt_msg_iter_set_emit_stream_beginning_message(), the first message - * which this iterator emits after calling bt_msg_iter_reset() is of - * type `BT_MESSAGE_TYPE_STREAM_BEGINNING`. - */ -BT_HIDDEN -void bt_msg_iter_reset(struct bt_msg_iter *notit); - -/* - * Like bt_msg_iter_reset(), but preserves stream-dependent state. - */ -BT_HIDDEN -void bt_msg_iter_reset_for_next_stream_file(struct bt_msg_iter *notit); - -BT_HIDDEN -void bt_msg_iter_set_emit_stream_beginning_message(struct bt_msg_iter *notit, - bool val); - -BT_HIDDEN -void bt_msg_iter_set_emit_stream_end_message(struct bt_msg_iter *notit, - bool val); - -static inline -const char *bt_msg_iter_medium_status_string( - enum bt_msg_iter_medium_status status) -{ - switch (status) { - case BT_MSG_ITER_MEDIUM_STATUS_EOF: - return "BT_MSG_ITER_MEDIUM_STATUS_EOF"; - case BT_MSG_ITER_MEDIUM_STATUS_AGAIN: - return "BT_MSG_ITER_MEDIUM_STATUS_AGAIN"; - case BT_MSG_ITER_MEDIUM_STATUS_INVAL: - return "BT_MSG_ITER_MEDIUM_STATUS_INVAL"; - case BT_MSG_ITER_MEDIUM_STATUS_ERROR: - return "BT_MSG_ITER_MEDIUM_STATUS_ERROR"; - case BT_MSG_ITER_MEDIUM_STATUS_OK: - return "BT_MSG_ITER_MEDIUM_STATUS_OK"; - default: - return "(unknown)"; - } -} - -static inline -const char *bt_msg_iter_status_string( - enum bt_msg_iter_status status) -{ - switch (status) { - case BT_MSG_ITER_STATUS_EOF: - return "BT_MSG_ITER_STATUS_EOF"; - case BT_MSG_ITER_STATUS_AGAIN: - return "BT_MSG_ITER_STATUS_AGAIN"; - case BT_MSG_ITER_STATUS_INVAL: - return "BT_MSG_ITER_STATUS_INVAL"; - case BT_MSG_ITER_STATUS_ERROR: - return "BT_MSG_ITER_STATUS_ERROR"; - case BT_MSG_ITER_STATUS_OK: - return "BT_MSG_ITER_STATUS_OK"; - default: - return "(unknown)"; - } -} - -#endif /* CTF_MSG_ITER_H */ diff --git a/plugins/ctf/common/print.h b/plugins/ctf/common/print.h deleted file mode 100644 index a19dbffa..00000000 --- a/plugins/ctf/common/print.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CTF_BTR_PRINT_H -#define CTF_BTR_PRINT_H - -/* - * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#define PERR(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "Error: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PWARN(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "Warning: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PDBG(fmt, ...) \ - do { \ - if (babeltrace_debug) { \ - fprintf(stderr, \ - "Debug: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#endif /* CTF_BTR_PRINT_H */ diff --git a/plugins/ctf/common/utils/Makefile.am b/plugins/ctf/common/utils/Makefile.am deleted file mode 100644 index c4b62ff5..00000000 --- a/plugins/ctf/common/utils/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -noinst_LTLIBRARIES = libctf-utils.la -libctf_utils_la_SOURCES = \ - logging.c \ - logging.h \ - utils.c \ - utils.h diff --git a/plugins/ctf/common/utils/logging.c b/plugins/ctf/common/utils/logging.c deleted file mode 100644 index 9a5e1e50..00000000 --- a/plugins/ctf/common/utils/logging.c +++ /dev/null @@ -1,26 +0,0 @@ - /* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL utils_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(utils_log_level, "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL"); diff --git a/plugins/ctf/common/utils/logging.h b/plugins/ctf/common/utils/logging.h deleted file mode 100644 index a5f51675..00000000 --- a/plugins/ctf/common/utils/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CTF_UTILS_LOGGING_H -#define CTF_UTILS_LOGGING_H - -/* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL utils_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(utils_log_level); - -#endif /* CTF_UTILS_LOGGING_H */ diff --git a/plugins/ctf/common/utils/utils.c b/plugins/ctf/common/utils/utils.c deleted file mode 100644 index dd65f941..00000000 --- a/plugins/ctf/common/utils/utils.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Babeltrace - CTF Utils - * - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-UTILS" -#include "logging.h" - -#include "utils.h" diff --git a/plugins/ctf/common/utils/utils.h b/plugins/ctf/common/utils/utils.h deleted file mode 100644 index 266ae7cf..00000000 --- a/plugins/ctf/common/utils/utils.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CTF_UTILS_H -#define CTF_UTILS_H - -/* - * Babeltrace - CTF Utilities - * - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#endif /* CTF_UTILS_H */ diff --git a/plugins/ctf/fs-sink/Makefile.am b/plugins/ctf/fs-sink/Makefile.am deleted file mode 100644 index 6035b1a1..00000000 --- a/plugins/ctf/fs-sink/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-sink.la - -libbabeltrace2_plugin_ctf_fs_sink_la_LIBADD = -libbabeltrace2_plugin_ctf_fs_sink_la_SOURCES = \ - fs-sink.c \ - fs-sink.h \ - logging.c \ - logging.h \ - fs-sink-ctf-meta.h \ - translate-trace-ir-to-ctf-ir.c \ - translate-trace-ir-to-ctf-ir.h \ - translate-ctf-ir-to-tsdl.c \ - translate-ctf-ir-to-tsdl.h \ - fs-sink-stream.c \ - fs-sink-stream.h \ - fs-sink-trace.c \ - fs-sink-trace.h diff --git a/plugins/ctf/fs-sink/fs-sink-ctf-meta.h b/plugins/ctf/fs-sink/fs-sink-ctf-meta.h deleted file mode 100644 index 2070c39a..00000000 --- a/plugins/ctf/fs-sink/fs-sink-ctf-meta.h +++ /dev/null @@ -1,926 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H - -/* - * Copyright 2018-2019 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum fs_sink_ctf_field_class_type { - FS_SINK_CTF_FIELD_CLASS_TYPE_INT, - FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT, - FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, - FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, - FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, - FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, - FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, -}; - -struct fs_sink_ctf_field_class { - enum fs_sink_ctf_field_class_type type; - - /* Weak */ - const bt_field_class *ir_fc; - - unsigned int alignment; - - /* Index of the field class within its own parent */ - uint64_t index_in_parent; -}; - -struct fs_sink_ctf_field_class_bit_array { - struct fs_sink_ctf_field_class base; - unsigned int size; -}; - -struct fs_sink_ctf_field_class_int { - struct fs_sink_ctf_field_class_bit_array base; - bool is_signed; -}; - -struct fs_sink_ctf_field_class_float { - struct fs_sink_ctf_field_class_bit_array base; -}; - -struct fs_sink_ctf_field_class_string { - struct fs_sink_ctf_field_class base; -}; - -struct fs_sink_ctf_named_field_class { - GString *name; - - /* Owned by this */ - struct fs_sink_ctf_field_class *fc; -}; - -struct fs_sink_ctf_field_class_struct { - struct fs_sink_ctf_field_class base; - - /* Array of `struct fs_sink_ctf_named_field_class` */ - GArray *members; -}; - -struct fs_sink_ctf_field_class_variant { - struct fs_sink_ctf_field_class base; - GString *tag_ref; - bool tag_is_before; - - /* Array of `struct fs_sink_ctf_named_field_class` */ - GArray *options; -}; - -struct fs_sink_ctf_field_class_array_base { - struct fs_sink_ctf_field_class base; - struct fs_sink_ctf_field_class *elem_fc; -}; - -struct fs_sink_ctf_field_class_array { - struct fs_sink_ctf_field_class_array_base base; - uint64_t length; -}; - -struct fs_sink_ctf_field_class_sequence { - struct fs_sink_ctf_field_class_array_base base; - GString *length_ref; - bool length_is_before; -}; - -struct fs_sink_ctf_stream_class; - -struct fs_sink_ctf_event_class { - /* Weak */ - const bt_event_class *ir_ec; - - /* Weak */ - struct fs_sink_ctf_stream_class *sc; - - /* Owned by this */ - struct fs_sink_ctf_field_class *spec_context_fc; - - /* Owned by this */ - struct fs_sink_ctf_field_class *payload_fc; -}; - -struct fs_sink_ctf_trace_class; - -struct fs_sink_ctf_stream_class { - /* Weak */ - struct fs_sink_ctf_trace_class *tc; - - /* Weak */ - const bt_stream_class *ir_sc; - - /* Weak */ - const bt_clock_class *default_clock_class; - - GString *default_clock_class_name; - bool packets_have_ts_begin; - bool packets_have_ts_end; - bool has_discarded_events; - bool discarded_events_has_ts; - bool discarded_packets_has_ts; - - /* Owned by this */ - struct fs_sink_ctf_field_class *packet_context_fc; - - /* Owned by this */ - struct fs_sink_ctf_field_class *event_common_context_fc; - - /* Array of `struct fs_sink_ctf_event_class *` (owned by this) */ - GPtrArray *event_classes; - - /* - * `const bt_event_class *` (weak) -> - * `struct fs_sink_ctf_event_class *` (weak) - */ - GHashTable *event_classes_from_ir; -}; - -struct fs_sink_ctf_trace_class { - /* Weak */ - const bt_trace_class *ir_tc; - - unsigned char uuid[BABELTRACE_UUID_LEN]; - - /* Array of `struct fs_sink_ctf_stream_class *` (owned by this) */ - GPtrArray *stream_classes; -}; - -static inline -void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc); - -static inline -void _fs_sink_ctf_field_class_init(struct fs_sink_ctf_field_class *fc, - enum fs_sink_ctf_field_class_type type, - const bt_field_class *ir_fc, unsigned int alignment, - uint64_t index_in_parent) -{ - BT_ASSERT(fc); - fc->type = type; - fc->ir_fc = ir_fc; - fc->alignment = alignment; - fc->index_in_parent = index_in_parent; -} - -static inline -void _fs_sink_ctf_field_class_bit_array_init( - struct fs_sink_ctf_field_class_bit_array *fc, - enum fs_sink_ctf_field_class_type type, - const bt_field_class *ir_fc, unsigned int size, - uint64_t index_in_parent) -{ - _fs_sink_ctf_field_class_init((void *) fc, type, ir_fc, - size % 8 == 0 ? 8 : 1, index_in_parent); - fc->size = size; -} - -static inline -void _fs_sink_ctf_field_class_int_init(struct fs_sink_ctf_field_class_int *fc, - enum fs_sink_ctf_field_class_type type, - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - bt_field_class_type ir_fc_type = bt_field_class_get_type(ir_fc); - - _fs_sink_ctf_field_class_bit_array_init((void *) fc, type, ir_fc, - (unsigned int) bt_field_class_integer_get_field_value_range( - ir_fc), - index_in_parent); - fc->is_signed = (ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || - ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); -} - -static inline -void _fs_sink_ctf_named_field_class_init( - struct fs_sink_ctf_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - named_fc->name = g_string_new(NULL); - BT_ASSERT(named_fc->name); -} - -static inline -void _fs_sink_ctf_named_field_class_fini( - struct fs_sink_ctf_named_field_class *named_fc) -{ - BT_ASSERT(named_fc); - - if (named_fc->name) { - g_string_free(named_fc->name, TRUE); - named_fc->name = NULL; - } - - fs_sink_ctf_field_class_destroy(named_fc->fc); - named_fc->fc = NULL; -} - -static inline -struct fs_sink_ctf_field_class_int *fs_sink_ctf_field_class_int_create( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_int *fc = - g_new0(struct fs_sink_ctf_field_class_int, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_int_init(fc, FS_SINK_CTF_FIELD_CLASS_TYPE_INT, - ir_fc, index_in_parent); - return fc; -} - -static inline -struct fs_sink_ctf_field_class_float *fs_sink_ctf_field_class_float_create( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_float *fc = - g_new0(struct fs_sink_ctf_field_class_float, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_bit_array_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT, - ir_fc, bt_field_class_real_is_single_precision(ir_fc) ? 32 : 64, - index_in_parent); - return fc; -} - -static inline -struct fs_sink_ctf_field_class_string *fs_sink_ctf_field_class_string_create( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_string *fc = - g_new0(struct fs_sink_ctf_field_class_string, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, ir_fc, - 8, index_in_parent); - return fc; -} - -static inline -struct fs_sink_ctf_field_class_struct *fs_sink_ctf_field_class_struct_create_empty( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_struct *fc = - g_new0(struct fs_sink_ctf_field_class_struct, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, ir_fc, 1, index_in_parent); - fc->members = g_array_new(FALSE, TRUE, - sizeof(struct fs_sink_ctf_named_field_class)); - BT_ASSERT(fc->members); - return fc; -} - -static inline -struct fs_sink_ctf_field_class_variant *fs_sink_ctf_field_class_variant_create_empty( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_variant *fc = - g_new0(struct fs_sink_ctf_field_class_variant, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, ir_fc, - 1, index_in_parent); - fc->options = g_array_new(FALSE, TRUE, - sizeof(struct fs_sink_ctf_named_field_class)); - BT_ASSERT(fc->options); - fc->tag_ref = g_string_new(NULL); - BT_ASSERT(fc->tag_ref); - fc->tag_is_before = - bt_field_class_variant_borrow_selector_field_path_const(ir_fc) == - NULL; - return fc; -} - -static inline -struct fs_sink_ctf_field_class_array *fs_sink_ctf_field_class_array_create_empty( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_array *fc = - g_new0(struct fs_sink_ctf_field_class_array, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc, - 1, index_in_parent); - fc->length = bt_field_class_static_array_get_length(ir_fc); - return fc; -} - -static inline -struct fs_sink_ctf_field_class_sequence *fs_sink_ctf_field_class_sequence_create_empty( - const bt_field_class *ir_fc, uint64_t index_in_parent) -{ - struct fs_sink_ctf_field_class_sequence *fc = - g_new0(struct fs_sink_ctf_field_class_sequence, 1); - - BT_ASSERT(fc); - _fs_sink_ctf_field_class_init((void *) fc, - FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, - ir_fc, 1, index_in_parent); - fc->length_ref = g_string_new(NULL); - BT_ASSERT(fc->length_ref); - fc->length_is_before = - bt_field_class_dynamic_array_borrow_length_field_path_const(ir_fc) == - NULL; - return fc; -} - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_struct_borrow_member_by_index( - struct fs_sink_ctf_field_class_struct *fc, uint64_t index); - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_variant_borrow_option_by_index( - struct fs_sink_ctf_field_class_variant *fc, uint64_t index); - -static inline -void _fs_sink_ctf_field_class_fini(struct fs_sink_ctf_field_class *fc) -{ - BT_ASSERT(fc); -} - -static inline -void _fs_sink_ctf_field_class_int_destroy( - struct fs_sink_ctf_field_class_int *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_float_destroy( - struct fs_sink_ctf_field_class_float *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_string_destroy( - struct fs_sink_ctf_field_class_string *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_struct_destroy( - struct fs_sink_ctf_field_class_struct *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - - if (fc->members) { - uint64_t i; - - for (i = 0; i < fc->members->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_struct_borrow_member_by_index( - fc, i); - - _fs_sink_ctf_named_field_class_fini(named_fc); - } - - g_array_free(fc->members, TRUE); - fc->members = NULL; - } - - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_array_base_fini( - struct fs_sink_ctf_field_class_array_base *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - fs_sink_ctf_field_class_destroy(fc->elem_fc); - fc->elem_fc = NULL; -} - -static inline -void _fs_sink_ctf_field_class_array_destroy( - struct fs_sink_ctf_field_class_array *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_array_base_fini((void *) fc); - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_sequence_destroy( - struct fs_sink_ctf_field_class_sequence *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_array_base_fini((void *) fc); - - if (fc->length_ref) { - g_string_free(fc->length_ref, TRUE); - fc->length_ref = NULL; - } - - g_free(fc); -} - -static inline -void _fs_sink_ctf_field_class_variant_destroy( - struct fs_sink_ctf_field_class_variant *fc) -{ - BT_ASSERT(fc); - _fs_sink_ctf_field_class_fini((void *) fc); - - if (fc->options) { - uint64_t i; - - for (i = 0; i < fc->options->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_variant_borrow_option_by_index( - fc, i); - - _fs_sink_ctf_named_field_class_fini(named_fc); - } - - g_array_free(fc->options, TRUE); - fc->options = NULL; - } - - if (fc->tag_ref) { - g_string_free(fc->tag_ref, TRUE); - fc->tag_ref = NULL; - } - - g_free(fc); -} - -static inline -void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc) -{ - if (!fc) { - return; - } - - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: - _fs_sink_ctf_field_class_int_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: - _fs_sink_ctf_field_class_float_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: - _fs_sink_ctf_field_class_string_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - _fs_sink_ctf_field_class_struct_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - _fs_sink_ctf_field_class_array_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - _fs_sink_ctf_field_class_sequence_destroy((void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - _fs_sink_ctf_field_class_variant_destroy((void *) fc); - break; - default: - abort(); - } -} - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_struct_borrow_member_by_index( - struct fs_sink_ctf_field_class_struct *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->members->len); - return &g_array_index(fc->members, struct fs_sink_ctf_named_field_class, - index); -} - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_struct_borrow_member_by_name( - struct fs_sink_ctf_field_class_struct *fc, const char *name) -{ - uint64_t i; - struct fs_sink_ctf_named_field_class *ret_named_fc = NULL; - - BT_ASSERT(fc); - BT_ASSERT(name); - - for (i = 0; i < fc->members->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_struct_borrow_member_by_index( - fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_named_fc = named_fc; - goto end; - } - } - -end: - return ret_named_fc; -} - -static inline -struct fs_sink_ctf_field_class * -fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name( - struct fs_sink_ctf_field_class_struct *struct_fc, const char *name) -{ - struct fs_sink_ctf_named_field_class *named_fc = NULL; - struct fs_sink_ctf_field_class *fc = NULL; - - if (!struct_fc) { - goto end; - } - - named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_name( - struct_fc, name); - if (!named_fc) { - goto end; - } - - fc = named_fc->fc; - -end: - return fc; -} - -static inline -struct fs_sink_ctf_field_class_int * -fs_sink_ctf_field_class_struct_borrow_member_int_field_class_by_name( - struct fs_sink_ctf_field_class_struct *struct_fc, - const char *name) -{ - struct fs_sink_ctf_field_class_int *int_fc = NULL; - - int_fc = (void *) - fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name( - struct_fc, name); - if (!int_fc) { - goto end; - } - - if (int_fc->base.base.type != FS_SINK_CTF_FIELD_CLASS_TYPE_INT) { - int_fc = NULL; - goto end; - } - -end: - return int_fc; -} - -static inline -void fs_sink_ctf_field_class_struct_align_at_least( - struct fs_sink_ctf_field_class_struct *fc, - unsigned int alignment) -{ - if (alignment > fc->base.alignment) { - fc->base.alignment = alignment; - } -} - -static inline -void fs_sink_ctf_field_class_struct_append_member( - struct fs_sink_ctf_field_class_struct *fc, - const char *name, struct fs_sink_ctf_field_class *member_fc) -{ - struct fs_sink_ctf_named_field_class *named_fc; - - BT_ASSERT(fc); - BT_ASSERT(name); - g_array_set_size(fc->members, fc->members->len + 1); - - named_fc = &g_array_index(fc->members, - struct fs_sink_ctf_named_field_class, fc->members->len - 1); - _fs_sink_ctf_named_field_class_init(named_fc); - g_string_assign(named_fc->name, name); - named_fc->fc = member_fc; - fs_sink_ctf_field_class_struct_align_at_least(fc, member_fc->alignment); -} - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_variant_borrow_option_by_index( - struct fs_sink_ctf_field_class_variant *fc, uint64_t index) -{ - BT_ASSERT(fc); - BT_ASSERT(index < fc->options->len); - return &g_array_index(fc->options, struct fs_sink_ctf_named_field_class, - index); -} - -static inline -struct fs_sink_ctf_named_field_class * -fs_sink_ctf_field_class_variant_borrow_option_by_name( - struct fs_sink_ctf_field_class_variant *fc, const char *name) -{ - uint64_t i; - struct fs_sink_ctf_named_field_class *ret_named_fc = NULL; - - BT_ASSERT(fc); - BT_ASSERT(name); - - for (i = 0; i < fc->options->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_variant_borrow_option_by_index( - fc, i); - - if (strcmp(name, named_fc->name->str) == 0) { - ret_named_fc = named_fc; - goto end; - } - } - -end: - return ret_named_fc; -} - -static inline -void fs_sink_ctf_field_class_variant_append_option( - struct fs_sink_ctf_field_class_variant *fc, - const char *name, struct fs_sink_ctf_field_class *option_fc) -{ - struct fs_sink_ctf_named_field_class *named_fc; - - BT_ASSERT(fc); - BT_ASSERT(name); - g_array_set_size(fc->options, fc->options->len + 1); - - named_fc = &g_array_index(fc->options, - struct fs_sink_ctf_named_field_class, fc->options->len - 1); - _fs_sink_ctf_named_field_class_init(named_fc); - g_string_assign(named_fc->name, name); - named_fc->fc = option_fc; -} - -static inline -struct fs_sink_ctf_event_class *fs_sink_ctf_event_class_create( - struct fs_sink_ctf_stream_class *sc, - const bt_event_class *ir_ec) -{ - struct fs_sink_ctf_event_class *ec = - g_new0(struct fs_sink_ctf_event_class, 1); - - BT_ASSERT(sc); - BT_ASSERT(ir_ec); - BT_ASSERT(ec); - ec->ir_ec = ir_ec; - ec->sc = sc; - g_ptr_array_add(sc->event_classes, ec); - g_hash_table_insert(sc->event_classes_from_ir, (gpointer) ir_ec, ec); - return ec; -} - -static inline -void fs_sink_ctf_event_class_destroy(struct fs_sink_ctf_event_class *ec) -{ - if (!ec) { - return; - } - - fs_sink_ctf_field_class_destroy(ec->spec_context_fc); - ec->spec_context_fc = NULL; - fs_sink_ctf_field_class_destroy(ec->payload_fc); - ec->payload_fc = NULL; - g_free(ec); -} - -static inline -struct fs_sink_ctf_stream_class *fs_sink_ctf_stream_class_create( - struct fs_sink_ctf_trace_class *tc, - const bt_stream_class *ir_sc) -{ - struct fs_sink_ctf_stream_class *sc = - g_new0(struct fs_sink_ctf_stream_class, 1); - - BT_ASSERT(tc); - BT_ASSERT(ir_sc); - BT_ASSERT(sc); - sc->tc = tc; - sc->ir_sc = ir_sc; - sc->default_clock_class = - bt_stream_class_borrow_default_clock_class_const(ir_sc); - sc->default_clock_class_name = g_string_new(NULL); - BT_ASSERT(sc->default_clock_class_name); - sc->event_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) fs_sink_ctf_event_class_destroy); - BT_ASSERT(sc->event_classes); - sc->event_classes_from_ir = g_hash_table_new(g_direct_hash, - g_direct_equal); - BT_ASSERT(sc->event_classes_from_ir); - sc->packets_have_ts_begin = - bt_stream_class_packets_have_beginning_default_clock_snapshot( - ir_sc); - sc->packets_have_ts_end = - bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc); - sc->has_discarded_events = - bt_stream_class_supports_discarded_events(ir_sc); - - if (sc->has_discarded_events) { - sc->discarded_events_has_ts = - bt_stream_class_discarded_events_have_default_clock_snapshots( - ir_sc); - } - - if (bt_stream_class_supports_discarded_packets(ir_sc)) { - sc->discarded_packets_has_ts = - bt_stream_class_discarded_packets_have_default_clock_snapshots( - ir_sc); - } - - g_ptr_array_add(tc->stream_classes, sc); - return sc; -} - -static inline -void fs_sink_ctf_stream_class_destroy(struct fs_sink_ctf_stream_class *sc) -{ - if (!sc) { - return; - } - - if (sc->default_clock_class_name) { - g_string_free(sc->default_clock_class_name, TRUE); - sc->default_clock_class_name = NULL; - } - - if (sc->event_classes) { - g_ptr_array_free(sc->event_classes, TRUE); - sc->event_classes = NULL; - } - - if (sc->event_classes_from_ir) { - g_hash_table_destroy(sc->event_classes_from_ir); - sc->event_classes_from_ir = NULL; - } - - fs_sink_ctf_field_class_destroy(sc->packet_context_fc); - sc->packet_context_fc = NULL; - fs_sink_ctf_field_class_destroy(sc->event_common_context_fc); - sc->event_common_context_fc = NULL; - g_free(sc); -} - -static inline -void fs_sink_ctf_stream_class_append_event_class( - struct fs_sink_ctf_stream_class *sc, - struct fs_sink_ctf_event_class *ec) -{ - g_ptr_array_add(sc->event_classes, ec); -} - -static inline -void fs_sink_ctf_trace_class_destroy(struct fs_sink_ctf_trace_class *tc) -{ - if (!tc) { - return; - } - - if (tc->stream_classes) { - g_ptr_array_free(tc->stream_classes, TRUE); - tc->stream_classes = NULL; - } - - g_free(tc); -} - -static inline -struct fs_sink_ctf_trace_class *fs_sink_ctf_trace_class_create( - const bt_trace_class *ir_tc) -{ - struct fs_sink_ctf_trace_class *tc = - g_new0(struct fs_sink_ctf_trace_class, 1); - - BT_ASSERT(tc); - - if (bt_uuid_generate(tc->uuid)) { - fs_sink_ctf_trace_class_destroy(tc); - tc = NULL; - goto end; - } - - tc->ir_tc = ir_tc; - tc->stream_classes = g_ptr_array_new_with_free_func( - (GDestroyNotify) fs_sink_ctf_stream_class_destroy); - BT_ASSERT(tc->stream_classes); - -end: - return tc; -} - -static inline -bool fs_sink_ctf_ist_valid_identifier(const char *name) -{ - const char *at; - uint64_t i; - bool ist_valid = true; - static const char *reserved_keywords[] = { - "align", - "callsite", - "const", - "char", - "clock", - "double", - "enum", - "env", - "event", - "floating_point", - "float", - "integer", - "int", - "long", - "short", - "signed", - "stream", - "string", - "struct", - "trace", - "typealias", - "typedef", - "unsigned", - "variant", - "void", - "_Bool", - "_Complex", - "_Imaginary", - }; - - /* Make sure the name is not a reserved keyword */ - for (i = 0; i < sizeof(reserved_keywords) / sizeof(*reserved_keywords); - i++) { - if (strcmp(name, reserved_keywords[i]) == 0) { - ist_valid = false; - goto end; - } - } - - /* Make sure the name is not an empty string */ - if (strlen(name) == 0) { - ist_valid = false; - goto end; - } - - /* Make sure the name starts with a letter or `_` */ - if (!isalpha(name[0]) && name[0] != '_') { - ist_valid = false; - goto end; - } - - /* Make sure the name only contains letters, digits, and `_` */ - for (at = name; *at != '\0'; at++) { - if (!isalnum(*at) && *at != '_') { - ist_valid = false; - goto end; - } - } - -end: - return ist_valid; -} - -static inline -int fs_sink_ctf_protect_name(GString *name) -{ - int ret = 0; - - if (!fs_sink_ctf_ist_valid_identifier(name->str)) { - ret = -1; - goto end; - } - - /* Prepend `_` to protect it */ - g_string_prepend_c(name, '_'); - -end: - return ret; -} - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H */ diff --git a/plugins/ctf/fs-sink/fs-sink-stream.c b/plugins/ctf/fs-sink/fs-sink-stream.c deleted file mode 100644 index edf8dd03..00000000 --- a/plugins/ctf/fs-sink/fs-sink-stream.c +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-STREAM" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "fs-sink-trace.h" -#include "fs-sink-stream.h" -#include "translate-trace-ir-to-ctf-ir.h" - -BT_HIDDEN -void fs_sink_stream_destroy(struct fs_sink_stream *stream) -{ - if (!stream) { - goto end; - } - - bt_ctfser_fini(&stream->ctfser); - - if (stream->file_name) { - g_string_free(stream->file_name, TRUE); - stream->file_name = NULL; - } - - bt_packet_put_ref(stream->packet_state.packet); - g_free(stream); - -end: - return; -} - -static -bool stream_file_name_exists(struct fs_sink_trace *trace, const char *name) -{ - bool exists = false; - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init(&iter, trace->streams); - - while (g_hash_table_iter_next(&iter, &key, &value)) { - struct fs_sink_stream *stream = value; - - if (strcmp(name, stream->file_name->str) == 0) { - exists = true; - goto end; - } - } - -end: - return exists; -} - -static -GString *sanitize_stream_file_name(const char *file_name) -{ - GString *san_file_name = g_string_new(NULL); - const char *ch; - gchar *basename; - - BT_ASSERT(san_file_name); - BT_ASSERT(file_name); - basename = g_path_get_basename(file_name); - - for (ch = basename; *ch != '\0'; ch++) { - if (*ch == '/') { - g_string_append_c(san_file_name, '_'); - } else { - g_string_append_c(san_file_name, *ch); - } - } - - /* Do not allow `.` and `..` either */ - if (strcmp(san_file_name->str, ".") == 0 || - strcmp(san_file_name->str, "..") == 0) { - g_string_assign(san_file_name, "stream"); - } - - g_free(basename); - return san_file_name; -} - -static -GString *make_unique_stream_file_name(struct fs_sink_trace *trace, - const char *base) -{ - GString *san_base = sanitize_stream_file_name(base); - GString *name = g_string_new(san_base->str); - unsigned int suffix = 0; - - BT_ASSERT(name); - - while (stream_file_name_exists(trace, name->str) && - strcmp(name->str, "metadata") == 0) { - g_string_printf(name, "%s-%u", san_base->str, suffix); - suffix++; - } - - g_string_free(san_base, TRUE); - return name; -} - -static -void set_stream_file_name(struct fs_sink_stream *stream) -{ - const char *base_name = bt_stream_get_name(stream->ir_stream); - - if (!base_name) { - base_name = "stream"; - } - - BT_ASSERT(!stream->file_name); - stream->file_name = make_unique_stream_file_name(stream->trace, - base_name); -} - -BT_HIDDEN -struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace, - const bt_stream *ir_stream) -{ - struct fs_sink_stream *stream = g_new0(struct fs_sink_stream, 1); - int ret; - GString *path = g_string_new(trace->path->str); - - if (!stream) { - goto end; - } - - stream->trace = trace; - stream->ir_stream = ir_stream; - stream->packet_state.beginning_cs = UINT64_C(-1); - stream->packet_state.end_cs = UINT64_C(-1); - stream->prev_packet_state.end_cs = UINT64_C(-1); - stream->prev_packet_state.discarded_events_counter = UINT64_C(-1); - stream->prev_packet_state.seq_num = UINT64_C(-1); - ret = try_translate_stream_class_trace_ir_to_ctf_ir(trace->tc, - bt_stream_borrow_class_const(ir_stream), &stream->sc); - if (ret) { - goto error; - } - - set_stream_file_name(stream); - g_string_append_printf(path, "/%s", stream->file_name->str); - ret = bt_ctfser_init(&stream->ctfser, path->str); - if (ret) { - goto error; - } - - g_hash_table_insert(trace->streams, (gpointer) ir_stream, stream); - goto end; - -error: - fs_sink_stream_destroy(stream); - stream = NULL; - -end: - if (path) { - g_string_free(path, TRUE); - } - - return stream; -} - -static -int write_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class *fc, const bt_field *field); - -static inline -int write_int_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_int *fc, const bt_field *field) -{ - int ret; - - if (fc->is_signed) { - ret = bt_ctfser_write_signed_int(&stream->ctfser, - bt_field_signed_integer_get_value(field), - fc->base.base.alignment, fc->base.size, BYTE_ORDER); - } else { - ret = bt_ctfser_write_unsigned_int(&stream->ctfser, - bt_field_unsigned_integer_get_value(field), - fc->base.base.alignment, fc->base.size, BYTE_ORDER); - } - - return ret; -} - -static inline -int write_float_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_float *fc, const bt_field *field) -{ - int ret; - double val = bt_field_real_get_value(field); - - if (fc->base.size == 32) { - ret = bt_ctfser_write_float32(&stream->ctfser, val, - fc->base.base.alignment, BYTE_ORDER); - } else { - ret = bt_ctfser_write_float64(&stream->ctfser, val, - fc->base.base.alignment, BYTE_ORDER); - } - - return ret; -} - -static inline -int write_string_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_string *fc, const bt_field *field) -{ - return bt_ctfser_write_string(&stream->ctfser, - bt_field_string_get_value(field)); -} - -static inline -int write_array_field_elements(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_array_base *fc, - const bt_field *field) -{ - uint64_t i; - uint64_t len = bt_field_array_get_length(field); - int ret = 0; - - for (i = 0; i < len; i++) { - const bt_field *elem_field = - bt_field_array_borrow_element_field_by_index_const( - field, i); - ret = write_field(stream, fc->elem_fc, elem_field); - if (unlikely(ret)) { - goto end; - } - } - -end: - return ret; -} - -static inline -int write_sequence_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_sequence *fc, - const bt_field *field) -{ - int ret; - - if (fc->length_is_before) { - ret = bt_ctfser_write_unsigned_int(&stream->ctfser, - bt_field_array_get_length(field), 8, 32, BYTE_ORDER); - if (unlikely(ret)) { - goto end; - } - } - - ret = write_array_field_elements(stream, (void *) fc, field); - -end: - return ret; -} - -static inline -int write_struct_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_struct *fc, - const bt_field *field, bool align_struct) -{ - int ret = 0; - uint64_t i; - - if (likely(align_struct)) { - ret = bt_ctfser_align_offset_in_current_packet(&stream->ctfser, - fc->base.alignment); - if (unlikely(ret)) { - goto end; - } - } - - for (i = 0; i < fc->members->len; i++) { - const bt_field *memb_field = - bt_field_structure_borrow_member_field_by_index_const( - field, i); - struct fs_sink_ctf_field_class *member_fc = - fs_sink_ctf_field_class_struct_borrow_member_by_index( - fc, i)->fc; - - ret = write_field(stream, member_fc, memb_field); - if (unlikely(ret)) { - goto end; - } - } - -end: - return ret; -} - -static inline -int write_variant_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class_variant *fc, - const bt_field *field) -{ - uint64_t opt_index = - bt_field_variant_get_selected_option_field_index(field); - int ret; - - if (fc->tag_is_before) { - ret = bt_ctfser_write_unsigned_int(&stream->ctfser, - opt_index, 8, 16, BYTE_ORDER); - if (unlikely(ret)) { - goto end; - } - } - - ret = write_field(stream, - fs_sink_ctf_field_class_variant_borrow_option_by_index(fc, - opt_index)->fc, - bt_field_variant_borrow_selected_option_field_const(field)); - -end: - return ret; -} - -static -int write_field(struct fs_sink_stream *stream, - struct fs_sink_ctf_field_class *fc, const bt_field *field) -{ - int ret; - - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: - ret = write_int_field(stream, (void *) fc, field); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: - ret = write_float_field(stream, (void *) fc, field); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: - ret = write_string_field(stream, (void *) fc, field); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - ret = write_struct_field(stream, (void *) fc, field, true); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - ret = write_array_field_elements(stream, (void *) fc, field); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - ret = write_sequence_field(stream, (void *) fc, field); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - ret = write_variant_field(stream, (void *) fc, field); - break; - default: - abort(); - } - - return ret; -} - -static inline -int write_event_header(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs, struct fs_sink_ctf_event_class *ec) -{ - int ret; - - /* Event class ID */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - bt_event_class_get_id(ec->ir_ec), 8, 64, BYTE_ORDER); - if (unlikely(ret)) { - goto end; - } - - /* Time */ - if (stream->sc->default_clock_class) { - BT_ASSERT(cs); - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - bt_clock_snapshot_get_value(cs), 8, 64, BYTE_ORDER); - if (unlikely(ret)) { - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -int fs_sink_stream_write_event(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs, const bt_event *event, - struct fs_sink_ctf_event_class *ec) -{ - int ret; - const bt_field *field; - - /* Header */ - ret = write_event_header(stream, cs, ec); - if (unlikely(ret)) { - goto end; - } - - /* Common context */ - if (stream->sc->event_common_context_fc) { - field = bt_event_borrow_common_context_field_const(event); - BT_ASSERT(field); - ret = write_struct_field(stream, - (void *) stream->sc->event_common_context_fc, - field, true); - if (unlikely(ret)) { - goto end; - } - } - - /* Specific context */ - if (ec->spec_context_fc) { - field = bt_event_borrow_specific_context_field_const(event); - BT_ASSERT(field); - ret = write_struct_field(stream, (void *) ec->spec_context_fc, - field, true); - if (unlikely(ret)) { - goto end; - } - } - - /* Specific context */ - if (ec->payload_fc) { - field = bt_event_borrow_payload_field_const(event); - BT_ASSERT(field); - ret = write_struct_field(stream, (void *) ec->payload_fc, - field, true); - if (unlikely(ret)) { - goto end; - } - } - -end: - return ret; -} - -static -int write_packet_context(struct fs_sink_stream *stream) -{ - int ret; - - /* Packet total size */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.total_size, 8, 64, BYTE_ORDER); - if (ret) { - goto end; - } - - /* Packet content size */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.content_size, 8, 64, BYTE_ORDER); - if (ret) { - goto end; - } - - if (stream->sc->packets_have_ts_begin) { - /* Beginning time */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.beginning_cs, 8, 64, BYTE_ORDER); - if (ret) { - goto end; - } - } - - if (stream->sc->packets_have_ts_end) { - /* End time */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.end_cs, 8, 64, BYTE_ORDER); - if (ret) { - goto end; - } - } - - if (stream->sc->has_discarded_events) { - /* Discarded event counter */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.discarded_events_counter, 8, 64, - BYTE_ORDER); - if (ret) { - goto end; - } - } - - /* Sequence number */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - stream->packet_state.seq_num, 8, 64, BYTE_ORDER); - if (ret) { - goto end; - } - - /* Other members */ - if (stream->sc->packet_context_fc) { - const bt_field *packet_context_field = - bt_packet_borrow_context_field_const( - stream->packet_state.packet); - - BT_ASSERT(packet_context_field); - ret = write_struct_field(stream, - (void *) stream->sc->packet_context_fc, - packet_context_field, false); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -int fs_sink_stream_open_packet(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs, const bt_packet *packet) -{ - int ret; - uint64_t i; - - BT_ASSERT(!stream->packet_state.is_open); - bt_packet_put_ref(stream->packet_state.packet); - stream->packet_state.packet = packet; - bt_packet_get_ref(stream->packet_state.packet); - if (cs) { - stream->packet_state.beginning_cs = - bt_clock_snapshot_get_value(cs); - } - - /* Open packet */ - ret = bt_ctfser_open_packet(&stream->ctfser); - if (ret) { - /* bt_ctfser_open_packet() logs errors */ - goto end; - } - - /* Packet header: magic */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - UINT64_C(0xc1fc1fc1), 8, 32, BYTE_ORDER); - if (ret) { - BT_LOGE("Error writing packet header magic: stream-file-name=%s", - stream->file_name->str); - goto end; - } - - /* Packet header: UUID */ - for (i = 0; i < BABELTRACE_UUID_LEN; i++) { - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - (uint64_t) stream->sc->tc->uuid[i], 8, 8, BYTE_ORDER); - if (ret) { - BT_LOGE("Error writing packet header UUID: stream-file-name=%s", - stream->file_name->str); - goto end; - } - } - - /* Packet header: stream class ID */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER); - if (ret) { - BT_LOGE("Error writing packet header stream class id: " - "stream-file-name=%s, stream-class-id=%"PRIu64, - stream->file_name->str, - bt_stream_class_get_id(stream->sc->ir_sc)); - goto end; - } - - /* Packet header: stream ID */ - ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, - bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER); - if (ret) { - BT_LOGE("Error writing packet header stream id: " - "stream-file-name=%s, stream-id=%"PRIu64, - stream->file_name->str, - bt_stream_get_id(stream->ir_stream)); - goto end; - } - - /* Save packet context's offset to rewrite it later */ - stream->packet_state.context_offset_bits = - bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser); - - /* Write packet context just to advance to content (first event) */ - ret = write_packet_context(stream); - if (ret) { - goto end; - } - - stream->packet_state.is_open = true; - -end: - return ret; -} - -BT_HIDDEN -int fs_sink_stream_close_packet(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs) -{ - int ret; - - BT_ASSERT(stream->packet_state.is_open); - - if (cs) { - stream->packet_state.end_cs = bt_clock_snapshot_get_value(cs); - } - - stream->packet_state.content_size = - bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser); - stream->packet_state.total_size = - (stream->packet_state.content_size + 7) & ~UINT64_C(7); - - /* Rewrite packet context */ - bt_ctfser_set_offset_in_current_packet_bits(&stream->ctfser, - stream->packet_state.context_offset_bits); - ret = write_packet_context(stream); - if (ret) { - goto end; - } - - /* Close packet */ - bt_ctfser_close_current_packet(&stream->ctfser, - stream->packet_state.total_size / 8); - - /* Partially copy current packet state to previous packet state */ - stream->prev_packet_state.end_cs = stream->packet_state.end_cs; - stream->prev_packet_state.discarded_events_counter = - stream->packet_state.discarded_events_counter; - stream->prev_packet_state.seq_num = - stream->packet_state.seq_num; - - /* Reset current packet state */ - stream->packet_state.beginning_cs = UINT64_C(-1); - stream->packet_state.end_cs = UINT64_C(-1); - stream->packet_state.content_size = 0; - stream->packet_state.total_size = 0; - stream->packet_state.seq_num += 1; - stream->packet_state.context_offset_bits = 0; - stream->packet_state.is_open = false; - BT_PACKET_PUT_REF_AND_RESET(stream->packet_state.packet); - -end: - return ret; -} diff --git a/plugins/ctf/fs-sink/fs-sink-stream.h b/plugins/ctf/fs-sink/fs-sink-stream.h deleted file mode 100644 index 959d01a5..00000000 --- a/plugins/ctf/fs-sink/fs-sink-stream.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "fs-sink-ctf-meta.h" - -struct fs_sink_trace; - -struct fs_sink_stream { - struct fs_sink_trace *trace; - struct bt_ctfser ctfser; - - /* Stream's file name */ - GString *file_name; - - /* Weak */ - const bt_stream *ir_stream; - - struct fs_sink_ctf_stream_class *sc; - - /* Current packet's state */ - struct { - /* - * True if we're, for this stream, within an opened - * packet (got a packet beginning message, but no - * packet end message yet). - */ - bool is_open; - - /* - * Current beginning default clock snapshot for the - * current packet (`UINT64_C(-1)` if not set). - */ - uint64_t beginning_cs; - - /* - * Current end default clock snapshot for the current - * packet (`UINT64_C(-1)` if not set). - */ - uint64_t end_cs; - - /* - * Current packet's content size (bits) for the current - * packet. - */ - uint64_t content_size; - - /* - * Current packet's total size (bits) for the current - * packet. - */ - uint64_t total_size; - - /* - * Discarded events (free running) counter for the - * current packet. - */ - uint64_t discarded_events_counter; - - /* Sequence number (free running) of the current packet */ - uint64_t seq_num; - - /* - * Offset of the packet context structure within the - * current packet (bits). - */ - uint64_t context_offset_bits; - - /* Owned by this */ - const bt_packet *packet; - } packet_state; - - /* Previous packet's state */ - struct { - /* End default clock snapshot (`UINT64_C(-1)` if not set) */ - uint64_t end_cs; - - /* Discarded events (free running) counter */ - uint64_t discarded_events_counter; - - /* Sequence number (free running) */ - uint64_t seq_num; - } prev_packet_state; - - /* State to handle discarded events */ - struct { - /* - * True if we're in the time range given by a previously - * received discarded events message. In this case, - * `beginning_cs` and `end_cs` below contain the - * beginning and end clock snapshots for this range. - * - * This is used to validate that, when receiving a - * packet end message, the current discarded events time - * range matches what's expected for CTF 1.8, that is: - * - * * Its beginning time is the previous packet's end - * time (or the current packet's beginning time if - * this is the first packet). - * - * * Its end time is the current packet's end time. - */ - bool in_range; - - /* - * Beginning and end times of the time range given by a - * previously received discarded events message. - */ - uint64_t beginning_cs; - uint64_t end_cs; - } discarded_events_state; - - /* State to handle discarded packets */ - struct { - /* - * True if we're in the time range given by a previously - * received discarded packets message. In this case, - * `beginning_cs` and `end_cs` below contain the - * beginning and end clock snapshots for this range. - * - * This is used to validate that, when receiving a - * packet beginning message, the current discarded - * packets time range matches what's expected for CTF - * 1.8, that is: - * - * * Its beginning time is the previous packet's end - * time. - * - * * Its end time is the current packet's beginning - * time. - */ - bool in_range; - - /* - * Beginning and end times of the time range given by a - * previously received discarded packets message. - */ - uint64_t beginning_cs; - uint64_t end_cs; - } discarded_packets_state; -}; - -BT_HIDDEN -struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace, - const bt_stream *ir_stream); - -BT_HIDDEN -void fs_sink_stream_destroy(struct fs_sink_stream *stream); - -BT_HIDDEN -int fs_sink_stream_write_event(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs, const bt_event *event, - struct fs_sink_ctf_event_class *ec); - -BT_HIDDEN -int fs_sink_stream_open_packet(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs, const bt_packet *packet); - -BT_HIDDEN -int fs_sink_stream_close_packet(struct fs_sink_stream *stream, - const bt_clock_snapshot *cs); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H */ diff --git a/plugins/ctf/fs-sink/fs-sink-trace.c b/plugins/ctf/fs-sink/fs-sink-trace.c deleted file mode 100644 index 400b21d7..00000000 --- a/plugins/ctf/fs-sink/fs-sink-trace.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE" -#include "logging.h" - -#include -#include -#include -#include -#include -#include - -#include "translate-trace-ir-to-ctf-ir.h" -#include "translate-ctf-ir-to-tsdl.h" -#include "fs-sink.h" -#include "fs-sink-trace.h" -#include "fs-sink-stream.h" - -/* - * Sanitizes `path` so as to: - * - * * Replace `.` subdirectories with `_`. - * * Replace `..` subdirectories with `__`. - * * Remove trailing slashes. - */ -static -GString *sanitize_trace_path(const char *path) -{ - GString *san_path = g_string_new(NULL); - const char *ch = path; - bool dir_start = true; - - BT_ASSERT(san_path); - BT_ASSERT(path); - - while (*ch != '\0') { - switch (*ch) { - case '/': - /* Start of directory */ - dir_start = true; - g_string_append_c(san_path, *ch); - ch++; - continue; - case '.': - if (dir_start) { - switch (ch[1]) { - case '\0': - case '/': - /* `.` -> `_` */ - g_string_append_c(san_path, '_'); - ch++; - continue; - case '.': - switch (ch[2]) { - case '\0': - case '/': - /* `..` -> `__` */ - g_string_append(san_path, "__"); - ch += 2; - continue; - default: - break; - } - default: - break; - } - } - default: - break; - } - - /* Not a special character */ - g_string_append_c(san_path, *ch); - ch++; - dir_start = false; - } - - /* Remove trailing slashes */ - while (san_path->len > 0 && - san_path->str[san_path->len - 1] == '/') { - /* Remove trailing slash */ - g_string_set_size(san_path, san_path->len - 1); - } - - if (san_path->len == 0) { - /* Looks like there's nothing left: just use `trace` */ - g_string_assign(san_path, "trace"); - } - - return san_path; -} - -/* - * Find a path based on `path` that doesn't exist yet. First, try `path` - * itself, then try with incrementing suffixes. - */ - -static -GString *make_unique_trace_path(const char *path) -{ - GString *unique_path; - unsigned int suffix = 0; - - unique_path = g_string_new(path); - - while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) { - g_string_printf(unique_path, "%s-%u", path, suffix); - suffix++; - } - - return unique_path; -} - -/* - * Validate that the input string `datetime` is an ISO8601-compliant string (the - * format used by LTTng in the metadata). - */ - -static -int lttng_validate_datetime(const char *datetime) -{ - GTimeVal tv; - int ret = -1; - - /* - * We are using g_time_val_from_iso8601, as the safer/more modern - * alternative, g_date_time_new_from_iso8601, is only available in - * glib >= 2.56, and this is sufficient for our use case of validating - * the format. - */ - if (!g_time_val_from_iso8601(datetime, &tv)) { - BT_LOGD("Couldn't parse datetime as iso8601: date=\"%s\"", datetime); - goto end; - } - - ret = 0; - -end: - return ret; -} - -static -int append_lttng_trace_path_ust_uid(GString *path, const bt_trace_class *tc) -{ - const bt_value *v; - int ret; - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id"); - if (!v || !bt_value_is_signed_integer(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_id\""); - goto error; - } - - g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, - bt_value_signed_integer_get(v)); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "isa_length"); - if (!v || !bt_value_is_signed_integer(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"isa_length\""); - goto error; - } - - g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit", - bt_value_signed_integer_get(v)); - - ret = 0; - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int append_lttng_trace_path_ust_pid(GString *path, const bt_trace_class *tc) -{ - const bt_value *v; - const char *datetime; - int ret; - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "procname"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"procname\""); - goto error; - } - - g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v)); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid"); - if (!v || !bt_value_is_signed_integer(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"vpid\""); - goto error; - } - - g_string_append_printf(path, "-%" PRId64, bt_value_signed_integer_get(v)); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"vpid_datetime\""); - goto error; - } - - datetime = bt_value_string_get(v); - - if (lttng_validate_datetime(datetime)) { - goto error; - } - - g_string_append_printf(path, "-%s", datetime); - - ret = 0; - goto end; - -error: - ret = -1; - -end: - return ret; -} - -/* - * Try to build a trace path based on environment values put in the trace - * environment by the LTTng tracer, starting with version 2.11. - */ -static -GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace) -{ - const bt_trace_class *tc; - const bt_value *v; - const char *tracer_name, *domain, *datetime; - int64_t tracer_major, tracer_minor; - GString *path; - - path = g_string_new(NULL); - if (!path) { - goto error; - } - - tc = bt_trace_borrow_class_const(trace->ir_trace); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_name"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_name\""); - goto error; - } - - tracer_name = bt_value_string_get(v); - - if (!g_str_equal(tracer_name, "lttng-ust") - && !g_str_equal(tracer_name, "lttng-modules")) { - BT_LOGD("Unrecognized tracer name: name=\"%s\"", tracer_name); - goto error; - } - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_major"); - if (!v || !bt_value_is_signed_integer(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_major\""); - goto error; - } - - tracer_major = bt_value_signed_integer_get(v); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_minor"); - if (!v || !bt_value_is_signed_integer(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_minor\""); - goto error; - } - - tracer_minor = bt_value_signed_integer_get(v); - - if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) { - BT_LOGD("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64, - tracer_major, tracer_minor); - goto error; - } - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "hostname"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_hostname\""); - goto error; - } - - g_string_assign(path, bt_value_string_get(v)); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_name"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"trace_name\""); - goto error; - } - - g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v)); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_creation_datetime"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"trace_creation_datetime\""); - goto error; - } - - datetime = bt_value_string_get(v); - - if (lttng_validate_datetime(datetime)) { - goto error; - } - - g_string_append_printf(path, "-%s", datetime); - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "domain"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"domain\""); - goto error; - } - - domain = bt_value_string_get(v); - g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain); - - if (g_str_equal(domain, "ust")) { - const char *tracer_buffering_scheme; - - v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_scheme"); - if (!v || !bt_value_is_string(v)) { - BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\""); - goto error; - } - - tracer_buffering_scheme = bt_value_string_get(v); - g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme); - - if (g_str_equal(tracer_buffering_scheme, "uid")) { - if (append_lttng_trace_path_ust_uid(path, tc)) { - goto error; - } - } else if (g_str_equal(tracer_buffering_scheme, "pid")){ - if (append_lttng_trace_path_ust_pid(path, tc)) { - goto error; - } - } else { - /* Unknown buffering scheme. */ - BT_LOGD("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme); - goto error; - } - } else if (!g_str_equal(domain, "kernel")) { - /* Unknown domain. */ - BT_LOGD("Unknown domain: domain=\"%s\"", domain); - goto error; - } - - goto end; - -error: - if (path) { - g_string_free(path, TRUE); - path = NULL; - } - -end: - return path; -} - -/* Build the relative output path for `trace`. */ - -static -GString *make_trace_path_rel(const struct fs_sink_trace *trace) -{ - GString *path = NULL; - - if (trace->fs_sink->assume_single_trace) { - /* Use output directory directly */ - path = g_string_new(""); - goto end; - } - - /* First, try to build a path using environment fields written by LTTng. */ - path = make_lttng_trace_path_rel(trace); - if (path) { - goto end; - } - - /* Otherwise, use the trace name, if available. */ - const char *trace_name = bt_trace_get_name(trace->ir_trace); - if (trace_name) { - path = g_string_new(trace_name); - goto end; - } - - /* Otherwise, use "trace". */ - path = g_string_new("trace"); - -end: - return path; -} - -/* - * Compute the trace output path for `trace`, rooted at `output_base_directory`. - */ - -static -GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory) -{ - GString *rel_path = NULL; - GString *rel_path_san = NULL; - GString *full_path = NULL; - GString *unique_full_path = NULL; - - rel_path = make_trace_path_rel(trace); - if (!rel_path) { - goto end; - } - - rel_path_san = sanitize_trace_path(rel_path->str); - if (!rel_path_san) { - goto end; - } - - full_path = g_string_new(NULL); - if (!full_path) { - goto end; - } - - g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s", - output_base_directory, rel_path_san->str); - - unique_full_path = make_unique_trace_path(full_path->str); - -end: - if (rel_path) { - g_string_free(rel_path, TRUE); - } - - if (rel_path_san) { - g_string_free(rel_path_san, TRUE); - } - - if (full_path) { - g_string_free(full_path, TRUE); - } - - return unique_full_path; -} - -BT_HIDDEN -void fs_sink_trace_destroy(struct fs_sink_trace *trace) -{ - GString *tsdl = NULL; - FILE *fh = NULL; - size_t len; - - if (!trace) { - goto end; - } - - if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) { - /* - * Remove the destruction listener, otherwise it could - * be called in the future, and its private data is this - * CTF FS sink trace object which won't exist anymore. - */ - (void) bt_trace_remove_destruction_listener(trace->ir_trace, - trace->ir_trace_destruction_listener_id); - trace->ir_trace_destruction_listener_id = UINT64_C(-1); - } - - if (trace->streams) { - g_hash_table_destroy(trace->streams); - trace->streams = NULL; - } - - tsdl = g_string_new(NULL); - BT_ASSERT(tsdl); - translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl); - - BT_ASSERT(trace->metadata_path); - fh = fopen(trace->metadata_path->str, "wb"); - if (!fh) { - BT_LOGF_ERRNO("In trace destruction listener: " - "cannot open metadata file for writing: ", - ": path=\"%s\"", trace->metadata_path->str); - abort(); - } - - len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh); - if (len != tsdl->len) { - BT_LOGF_ERRNO("In trace destruction listener: " - "cannot write metadata file: ", - ": path=\"%s\"", trace->metadata_path->str); - abort(); - } - - if (!trace->fs_sink->quiet) { - printf("Created CTF trace `%s`.\n", trace->path->str); - } - - if (trace->path) { - g_string_free(trace->path, TRUE); - trace->path = NULL; - } - - g_string_free(trace->metadata_path, TRUE); - trace->metadata_path = NULL; - - fs_sink_ctf_trace_class_destroy(trace->tc); - trace->tc = NULL; - g_free(trace); - -end: - if (fh) { - int ret = fclose(fh); - - if (ret != 0) { - BT_LOGW_ERRNO("In trace destruction listener: " - "cannot close metadata file: ", - ": path=\"%s\"", trace->metadata_path->str); - } - } - - if (tsdl) { - g_string_free(tsdl, TRUE); - } - - return; -} - -static -void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data) -{ - struct fs_sink_trace *trace = data; - - /* - * Prevent bt_trace_remove_destruction_listener() from being - * called in fs_sink_trace_destroy(), which is called by - * g_hash_table_remove() below. - */ - trace->ir_trace_destruction_listener_id = UINT64_C(-1); - g_hash_table_remove(trace->fs_sink->traces, ir_trace); -} - -BT_HIDDEN -struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, - const bt_trace *ir_trace) -{ - int ret; - struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1); - bt_trace_status trace_status; - - if (!trace) { - goto end; - } - - trace->fs_sink = fs_sink; - trace->ir_trace = ir_trace; - trace->ir_trace_destruction_listener_id = UINT64_C(-1); - trace->tc = translate_trace_class_trace_ir_to_ctf_ir( - bt_trace_borrow_class_const(ir_trace)); - if (!trace->tc) { - goto error; - } - - trace->path = make_trace_path(trace, fs_sink->output_dir_path->str); - BT_ASSERT(trace->path); - ret = g_mkdir_with_parents(trace->path->str, 0755); - if (ret) { - BT_LOGE_ERRNO("Cannot create directories for trace directory", - ": path=\"%s\"", trace->path->str); - goto error; - } - - trace->metadata_path = g_string_new(trace->path->str); - BT_ASSERT(trace->metadata_path); - g_string_append(trace->metadata_path, "/metadata"); - trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) fs_sink_stream_destroy); - BT_ASSERT(trace->streams); - trace_status = bt_trace_add_destruction_listener(ir_trace, - ir_trace_destruction_listener, trace, - &trace->ir_trace_destruction_listener_id); - if (trace_status) { - goto error; - } - - g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace); - goto end; - -error: - fs_sink_trace_destroy(trace); - trace = NULL; - -end: - return trace; -} diff --git a/plugins/ctf/fs-sink/fs-sink-trace.h b/plugins/ctf/fs-sink/fs-sink-trace.h deleted file mode 100644 index e135ffa6..00000000 --- a/plugins/ctf/fs-sink/fs-sink-trace.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#include "fs-sink-ctf-meta.h" - -struct fs_sink_comp; - -struct fs_sink_trace { - struct fs_sink_comp *fs_sink; - - /* Owned by this */ - struct fs_sink_ctf_trace_class *tc; - - /* - * Weak reference: this object does not own it, and `tc` above - * does not own its trace IR trace class either. Instead, we add - * a "trace destruction" listener (in create_trace()) so that - * this object gets destroyed when the trace object is - * destroyed. - * - * Otherwise (with a strong reference), we would keep this trace - * object alive until the upstream message iterator ends. This - * could "leak" resources (memory, file descriptors) associated - * to traces and streams which otherwise would not exist. - */ - const bt_trace *ir_trace; - - uint64_t ir_trace_destruction_listener_id; - - /* Trace's directory */ - GString *path; - - /* `metadata` file path */ - GString *metadata_path; - - /* - * Hash table of `const bt_stream *` (weak) to - * `struct fs_sink_stream *` (owned by hash table). - */ - GHashTable *streams; -}; - -BT_HIDDEN -struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, - const bt_trace *ir_trace); - -BT_HIDDEN -void fs_sink_trace_destroy(struct fs_sink_trace *trace); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H */ diff --git a/plugins/ctf/fs-sink/fs-sink.c b/plugins/ctf/fs-sink/fs-sink.c deleted file mode 100644 index 021066f0..00000000 --- a/plugins/ctf/fs-sink/fs-sink.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK" -#include "logging.h" - -#include -#include -#include -#include -#include -#include - -#include "fs-sink.h" -#include "fs-sink-trace.h" -#include "fs-sink-stream.h" -#include "fs-sink-ctf-meta.h" -#include "translate-trace-ir-to-ctf-ir.h" -#include "translate-ctf-ir-to-tsdl.h" - -static -const char * const in_port_name = "in"; - -static -bt_self_component_status ensure_output_dir_exists( - struct fs_sink_comp *fs_sink) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - int ret; - - ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755); - if (ret) { - BT_LOGE_ERRNO("Cannot create directories for output directory", - ": output-dir-path=\"%s\"", - fs_sink->output_dir_path->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static -bt_self_component_status configure_component(struct fs_sink_comp *fs_sink, - const bt_value *params) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_value *value; - - value = bt_value_map_borrow_entry_value_const(params, "path"); - if (!value) { - BT_LOGE_STR("Missing mandatory `path` parameter."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (!bt_value_is_string(value)) { - BT_LOGE_STR("`path` parameter: expecting a string."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - g_string_assign(fs_sink->output_dir_path, - bt_value_string_get(value)); - value = bt_value_map_borrow_entry_value_const(params, - "assume-single-trace"); - if (value) { - if (!bt_value_is_bool(value)) { - BT_LOGE_STR("`assume-single-trace` parameter: expecting a boolean."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - fs_sink->assume_single_trace = (bool) bt_value_bool_get(value); - } - - value = bt_value_map_borrow_entry_value_const(params, - "ignore-discarded-events"); - if (value) { - if (!bt_value_is_bool(value)) { - BT_LOGE_STR("`ignore-discarded-events` parameter: expecting a boolean."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - fs_sink->ignore_discarded_events = - (bool) bt_value_bool_get(value); - } - - value = bt_value_map_borrow_entry_value_const(params, - "ignore-discarded-packets"); - if (value) { - if (!bt_value_is_bool(value)) { - BT_LOGE_STR("`ignore-discarded-packets` parameter: expecting a boolean."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - fs_sink->ignore_discarded_packets = - (bool) bt_value_bool_get(value); - } - - value = bt_value_map_borrow_entry_value_const(params, - "quiet"); - if (value) { - if (!bt_value_is_bool(value)) { - BT_LOGE_STR("`quiet` parameter: expecting a boolean."); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - fs_sink->quiet = (bool) bt_value_bool_get(value); - } - -end: - return status; -} - -static -void destroy_fs_sink_comp(struct fs_sink_comp *fs_sink) -{ - if (!fs_sink) { - goto end; - } - - if (fs_sink->output_dir_path) { - g_string_free(fs_sink->output_dir_path, TRUE); - fs_sink->output_dir_path = NULL; - } - - if (fs_sink->traces) { - g_hash_table_destroy(fs_sink->traces); - fs_sink->traces = NULL; - } - - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_PUT_REF_AND_RESET( - fs_sink->upstream_iter); - g_free(fs_sink); - -end: - return; -} - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_init( - bt_self_component_sink *self_comp, const bt_value *params, - void *init_method_data) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct fs_sink_comp *fs_sink = NULL; - - fs_sink = g_new0(struct fs_sink_comp, 1); - if (!fs_sink) { - BT_LOGE_STR("Failed to allocate one CTF FS sink structure."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - fs_sink->output_dir_path = g_string_new(NULL); - fs_sink->self_comp = self_comp; - status = configure_component(fs_sink, params); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - /* configure_component() logs errors */ - goto end; - } - - if (fs_sink->assume_single_trace && - g_file_test(fs_sink->output_dir_path->str, - G_FILE_TEST_EXISTS)) { - BT_LOGE("Single trace mode, but output path exists: " - "output-path=\"%s\"", fs_sink->output_dir_path->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - status = ensure_output_dir_exists(fs_sink); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - /* ensure_output_dir_exists() logs errors */ - goto end; - } - - fs_sink->traces = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) fs_sink_trace_destroy); - if (!fs_sink->traces) { - BT_LOGE_STR("Failed to allocate one GHashTable."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - status = bt_self_component_sink_add_input_port(self_comp, in_port_name, - NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - bt_self_component_set_data( - bt_self_component_sink_as_self_component(self_comp), fs_sink); - -end: - if (status != BT_SELF_COMPONENT_STATUS_OK) { - destroy_fs_sink_comp(fs_sink); - } - - return status; -} - -static inline -struct fs_sink_stream *borrow_stream(struct fs_sink_comp *fs_sink, - const bt_stream *ir_stream) -{ - const bt_trace *ir_trace = bt_stream_borrow_trace_const(ir_stream); - struct fs_sink_trace *trace; - struct fs_sink_stream *stream = NULL; - - trace = g_hash_table_lookup(fs_sink->traces, ir_trace); - if (unlikely(!trace)) { - if (fs_sink->assume_single_trace && - g_hash_table_size(fs_sink->traces) > 0) { - BT_LOGE("Single trace mode, but getting more than one trace: " - "stream-name=\"%s\"", - bt_stream_get_name(ir_stream)); - goto end; - } - - trace = fs_sink_trace_create(fs_sink, ir_trace); - if (!trace) { - goto end; - } - } - - stream = g_hash_table_lookup(trace->streams, ir_stream); - if (unlikely(!stream)) { - stream = fs_sink_stream_create(trace, ir_stream); - if (!stream) { - goto end; - } - } - -end: - return stream; -} - -static inline -bt_self_component_status handle_event_msg(struct fs_sink_comp *fs_sink, - const bt_message *msg) -{ - int ret; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_event *ir_event = bt_message_event_borrow_event_const(msg); - const bt_stream *ir_stream = bt_event_borrow_stream_const(ir_event); - struct fs_sink_stream *stream; - struct fs_sink_ctf_event_class *ec = NULL; - const bt_clock_snapshot *cs = NULL; - - stream = borrow_stream(fs_sink, ir_stream); - if (unlikely(!stream)) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - ret = try_translate_event_class_trace_ir_to_ctf_ir(stream->sc, - bt_event_borrow_class_const(ir_event), &ec); - if (ret) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - BT_ASSERT(ec); - - if (stream->sc->default_clock_class) { - cs = bt_message_event_borrow_default_clock_snapshot_const( - msg); - } - - ret = fs_sink_stream_write_event(stream, cs, ir_event, ec); - if (unlikely(ret)) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static inline -bt_self_component_status handle_packet_beginning_msg( - struct fs_sink_comp *fs_sink, const bt_message *msg) -{ - int ret; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_packet *ir_packet = - bt_message_packet_beginning_borrow_packet_const(msg); - const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet); - struct fs_sink_stream *stream; - const bt_clock_snapshot *cs = NULL; - - stream = borrow_stream(fs_sink, ir_stream); - if (unlikely(!stream)) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (stream->sc->packets_have_ts_begin) { - cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - } - - /* - * If we previously received a discarded events message with - * a time range, make sure that its beginning time matches what's - * expected for CTF 1.8, that is: - * - * * Its beginning time is the previous packet's end - * time (or the current packet's beginning time if - * this is the first packet). - * - * We check this here instead of in handle_packet_end_msg() - * because we want to catch any incompatible message as early as - * possible to report the error. - * - * Validation of the discarded events message's end time is - * performed in handle_packet_end_msg(). - */ - if (stream->discarded_events_state.in_range) { - uint64_t expected_cs; - - /* - * `stream->discarded_events_state.in_range` is only set - * when the stream class's discarded events have a time - * range. - * - * It is required that the packet beginning and end - * messages for this stream class have times when - * discarded events have a time range. - */ - BT_ASSERT(stream->sc->discarded_events_has_ts); - BT_ASSERT(stream->sc->packets_have_ts_begin); - BT_ASSERT(stream->sc->packets_have_ts_end); - - if (stream->prev_packet_state.end_cs == UINT64_C(-1)) { - /* We're opening the first packet */ - expected_cs = bt_clock_snapshot_get_value(cs); - } else { - expected_cs = stream->prev_packet_state.end_cs; - } - - if (stream->discarded_events_state.beginning_cs != - expected_cs) { - BT_LOGE("Incompatible discarded events message: " - "unexpected beginning time: " - "beginning-cs-val=%" PRIu64 ", " - "expected-beginning-cs-val=%" PRIu64 ", " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - stream->discarded_events_state.beginning_cs, - expected_cs, - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - } - - /* - * If we previously received a discarded packets message with a - * time range, make sure that its beginning and end times match - * what's expected for CTF 1.8, that is: - * - * * Its beginning time is the previous packet's end time. - * - * * Its end time is the current packet's beginning time. - */ - if (stream->discarded_packets_state.in_range) { - uint64_t expected_end_cs; - - /* - * `stream->discarded_packets_state.in_range` is only - * set when the stream class's discarded packets have a - * time range. - * - * It is required that the packet beginning and end - * messages for this stream class have times when - * discarded packets have a time range. - */ - BT_ASSERT(stream->sc->discarded_packets_has_ts); - BT_ASSERT(stream->sc->packets_have_ts_begin); - BT_ASSERT(stream->sc->packets_have_ts_end); - - /* - * It is not supported to have a discarded packets - * message _before_ the first packet: we cannot validate - * that its beginning time is compatible with CTF 1.8 in - * this case. - */ - if (stream->prev_packet_state.end_cs == UINT64_C(-1)) { - BT_LOGE("Incompatible discarded packets message " - "occuring before the stream's first packet: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (stream->discarded_packets_state.beginning_cs != - stream->prev_packet_state.end_cs) { - BT_LOGE("Incompatible discarded packets message: " - "unexpected beginning time: " - "beginning-cs-val=%" PRIu64 ", " - "expected-beginning-cs-val=%" PRIu64 ", " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - stream->discarded_packets_state.beginning_cs, - stream->prev_packet_state.end_cs, - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - expected_end_cs = bt_clock_snapshot_get_value(cs); - - if (stream->discarded_packets_state.end_cs != - expected_end_cs) { - BT_LOGE("Incompatible discarded packets message: " - "unexpected end time: " - "end-cs-val=%" PRIu64 ", " - "expected-end-cs-val=%" PRIu64 ", " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - stream->discarded_packets_state.end_cs, - expected_end_cs, - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - } - - /* - * We're not in a discarded packets time range anymore since we - * require that the discarded packets time ranges go from one - * packet's end time to the next packet's beginning time, and - * we're handling a packet beginning message here. - */ - stream->discarded_packets_state.in_range = false; - - ret = fs_sink_stream_open_packet(stream, cs, ir_packet); - if (ret) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static inline -bt_self_component_status handle_packet_end_msg( - struct fs_sink_comp *fs_sink, const bt_message *msg) -{ - int ret; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_packet *ir_packet = - bt_message_packet_end_borrow_packet_const(msg); - const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet); - struct fs_sink_stream *stream; - const bt_clock_snapshot *cs = NULL; - - stream = borrow_stream(fs_sink, ir_stream); - if (unlikely(!stream)) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (stream->sc->packets_have_ts_end) { - cs = bt_message_packet_end_borrow_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - } - - /* - * If we previously received a discarded events message with - * a time range, make sure that its end time matches what's - * expected for CTF 1.8, that is: - * - * * Its end time is the current packet's end time. - * - * Validation of the discarded events message's beginning time - * is performed in handle_packet_beginning_msg(). - */ - if (stream->discarded_events_state.in_range) { - uint64_t expected_cs; - - /* - * `stream->discarded_events_state.in_range` is only set - * when the stream class's discarded events have a time - * range. - * - * It is required that the packet beginning and end - * messages for this stream class have times when - * discarded events have a time range. - */ - BT_ASSERT(stream->sc->discarded_events_has_ts); - BT_ASSERT(stream->sc->packets_have_ts_begin); - BT_ASSERT(stream->sc->packets_have_ts_end); - - expected_cs = bt_clock_snapshot_get_value(cs); - - if (stream->discarded_events_state.end_cs != expected_cs) { - BT_LOGE("Incompatible discarded events message: " - "unexpected end time: " - "end-cs-val=%" PRIu64 ", " - "expected-end-cs-val=%" PRIu64 ", " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - stream->discarded_events_state.end_cs, - expected_cs, - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - } - - ret = fs_sink_stream_close_packet(stream, cs); - if (ret) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - /* - * We're not in a discarded events time range anymore since we - * require that the discarded events time ranges go from one - * packet's end time to the next packet's end time, and we're - * handling a packet end message here. - */ - stream->discarded_events_state.in_range = false; - -end: - return status; -} - -static inline -bt_self_component_status handle_stream_beginning_msg( - struct fs_sink_comp *fs_sink, const bt_message *msg) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_stream *ir_stream = - bt_message_stream_beginning_borrow_stream_const(msg); - const bt_stream_class *ir_sc = - bt_stream_borrow_class_const(ir_stream); - struct fs_sink_stream *stream; - bool packets_have_beginning_end_cs = - bt_stream_class_packets_have_beginning_default_clock_snapshot(ir_sc) && - bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc); - - /* - * Not supported: discarded events with default clock snapshots, - * but packet beginning/end without default clock snapshot. - */ - if (!fs_sink->ignore_discarded_events && - bt_stream_class_discarded_events_have_default_clock_snapshots(ir_sc) && - !packets_have_beginning_end_cs) { - BT_LOGE("Unsupported stream: discarded events have " - "default clock snapshots, but packets have no " - "beginning and/or end default clock snapshots: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - ir_stream, bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - /* - * Not supported: discarded packets with default clock - * snapshots, but packet beginning/end without default clock - * snapshot. - */ - if (!fs_sink->ignore_discarded_packets && - bt_stream_class_discarded_packets_have_default_clock_snapshots(ir_sc) && - !packets_have_beginning_end_cs) { - BT_LOGE("Unsupported stream: discarded packets have " - "default clock snapshots, but packets have no " - "beginning and/or end default clock snapshots: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - ir_stream, bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - stream = borrow_stream(fs_sink, ir_stream); - if (!stream) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - BT_LOGI("Created new, empty stream file: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream), - bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - -end: - return status; -} - -static inline -bt_self_component_status handle_stream_end_msg(struct fs_sink_comp *fs_sink, - const bt_message *msg) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_stream *ir_stream = - bt_message_stream_end_borrow_stream_const(msg); - struct fs_sink_stream *stream; - - stream = borrow_stream(fs_sink, ir_stream); - if (!stream) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - BT_LOGI("Closing stream file: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream), - bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - - /* - * This destroys the stream object and frees all its resources, - * closing the stream file. - */ - g_hash_table_remove(stream->trace->streams, ir_stream); - -end: - return status; -} - -static inline -bt_self_component_status handle_discarded_events_msg( - struct fs_sink_comp *fs_sink, const bt_message *msg) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_stream *ir_stream = - bt_message_discarded_events_borrow_stream_const(msg); - struct fs_sink_stream *stream; - const bt_clock_snapshot *cs = NULL; - bt_property_availability avail; - uint64_t count; - - stream = borrow_stream(fs_sink, ir_stream); - if (!stream) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (fs_sink->ignore_discarded_events) { - BT_LOGI("Ignoring discarded events message: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - goto end; - } - - if (stream->discarded_events_state.in_range) { - BT_LOGE("Unsupported contiguous discarded events message: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - /* - * If we're currently in an opened packet (got a packet - * beginning message, but no packet end message yet), we do not - * support having a discarded events message with a time range - * because we require that the discarded events message's time - * range go from a packet's end time to the next packet's end - * time. - */ - if (stream->packet_state.is_open && - stream->sc->discarded_events_has_ts) { - BT_LOGE("Unsupported discarded events message with " - "default clock snapshots occuring within a packet: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (stream->sc->discarded_events_has_ts) { - /* - * Make the stream's state be in the time range of a - * discarded events message since we have the message's - * time range (`stream->sc->discarded_events_has_ts`). - */ - stream->discarded_events_state.in_range = true; - - /* - * The clock snapshot values will be validated when - * handling the next packet beginning and end messages - * (next calls to handle_packet_beginning_msg() and - * handle_packet_end_msg()). - */ - cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - stream->discarded_events_state.beginning_cs = - bt_clock_snapshot_get_value(cs); - cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - stream->discarded_events_state.end_cs = bt_clock_snapshot_get_value(cs); - } - - avail = bt_message_discarded_events_get_count(msg, &count); - if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) { - /* - * There's no specific count of discarded events: set it - * to 1 so that we know that we at least discarded - * something. - */ - count = 1; - } - - stream->packet_state.discarded_events_counter += count; - -end: - return status; -} - -static inline -bt_self_component_status handle_discarded_packets_msg( - struct fs_sink_comp *fs_sink, const bt_message *msg) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - const bt_stream *ir_stream = - bt_message_discarded_packets_borrow_stream_const(msg); - struct fs_sink_stream *stream; - const bt_clock_snapshot *cs = NULL; - bt_property_availability avail; - uint64_t count; - - stream = borrow_stream(fs_sink, ir_stream); - if (!stream) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - if (fs_sink->ignore_discarded_packets) { - BT_LOGI("Ignoring discarded packets message: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - goto end; - } - - if (stream->discarded_packets_state.in_range) { - BT_LOGE("Unsupported contiguous discarded packets message: " - "stream-id=%" PRIu64 ", stream-name=\"%s\", " - "trace-name=\"%s\", path=\"%s/%s\"", - bt_stream_get_id(ir_stream), - bt_stream_get_name(ir_stream), - bt_trace_get_name( - bt_stream_borrow_trace_const(ir_stream)), - stream->trace->path->str, stream->file_name->str); - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - /* - * Discarded packets messages are guaranteed to occur between - * packets. - */ - BT_ASSERT(!stream->packet_state.is_open); - - if (stream->sc->discarded_packets_has_ts) { - /* - * Make the stream's state be in the time range of a - * discarded packets message since we have the message's - * time range (`stream->sc->discarded_packets_has_ts`). - */ - stream->discarded_packets_state.in_range = true; - - /* - * The clock snapshot values will be validated when - * handling the next packet beginning message (next call - * to handle_packet_beginning_msg()). - */ - cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - stream->discarded_packets_state.beginning_cs = - bt_clock_snapshot_get_value(cs); - cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - msg); - BT_ASSERT(cs); - stream->discarded_packets_state.end_cs = - bt_clock_snapshot_get_value(cs); - } - - avail = bt_message_discarded_packets_get_count(msg, &count); - if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) { - /* - * There's no specific count of discarded packets: set - * it to 1 so that we know that we at least discarded - * something. - */ - count = 1; - } - - stream->packet_state.seq_num += count; - -end: - return status; -} - -static inline -void put_messages(bt_message_array_const msgs, uint64_t count) -{ - uint64_t i; - - for (i = 0; i < count; i++) { - BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); - } -} - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_consume(bt_self_component_sink *self_comp) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct fs_sink_comp *fs_sink; - bt_message_iterator_status it_status; - uint64_t msg_count = 0; - bt_message_array_const msgs; - - fs_sink = bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - BT_ASSERT(fs_sink); - BT_ASSERT(fs_sink->upstream_iter); - - /* Consume messages */ - it_status = bt_self_component_port_input_message_iterator_next( - fs_sink->upstream_iter, &msgs, &msg_count); - if (it_status < 0) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - switch (it_status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - { - uint64_t i; - - for (i = 0; i < msg_count; i++) { - const bt_message *msg = msgs[i]; - - BT_ASSERT(msg); - - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_EVENT: - status = handle_event_msg(fs_sink, msg); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - status = handle_packet_beginning_msg( - fs_sink, msg); - break; - case BT_MESSAGE_TYPE_PACKET_END: - status = handle_packet_end_msg( - fs_sink, msg); - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - /* Ignore */ - BT_LOGD_STR("Ignoring message iterator inactivity message."); - break; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - status = handle_stream_beginning_msg( - fs_sink, msg); - break; - case BT_MESSAGE_TYPE_STREAM_END: - status = handle_stream_end_msg( - fs_sink, msg); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - /* Not supported by CTF 1.8 */ - BT_LOGD_STR("Ignoring stream activity message."); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - status = handle_discarded_events_msg( - fs_sink, msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - status = handle_discarded_packets_msg( - fs_sink, msg); - break; - default: - abort(); - } - - BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); - - if (status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGE("Failed to handle message: " - "generated CTF traces could be incomplete: " - "output-dir-path=\"%s\"", - fs_sink->output_dir_path->str); - goto error; - } - } - - break; - } - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - status = BT_SELF_COMPONENT_STATUS_AGAIN; - break; - case BT_MESSAGE_ITERATOR_STATUS_END: - /* TODO: Finalize all traces (should already be done?) */ - status = BT_SELF_COMPONENT_STATUS_END; - break; - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - status = BT_SELF_COMPONENT_STATUS_NOMEM; - break; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - status = BT_SELF_COMPONENT_STATUS_NOMEM; - break; - default: - break; - } - - goto end; - -error: - BT_ASSERT(status != BT_SELF_COMPONENT_STATUS_OK); - put_messages(msgs, msg_count); - -end: - return status; -} - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_graph_is_configured( - bt_self_component_sink *self_comp) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct fs_sink_comp *fs_sink = bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - - fs_sink->upstream_iter = - bt_self_component_port_input_message_iterator_create( - bt_self_component_sink_borrow_input_port_by_name( - self_comp, in_port_name)); - if (!fs_sink->upstream_iter) { - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - -end: - return status; -} - -BT_HIDDEN -void ctf_fs_sink_finalize(bt_self_component_sink *self_comp) -{ - struct fs_sink_comp *fs_sink = bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_comp)); - - destroy_fs_sink_comp(fs_sink); -} diff --git a/plugins/ctf/fs-sink/fs-sink.h b/plugins/ctf/fs-sink/fs-sink.h deleted file mode 100644 index 278bdbec..00000000 --- a/plugins/ctf/fs-sink/fs-sink.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct fs_sink_comp { - bt_self_component_sink *self_comp; - - /* Owned by this */ - bt_self_component_port_input_message_iterator *upstream_iter; - - /* Base output directory path */ - GString *output_dir_path; - - /* - * True if the component assumes that it will only write a - * single CTF trace (which can contain one or more data - * streams). This makes the component write the stream files - * directly in the output directory (`output_dir_path` above). - */ - bool assume_single_trace; - - /* True to completely ignore discarded events messages */ - bool ignore_discarded_events; - - /* True to completely ignore discarded packets messages */ - bool ignore_discarded_packets; - - /* - * True to make the component quiet (nothing printed to the - * standard output). - */ - bool quiet; - - /* - * Hash table of `const bt_trace *` (weak) to - * `struct fs_sink_trace *` (owned by hash table). - */ - GHashTable *traces; -}; - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_init( - bt_self_component_sink *component, - const bt_value *params, - void *init_method_data); - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_consume( - bt_self_component_sink *component); - -BT_HIDDEN -bt_self_component_status ctf_fs_sink_graph_is_configured( - bt_self_component_sink *component); - -BT_HIDDEN -void ctf_fs_sink_finalize(bt_self_component_sink *component); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H */ diff --git a/plugins/ctf/fs-sink/logging.c b/plugins/ctf/fs-sink/logging.c deleted file mode 100644 index e53d744b..00000000 --- a/plugins/ctf/fs-sink/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_fs_sink_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_fs_sink_log_level, - "BABELTRACE_SINK_CTF_FS_LOG_LEVEL"); diff --git a/plugins/ctf/fs-sink/logging.h b/plugins/ctf/fs-sink/logging.h deleted file mode 100644 index 3ae63201..00000000 --- a/plugins/ctf/fs-sink/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_FS_SINK_LOGGING_H -#define PLUGINS_FS_SINK_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_fs_sink_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_fs_sink_log_level); - -#endif /* PLUGINS_FS_SINK_LOGGING_H */ diff --git a/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c b/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c deleted file mode 100644 index 57159953..00000000 --- a/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c +++ /dev/null @@ -1,907 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-CTF-IR-TO-TSDL" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fs-sink-ctf-meta.h" - -struct ctx { - unsigned int indent_level; - GString *tsdl; -}; - -static inline -void append_indent(struct ctx *ctx) -{ - unsigned int i; - - for (i = 0; i < ctx->indent_level; i++) { - g_string_append_c(ctx->tsdl, '\t'); - } -} - -static -void append_uuid(struct ctx *ctx, bt_uuid uuid) -{ - g_string_append_printf(ctx->tsdl, - "\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - (unsigned int) uuid[0], - (unsigned int) uuid[1], - (unsigned int) uuid[2], - (unsigned int) uuid[3], - (unsigned int) uuid[4], - (unsigned int) uuid[5], - (unsigned int) uuid[6], - (unsigned int) uuid[7], - (unsigned int) uuid[8], - (unsigned int) uuid[9], - (unsigned int) uuid[10], - (unsigned int) uuid[11], - (unsigned int) uuid[12], - (unsigned int) uuid[13], - (unsigned int) uuid[14], - (unsigned int) uuid[15]); -} - -static -void append_quoted_string_content(struct ctx *ctx, const char *str) -{ - const char *ch; - - for (ch = str; *ch != '\0'; ch++) { - unsigned char uch = (unsigned char) *ch; - - if (uch < 32 || uch >= 127) { - switch (*ch) { - case '\a': - g_string_append(ctx->tsdl, "\\a"); - break; - case '\b': - g_string_append(ctx->tsdl, "\\b"); - break; - case '\f': - g_string_append(ctx->tsdl, "\\f"); - break; - case '\n': - g_string_append(ctx->tsdl, "\\n"); - break; - case '\r': - g_string_append(ctx->tsdl, "\\r"); - break; - case '\t': - g_string_append(ctx->tsdl, "\\t"); - break; - case '\v': - g_string_append(ctx->tsdl, "\\v"); - break; - default: - g_string_append_printf(ctx->tsdl, "\\x%02x", - (unsigned int) uch); - break; - } - } else if (*ch == '"' || *ch == '\\') { - g_string_append_c(ctx->tsdl, '\\'); - g_string_append_c(ctx->tsdl, *ch); - } else { - g_string_append_c(ctx->tsdl, *ch); - } - } -} - -static -void append_quoted_string(struct ctx *ctx, const char *str) -{ - g_string_append_c(ctx->tsdl, '"'); - append_quoted_string_content(ctx, str); - g_string_append_c(ctx->tsdl, '"'); -} - -static -void append_integer_field_class_from_props(struct ctx *ctx, unsigned int size, - unsigned int alignment, bool is_signed, - bt_field_class_integer_preferred_display_base disp_base, - const char *mapped_clock_class_name, const char *field_name, - bool end) -{ - g_string_append_printf(ctx->tsdl, - "integer { size = %u; align = %u;", - size, alignment); - - if (is_signed) { - g_string_append(ctx->tsdl, " signed = true;"); - } - - if (disp_base != BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL) { - g_string_append(ctx->tsdl, " base = "); - - switch (disp_base) { - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: - g_string_append(ctx->tsdl, "b"); - break; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: - g_string_append(ctx->tsdl, "o"); - break; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: - g_string_append(ctx->tsdl, "x"); - break; - default: - abort(); - } - - g_string_append_c(ctx->tsdl, ';'); - } - - if (mapped_clock_class_name) { - g_string_append_printf(ctx->tsdl, " map = clock.%s.value;", - mapped_clock_class_name); - } - - g_string_append(ctx->tsdl, " }"); - - if (field_name) { - g_string_append_printf(ctx->tsdl, " %s", field_name); - } - - if (end) { - g_string_append(ctx->tsdl, ";\n"); - } -} - -static -void append_end_block(struct ctx *ctx) -{ - ctx->indent_level--; - append_indent(ctx); - g_string_append(ctx->tsdl, "}"); -} - -static -void append_end_block_semi_nl(struct ctx *ctx) -{ - ctx->indent_level--; - append_indent(ctx); - g_string_append(ctx->tsdl, "};\n"); -} - -static -void append_end_block_semi_nl_nl(struct ctx *ctx) -{ - append_end_block_semi_nl(ctx); - g_string_append_c(ctx->tsdl, '\n'); -} - -static -void append_integer_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class_int *fc) -{ - const bt_field_class *ir_fc = fc->base.base.ir_fc; - bt_field_class_type type = bt_field_class_get_type(ir_fc); - bool is_signed = type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION || - type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER; - - if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || - type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { - g_string_append(ctx->tsdl, "enum : "); - } - - append_integer_field_class_from_props(ctx, fc->base.size, - fc->base.base.alignment, is_signed, - bt_field_class_integer_get_preferred_display_base(ir_fc), - NULL, NULL, false); - - if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || - type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { - uint64_t i; - - g_string_append(ctx->tsdl, " {\n"); - ctx->indent_level++; - - for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_fc); i++) { - const char *label; - const bt_field_class_enumeration_mapping *mapping; - const bt_field_class_unsigned_enumeration_mapping *u_mapping; - const bt_field_class_signed_enumeration_mapping *i_mapping; - uint64_t range_count; - uint64_t range_i; - - if (is_signed) { - i_mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const( - ir_fc, i); - mapping = bt_field_class_signed_enumeration_mapping_as_mapping_const( - i_mapping); - } else { - u_mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( - ir_fc, i); - mapping = bt_field_class_unsigned_enumeration_mapping_as_mapping_const( - u_mapping); - } - - label = bt_field_class_enumeration_mapping_get_label( - mapping); - range_count = - bt_field_class_enumeration_mapping_get_range_count( - mapping); - - for (range_i = 0; range_i < range_count; range_i++) { - append_indent(ctx); - - /* - * Systematically prepend `_` to the - * mapping's label as this could be used - * as the tag of a subsequent variant - * field class and variant FC option - * names are systematically protected - * with a leading `_`. - * - * FIXME: This is temporary as the - * library's API should change to - * decouple variant FC option names from - * selector FC labels. The current - * drawback is that an original label - * `HELLO` becomes `_HELLO` in the - * generated metadata, therefore tools - * expecting `HELLO` could fail. - */ - g_string_append(ctx->tsdl, "\"_"); - append_quoted_string_content(ctx, label); - g_string_append(ctx->tsdl, "\" = "); - - if (is_signed) { - int64_t lower, upper; - - bt_field_class_signed_enumeration_mapping_get_range_by_index( - i_mapping, range_i, - &lower, &upper); - - if (lower == upper) { - g_string_append_printf( - ctx->tsdl, "%" PRId64, - lower); - } else { - g_string_append_printf( - ctx->tsdl, "%" PRId64 " ... %" PRId64, - lower, upper); - } - } else { - uint64_t lower, upper; - - bt_field_class_unsigned_enumeration_mapping_get_range_by_index( - u_mapping, range_i, - &lower, &upper); - - if (lower == upper) { - g_string_append_printf( - ctx->tsdl, "%" PRIu64, - lower); - } else { - g_string_append_printf( - ctx->tsdl, "%" PRIu64 " ... %" PRIu64, - lower, upper); - } - } - - g_string_append(ctx->tsdl, ",\n"); - } - } - - append_end_block(ctx); - } -} - -static -void append_float_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class_float *fc) -{ - unsigned int mant_dig, exp_dig; - - if (bt_field_class_real_is_single_precision(fc->base.base.ir_fc)) { - mant_dig = 24; - exp_dig = 8; - } else { - mant_dig = 53; - exp_dig = 11; - } - - g_string_append_printf(ctx->tsdl, - "floating_point { mant_dig = %u; exp_dig = %u; align = %u; }", - mant_dig, exp_dig, fc->base.base.alignment); -} - -static -void append_string_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class_float *fc) -{ - g_string_append(ctx->tsdl, "string { encoding = UTF8; }"); -} - -static -void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc); - -static -void append_member(struct ctx *ctx, const char *name, - struct fs_sink_ctf_field_class *fc) -{ - GString *lengths = NULL; - const char *lengths_str = ""; - - while (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY || - fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { - if (!lengths) { - lengths = g_string_new(NULL); - BT_ASSERT(lengths); - } - - if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY) { - struct fs_sink_ctf_field_class_array *array_fc = - (void *) fc; - - g_string_append_printf(lengths, "[%" PRIu64 "]", - array_fc->length); - fc = array_fc->base.elem_fc; - } else { - struct fs_sink_ctf_field_class_sequence *seq_fc = - (void *) fc; - - g_string_append_printf(lengths, "[%s]", - seq_fc->length_ref->str); - fc = seq_fc->base.elem_fc; - } - } - - append_field_class(ctx, fc); - - if (lengths) { - lengths_str = lengths->str; - } - - g_string_append_printf(ctx->tsdl, " %s%s;\n", name, lengths_str); - - if (lengths) { - g_string_free(lengths, TRUE); - } -} - -static -void append_struct_field_class_members(struct ctx *ctx, - struct fs_sink_ctf_field_class_struct *struct_fc) -{ - uint64_t i; - - for (i = 0; i < struct_fc->members->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - struct fs_sink_ctf_field_class *fc = named_fc->fc; - - if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { - struct fs_sink_ctf_field_class_sequence *seq_fc = - (void *) fc; - - if (seq_fc->length_is_before) { - append_indent(ctx); - append_integer_field_class_from_props(ctx, - 32, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, seq_fc->length_ref->str, true); - } - } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT) { - struct fs_sink_ctf_field_class_variant *var_fc = - (void *) fc; - - if (var_fc->tag_is_before) { - append_indent(ctx); - g_string_append(ctx->tsdl, "enum : "); - append_integer_field_class_from_props(ctx, - 16, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, NULL, false); - g_string_append(ctx->tsdl, " {\n"); - ctx->indent_level++; - - for (i = 0; i < var_fc->options->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - append_indent(ctx); - g_string_append_printf(ctx->tsdl, - "\"%s\" = %" PRIu64 ",\n", - named_fc->name->str, i); - } - - append_end_block(ctx); - g_string_append_printf(ctx->tsdl, " %s;\n", - var_fc->tag_ref->str); - } - } - - append_indent(ctx); - append_member(ctx, named_fc->name->str, fc); - } -} - -static -void append_struct_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class_struct *fc) -{ - g_string_append(ctx->tsdl, "struct {\n"); - ctx->indent_level++; - append_struct_field_class_members(ctx, fc); - append_end_block(ctx); - g_string_append_printf(ctx->tsdl, " align(%u)", - fc->base.alignment); -} - -static -void append_variant_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class_variant *var_fc) -{ - uint64_t i; - - g_string_append_printf(ctx->tsdl, "variant <%s> {\n", - var_fc->tag_ref->str); - ctx->indent_level++; - - for (i = 0; i < var_fc->options->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - - append_indent(ctx); - append_member(ctx, named_fc->name->str, named_fc->fc); - } - - append_end_block(ctx); -} - -static -void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc) -{ - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: - append_integer_field_class(ctx, (void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: - append_float_field_class(ctx, (void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: - append_string_field_class(ctx, (void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - append_struct_field_class(ctx, (void *) fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - append_variant_field_class(ctx, (void *) fc); - break; - default: - abort(); - } -} - -static -void append_event_class(struct ctx *ctx, struct fs_sink_ctf_event_class *ec) -{ - const char *str; - bt_event_class_log_level log_level; - - /* Event class */ - append_indent(ctx); - g_string_append(ctx->tsdl, "event {\n"); - ctx->indent_level++; - - /* Event class properties */ - append_indent(ctx); - g_string_append(ctx->tsdl, "name = "); - str = bt_event_class_get_name(ec->ir_ec); - if (!str) { - str = "unknown"; - } - - append_quoted_string(ctx, str); - g_string_append(ctx->tsdl, ";\n"); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "stream_id = %" PRIu64 ";\n", - bt_stream_class_get_id(ec->sc->ir_sc)); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n", - bt_event_class_get_id(ec->ir_ec)); - - str = bt_event_class_get_emf_uri(ec->ir_ec); - if (str) { - append_indent(ctx); - g_string_append(ctx->tsdl, "model.emf.uri = "); - append_quoted_string(ctx, str); - g_string_append(ctx->tsdl, ";\n"); - } - - if (bt_event_class_get_log_level(ec->ir_ec, &log_level) == - BT_PROPERTY_AVAILABILITY_AVAILABLE) { - unsigned int level; - - append_indent(ctx); - g_string_append(ctx->tsdl, "loglevel = "); - - switch (log_level) { - case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY: - level = 0; - break; - case BT_EVENT_CLASS_LOG_LEVEL_ALERT: - level = 1; - break; - case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL: - level = 2; - break; - case BT_EVENT_CLASS_LOG_LEVEL_ERROR: - level = 3; - break; - case BT_EVENT_CLASS_LOG_LEVEL_WARNING: - level = 4; - break; - case BT_EVENT_CLASS_LOG_LEVEL_NOTICE: - level = 5; - break; - case BT_EVENT_CLASS_LOG_LEVEL_INFO: - level = 6; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: - level = 7; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: - level = 8; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: - level = 9; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: - level = 10; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: - level = 11; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: - level = 12; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: - level = 13; - break; - case BT_EVENT_CLASS_LOG_LEVEL_DEBUG: - level = 14; - break; - default: - abort(); - } - - g_string_append_printf(ctx->tsdl, "%u;\n", level); - } - - /* Event specific context field class */ - if (ec->spec_context_fc) { - append_indent(ctx); - g_string_append(ctx->tsdl, "context := "); - append_field_class(ctx, ec->spec_context_fc); - g_string_append(ctx->tsdl, ";\n"); - } - - /* Event payload field class */ - if (ec->payload_fc) { - append_indent(ctx); - g_string_append(ctx->tsdl, "fields := "); - append_field_class(ctx, ec->payload_fc); - g_string_append(ctx->tsdl, ";\n"); - } - - append_end_block_semi_nl_nl(ctx); -} - -static -void append_stream_class(struct ctx *ctx, - struct fs_sink_ctf_stream_class *sc) -{ - uint64_t i; - - /* Default clock class */ - if (sc->default_clock_class) { - const char *descr; - int64_t offset_seconds; - uint64_t offset_cycles; - bt_uuid uuid; - - append_indent(ctx); - g_string_append(ctx->tsdl, "clock {\n"); - ctx->indent_level++; - BT_ASSERT(sc->default_clock_class_name->len > 0); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "name = %s;\n", - sc->default_clock_class_name->str); - descr = bt_clock_class_get_description(sc->default_clock_class); - if (descr) { - append_indent(ctx); - g_string_append(ctx->tsdl, "description = "); - append_quoted_string(ctx, descr); - g_string_append(ctx->tsdl, ";\n"); - } - - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "freq = %" PRIu64 ";\n", - bt_clock_class_get_frequency(sc->default_clock_class)); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "precision = %" PRIu64 ";\n", - bt_clock_class_get_precision(sc->default_clock_class)); - bt_clock_class_get_offset(sc->default_clock_class, - &offset_seconds, &offset_cycles); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "offset_s = %" PRId64 ";\n", - offset_seconds); - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "offset = %" PRIu64 ";\n", - offset_cycles); - append_indent(ctx); - g_string_append(ctx->tsdl, "absolute = "); - - if (bt_clock_class_origin_is_unix_epoch( - sc->default_clock_class)) { - g_string_append(ctx->tsdl, "true"); - } else { - g_string_append(ctx->tsdl, "false"); - } - - g_string_append(ctx->tsdl, ";\n"); - uuid = bt_clock_class_get_uuid(sc->default_clock_class); - if (uuid) { - append_indent(ctx); - g_string_append(ctx->tsdl, "uuid = "); - append_uuid(ctx, uuid); - g_string_append(ctx->tsdl, ";\n"); - } - - /* End clock class */ - append_end_block_semi_nl_nl(ctx); - } - - /* Stream class */ - append_indent(ctx); - g_string_append(ctx->tsdl, "stream {\n"); - ctx->indent_level++; - - /* Stream class properties */ - append_indent(ctx); - g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n", - bt_stream_class_get_id(sc->ir_sc)); - - /* Packet context field class */ - append_indent(ctx); - g_string_append(ctx->tsdl, "packet.context := struct {\n"); - ctx->indent_level++; - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "packet_size", true); - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "content_size", true); - - if (sc->packets_have_ts_begin) { - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - sc->default_clock_class_name->str, - "timestamp_begin", true); - } - - if (sc->packets_have_ts_end) { - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - sc->default_clock_class_name->str, - "timestamp_end", true); - } - - if (sc->has_discarded_events) { - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "events_discarded", true); - } - - /* - * Unconditionnally write the packet sequence number as, even if - * there's no possible discarded packets message, it's still - * useful information to have. - */ - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "packet_seq_num", true); - - if (sc->packet_context_fc) { - append_struct_field_class_members(ctx, - (void *) sc->packet_context_fc); - fs_sink_ctf_field_class_struct_align_at_least( - (void *) sc->packet_context_fc, 8); - } - - /* End packet context field class */ - append_end_block(ctx); - g_string_append_printf(ctx->tsdl, " align(%u);\n\n", - sc->packet_context_fc ? sc->packet_context_fc->alignment : 8); - - /* Event header field class */ - append_indent(ctx); - g_string_append(ctx->tsdl, "event.header := struct {\n"); - ctx->indent_level++; - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "id", true); - - if (sc->default_clock_class) { - append_indent(ctx); - append_integer_field_class_from_props(ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - sc->default_clock_class_name->str, - "timestamp", true); - } - - /* End event header field class */ - append_end_block(ctx); - g_string_append(ctx->tsdl, " align(8);\n"); - - /* Event common context field class */ - if (sc->event_common_context_fc) { - append_indent(ctx); - g_string_append(ctx->tsdl, "event.context := "); - append_field_class(ctx, - (void *) sc->event_common_context_fc); - g_string_append(ctx->tsdl, ";\n"); - } - - /* End stream class */ - append_end_block_semi_nl_nl(ctx); - - /* Event classes */ - for (i = 0; i < sc->event_classes->len; i++) { - append_event_class(ctx, sc->event_classes->pdata[i]); - } -} - -BT_HIDDEN -void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc, - GString *tsdl) -{ - struct ctx ctx = { - .indent_level = 0, - .tsdl = tsdl, - }; - uint64_t i; - uint64_t count; - - g_string_assign(tsdl, "/* CTF 1.8 */\n\n"); - g_string_append(tsdl, "/* This was generated by a Babeltrace `sink.ctf.fs` component. */\n\n"); - - /* Trace class */ - append_indent(&ctx); - g_string_append(tsdl, "trace {\n"); - ctx.indent_level++; - - /* Trace class properties */ - append_indent(&ctx); - g_string_append(tsdl, "major = 1;\n"); - append_indent(&ctx); - g_string_append(tsdl, "minor = 8;\n"); - append_indent(&ctx); - g_string_append(tsdl, "uuid = "); - append_uuid(&ctx, tc->uuid); - g_string_append(tsdl, ";\n"); - append_indent(&ctx); - g_string_append(tsdl, "byte_order = "); - - if (BYTE_ORDER == LITTLE_ENDIAN) { - g_string_append(tsdl, "le"); - } else { - g_string_append(tsdl, "be"); - } - - g_string_append(tsdl, ";\n"); - - /* Packet header field class */ - append_indent(&ctx); - g_string_append(tsdl, "packet.header := struct {\n"); - ctx.indent_level++; - append_indent(&ctx); - append_integer_field_class_from_props(&ctx, 32, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL, - NULL, "magic", true); - append_indent(&ctx); - append_integer_field_class_from_props(&ctx, 8, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "uuid[16]", true); - append_indent(&ctx); - append_integer_field_class_from_props(&ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "stream_id", true); - append_indent(&ctx); - append_integer_field_class_from_props(&ctx, 64, 8, false, - BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, - NULL, "stream_instance_id", true); - - /* End packet header field class */ - append_end_block(&ctx); - g_string_append(ctx.tsdl, " align(8);\n"); - - /* End trace class */ - append_end_block_semi_nl_nl(&ctx); - - /* Trace class environment */ - count = bt_trace_class_get_environment_entry_count(tc->ir_tc); - if (count > 0) { - append_indent(&ctx); - g_string_append(tsdl, "env {\n"); - ctx.indent_level++; - - for (i = 0; i < count; i++) { - const char *name; - const bt_value *val; - - bt_trace_class_borrow_environment_entry_by_index_const( - tc->ir_tc, i, &name, &val); - append_indent(&ctx); - g_string_append_printf(tsdl, "%s = ", name); - - switch (bt_value_get_type(val)) { - case BT_VALUE_TYPE_SIGNED_INTEGER: - g_string_append_printf(tsdl, "%" PRId64, - bt_value_signed_integer_get(val)); - break; - case BT_VALUE_TYPE_STRING: - append_quoted_string(&ctx, bt_value_string_get(val)); - break; - default: - /* - * This is checked in - * translate_trace_class_trace_ir_to_ctf_ir(). - */ - abort(); - } - - g_string_append(tsdl, ";\n"); - } - - /* End trace class environment */ - append_end_block_semi_nl_nl(&ctx); - } - - /* Stream classes and their event classes */ - for (i = 0; i < tc->stream_classes->len; i++) { - append_stream_class(&ctx, tc->stream_classes->pdata[i]); - } -} diff --git a/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h b/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h deleted file mode 100644 index 4ed02625..00000000 --- a/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "fs-sink-ctf-meta.h" - -BT_HIDDEN -void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc, - GString *tsdl); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */ diff --git a/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c b/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c deleted file mode 100644 index d398f659..00000000 --- a/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-TRACE-IR-TO-CTF-IR" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fs-sink-ctf-meta.h" - -struct field_path_elem { - uint64_t index_in_parent; - GString *name; - - /* Weak */ - const bt_field_class *ir_fc; - - /* Weak */ - struct fs_sink_ctf_field_class *parent_fc; -}; - -struct ctx { - /* Weak */ - struct fs_sink_ctf_stream_class *cur_sc; - - /* Weak */ - struct fs_sink_ctf_event_class *cur_ec; - - bt_scope cur_scope; - - /* - * Array of `struct field_path_elem` */ - GArray *cur_path; -}; - -static inline -struct field_path_elem *cur_path_stack_at(struct ctx *ctx, uint64_t i) -{ - BT_ASSERT(i < ctx->cur_path->len); - return &g_array_index(ctx->cur_path, struct field_path_elem, i); -} - -static inline -struct field_path_elem *cur_path_stack_top(struct ctx *ctx) -{ - BT_ASSERT(ctx->cur_path->len > 0); - return cur_path_stack_at(ctx, ctx->cur_path->len - 1); -} - -static inline -bool is_reserved_member_name(const char *name, const char *reserved_name) -{ - bool is_reserved = false; - - if (strcmp(name, reserved_name) == 0) { - is_reserved = true; - goto end; - } - - if (name[0] == '_' && strcmp(&name[1], reserved_name) == 0) { - is_reserved = true; - goto end; - } - -end: - return is_reserved; -} - -static inline -int cur_path_stack_push(struct ctx *ctx, - uint64_t index_in_parent, const char *ir_name, - const bt_field_class *ir_fc, - struct fs_sink_ctf_field_class *parent_fc) -{ - int ret = 0; - struct field_path_elem *field_path_elem; - - g_array_set_size(ctx->cur_path, ctx->cur_path->len + 1); - field_path_elem = cur_path_stack_top(ctx); - field_path_elem->index_in_parent = index_in_parent; - field_path_elem->name = g_string_new(ir_name); - - if (ir_name) { - if (ctx->cur_scope == BT_SCOPE_PACKET_CONTEXT) { - if (is_reserved_member_name(ir_name, "packet_size") || - is_reserved_member_name(ir_name, "content_size") || - is_reserved_member_name(ir_name, "timestamp_begin") || - is_reserved_member_name(ir_name, "timestamp_end") || - is_reserved_member_name(ir_name, "events_discarded") || - is_reserved_member_name(ir_name, "packet_seq_num")) { - BT_LOGE("Unsupported reserved TSDL structure field class member " - "or variant field class option name: name=\"%s\"", - ir_name); - ret = -1; - goto end; - } - } - - ret = fs_sink_ctf_protect_name(field_path_elem->name); - if (ret) { - BT_LOGE("Unsupported non-TSDL structure field class member " - "or variant field class option name: name=\"%s\"", - ir_name); - goto end; - } - } - - field_path_elem->ir_fc = ir_fc; - field_path_elem->parent_fc = parent_fc; - -end: - return ret; -} - -static inline -void cur_path_stack_pop(struct ctx *ctx) -{ - struct field_path_elem *field_path_elem; - - BT_ASSERT(ctx->cur_path->len > 0); - field_path_elem = cur_path_stack_top(ctx); - - if (field_path_elem->name) { - g_string_free(field_path_elem->name, TRUE); - field_path_elem->name = NULL; - } - - g_array_set_size(ctx->cur_path, ctx->cur_path->len - 1); -} - -/* - * Creates a relative field ref (a single name) from IR field path - * `tgt_ir_field_path`. - * - * This function tries to locate the target field class recursively from - * the top to the bottom of the context's current path using only the - * target field class's own name. This is because many CTF reading tools - * do not support a relative field ref with more than one element, for - * example `prev_struct.len`. - * - * Returns a negative value if this resolving operation failed. - */ -static -int create_relative_field_ref(struct ctx *ctx, - const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref) -{ - int ret = 0; - struct fs_sink_ctf_field_class *tgt_fc = NULL; - uint64_t i; - int64_t si; - const char *tgt_fc_name = NULL; - struct field_path_elem *field_path_elem; - - /* Get target field class's name */ - switch (bt_field_path_get_root_scope(tgt_ir_field_path)) { - case BT_SCOPE_PACKET_CONTEXT: - BT_ASSERT(ctx->cur_sc); - tgt_fc = ctx->cur_sc->packet_context_fc; - break; - case BT_SCOPE_EVENT_COMMON_CONTEXT: - BT_ASSERT(ctx->cur_sc); - tgt_fc = ctx->cur_sc->event_common_context_fc; - break; - case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: - BT_ASSERT(ctx->cur_ec); - tgt_fc = ctx->cur_ec->spec_context_fc; - break; - case BT_SCOPE_EVENT_PAYLOAD: - BT_ASSERT(ctx->cur_ec); - tgt_fc = ctx->cur_ec->payload_fc; - break; - default: - abort(); - } - - i = 0; - - while (i < bt_field_path_get_item_count(tgt_ir_field_path)) { - const bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_const( - tgt_ir_field_path, i); - struct fs_sink_ctf_named_field_class *named_fc = NULL; - - BT_ASSERT(tgt_fc); - BT_ASSERT(fp_item); - - switch (tgt_fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( - (void *) tgt_fc, - bt_field_path_item_index_get_index(fp_item)); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( - (void *) tgt_fc, - bt_field_path_item_index_get_index(fp_item)); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct fs_sink_ctf_field_class_array_base *array_base_fc = - (void *) tgt_fc; - - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); - tgt_fc = array_base_fc->elem_fc; - break; - } - default: - abort(); - } - - if (named_fc) { - tgt_fc = named_fc->fc; - tgt_fc_name = named_fc->name->str; - i++; - } - } - - BT_ASSERT(tgt_fc); - BT_ASSERT(tgt_fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT); - BT_ASSERT(tgt_fc_name); - - /* Find target field class having this name in current context */ - for (si = ctx->cur_path->len - 1; si >= 0; si--) { - struct fs_sink_ctf_field_class *fc; - struct fs_sink_ctf_field_class_struct *struct_fc; - struct fs_sink_ctf_field_class_variant *var_fc; - struct fs_sink_ctf_named_field_class *named_fc; - uint64_t len; - - field_path_elem = cur_path_stack_at(ctx, (uint64_t) si); - fc = field_path_elem->parent_fc; - if (!fc) { - /* Reached stack's bottom */ - ret = -1; - goto end; - } - - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - continue; - default: - /* Not supported by TSDL 1.8 */ - ret = -1; - goto end; - } - - if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { - struct_fc = (void *) fc; - len = struct_fc->members->len; - } else { - var_fc = (void *) fc; - len = var_fc->options->len; - } - - for (i = 0; i < len; i++) { - if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { - named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - } else { - named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - } - - if (strcmp(named_fc->name->str, tgt_fc_name) == 0) { - if (named_fc->fc == tgt_fc) { - g_string_assign(tgt_field_ref, - tgt_fc_name); - } else { - /* - * Using only the target field - * class's name, we're not - * reaching the target field - * class. This is not supported - * by TSDL 1.8. - */ - ret = -1; - } - - goto end; - } - } - } - -end: - return ret; -} - -/* - * Creates an absolute field ref from IR field path `tgt_ir_field_path`. - * - * Returns a negative value if this resolving operation failed. - */ -static -int create_absolute_field_ref(struct ctx *ctx, - const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref) -{ - int ret = 0; - struct fs_sink_ctf_field_class *fc = NULL; - uint64_t i; - - switch (bt_field_path_get_root_scope(tgt_ir_field_path)) { - case BT_SCOPE_PACKET_CONTEXT: - BT_ASSERT(ctx->cur_sc); - fc = ctx->cur_sc->packet_context_fc; - g_string_assign(tgt_field_ref, "stream.packet.context"); - break; - case BT_SCOPE_EVENT_COMMON_CONTEXT: - BT_ASSERT(ctx->cur_sc); - fc = ctx->cur_sc->event_common_context_fc; - g_string_assign(tgt_field_ref, "stream.event.context"); - break; - case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: - BT_ASSERT(ctx->cur_ec); - fc = ctx->cur_ec->spec_context_fc; - g_string_assign(tgt_field_ref, "event.context"); - break; - case BT_SCOPE_EVENT_PAYLOAD: - BT_ASSERT(ctx->cur_ec); - fc = ctx->cur_ec->payload_fc; - g_string_assign(tgt_field_ref, "event.fields"); - break; - default: - abort(); - } - - BT_ASSERT(fc); - - for (i = 0; i < bt_field_path_get_item_count(tgt_ir_field_path); i++) { - const bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_const( - tgt_ir_field_path, i); - struct fs_sink_ctf_named_field_class *named_fc = NULL; - - if (bt_field_path_item_get_type(fp_item) != - BT_FIELD_PATH_ITEM_TYPE_INDEX) { - /* Not supported by TSDL 1.8 */ - ret = -1; - goto end; - } - - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( - (void *) fc, - bt_field_path_item_index_get_index(fp_item)); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( - (void *) fc, - bt_field_path_item_index_get_index(fp_item)); - break; - default: - abort(); - } - - BT_ASSERT(named_fc); - g_string_append_c(tgt_field_ref, '.'); - g_string_append(tgt_field_ref, named_fc->name->str); - fc = named_fc->fc; - } - -end: - return ret; -} - -/* - * Resolves a target field class located at `tgt_ir_field_path`, writing - * the resolved field ref to `tgt_field_ref` and setting - * `*create_before` according to whether or not the target field must be - * created immediately before (in which case `tgt_field_ref` is - * irrelevant). - */ -static -void resolve_field_class(struct ctx *ctx, - const bt_field_path *tgt_ir_field_path, - GString *tgt_field_ref, bool *create_before) -{ - int ret; - bt_scope tgt_scope; - - *create_before = false; - - if (!tgt_ir_field_path) { - *create_before = true; - goto end; - } - - tgt_scope = bt_field_path_get_root_scope(tgt_ir_field_path); - - if (tgt_scope == ctx->cur_scope) { - /* - * Try, in this order: - * - * 1. Use a relative path, using only the target field - * class's name. This is what is the most commonly - * supported by popular CTF reading tools. - * - * 2. Use an absolute path. This could fail if there's - * an array field class from the current root's field - * class to the target field class. - * - * 3. Create the target field class before the - * requesting field class (fallback). - */ - ret = create_relative_field_ref(ctx, tgt_ir_field_path, - tgt_field_ref); - if (ret) { - ret = create_absolute_field_ref(ctx, tgt_ir_field_path, - tgt_field_ref); - if (ret) { - *create_before = true; - ret = 0; - goto end; - } - } - } else { - ret = create_absolute_field_ref(ctx, tgt_ir_field_path, - tgt_field_ref); - - /* It must always work in previous scopes */ - BT_ASSERT(ret == 0); - } - -end: - return; -} - -static -int translate_field_class(struct ctx *ctx); - -static inline -void append_to_parent_field_class(struct ctx *ctx, - struct fs_sink_ctf_field_class *fc) -{ - struct fs_sink_ctf_field_class *parent_fc = - cur_path_stack_top(ctx)->parent_fc; - - switch (parent_fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - fs_sink_ctf_field_class_struct_append_member((void *) parent_fc, - cur_path_stack_top(ctx)->name->str, fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - fs_sink_ctf_field_class_variant_append_option((void *) parent_fc, - cur_path_stack_top(ctx)->name->str, fc); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct fs_sink_ctf_field_class_array_base *array_base_fc = - (void *) parent_fc; - - BT_ASSERT(!array_base_fc->elem_fc); - array_base_fc->elem_fc = fc; - array_base_fc->base.alignment = fc->alignment; - break; - } - default: - abort(); - } -} - -static inline -void update_parent_field_class_alignment(struct ctx *ctx, - unsigned int alignment) -{ - struct fs_sink_ctf_field_class *parent_fc = - cur_path_stack_top(ctx)->parent_fc; - - switch (parent_fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - fs_sink_ctf_field_class_struct_align_at_least( - (void *) parent_fc, alignment); - break; - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct fs_sink_ctf_field_class_array_base *array_base_fc = - (void *) parent_fc; - - array_base_fc->base.alignment = alignment; - break; - } - default: - break; - } -} - -static inline -int translate_structure_field_class_members(struct ctx *ctx, - struct fs_sink_ctf_field_class_struct *struct_fc, - const bt_field_class *ir_fc) -{ - int ret = 0; - uint64_t i; - - for (i = 0; i < bt_field_class_structure_get_member_count(ir_fc); i++) { - const bt_field_class_structure_member *member; - const char *name; - const bt_field_class *memb_ir_fc; - - member = - bt_field_class_structure_borrow_member_by_index_const( - ir_fc, i); - name = bt_field_class_structure_member_get_name(member); - memb_ir_fc = bt_field_class_structure_member_borrow_field_class_const( - member); - ret = cur_path_stack_push(ctx, i, name, memb_ir_fc, - (void *) struct_fc); - if (ret) { - BT_LOGE("Cannot translate structure field class member: " - "name=\"%s\"", name); - goto end; - } - - ret = translate_field_class(ctx); - if (ret) { - BT_LOGE("Cannot translate structure field class member: " - "name=\"%s\"", name); - goto end; - } - - cur_path_stack_pop(ctx); - } - -end: - return ret; -} - -static inline -int translate_structure_field_class(struct ctx *ctx) -{ - int ret; - struct fs_sink_ctf_field_class_struct *fc = - fs_sink_ctf_field_class_struct_create_empty( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - - BT_ASSERT(fc); - append_to_parent_field_class(ctx, (void *) fc); - ret = translate_structure_field_class_members(ctx, fc, fc->base.ir_fc); - if (ret) { - goto end; - } - - update_parent_field_class_alignment(ctx, fc->base.alignment); - -end: - return ret; -} - -static inline -int translate_variant_field_class(struct ctx *ctx) -{ - int ret = 0; - uint64_t i; - struct fs_sink_ctf_field_class_variant *fc = - fs_sink_ctf_field_class_variant_create_empty( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - - BT_ASSERT(fc); - - /* Resolve tag field class before appending to parent */ - resolve_field_class(ctx, - bt_field_class_variant_borrow_selector_field_path_const( - fc->base.ir_fc), fc->tag_ref, &fc->tag_is_before); - - append_to_parent_field_class(ctx, (void *) fc); - - for (i = 0; i < bt_field_class_variant_get_option_count(fc->base.ir_fc); - i++) { - const bt_field_class_variant_option *opt; - const char *name; - const bt_field_class *opt_ir_fc; - - opt = bt_field_class_variant_borrow_option_by_index_const( - fc->base.ir_fc, i); - name = bt_field_class_variant_option_get_name(opt); - opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const( - opt); - ret = cur_path_stack_push(ctx, i, name, opt_ir_fc, (void *) fc); - if (ret) { - BT_LOGE("Cannot translate variant field class option: " - "name=\"%s\"", name); - goto end; - } - - ret = translate_field_class(ctx); - if (ret) { - BT_LOGE("Cannot translate variant field class option: " - "name=\"%s\"", name); - goto end; - } - - cur_path_stack_pop(ctx); - } - -end: - return ret; -} - -static inline -int translate_static_array_field_class(struct ctx *ctx) -{ - struct fs_sink_ctf_field_class_array *fc = - fs_sink_ctf_field_class_array_create_empty( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - const bt_field_class *elem_ir_fc = - bt_field_class_array_borrow_element_field_class_const( - fc->base.base.ir_fc); - int ret; - - BT_ASSERT(fc); - append_to_parent_field_class(ctx, (void *) fc); - ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc, - (void *) fc); - if (ret) { - BT_LOGE_STR("Cannot translate static array field class element."); - goto end; - } - - ret = translate_field_class(ctx); - if (ret) { - BT_LOGE_STR("Cannot translate static array field class element."); - goto end; - } - - cur_path_stack_pop(ctx); - update_parent_field_class_alignment(ctx, fc->base.base.alignment); - -end: - return ret; -} - -static inline -int translate_dynamic_array_field_class(struct ctx *ctx) -{ - struct fs_sink_ctf_field_class_sequence *fc = - fs_sink_ctf_field_class_sequence_create_empty( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - const bt_field_class *elem_ir_fc = - bt_field_class_array_borrow_element_field_class_const( - fc->base.base.ir_fc); - int ret; - - BT_ASSERT(fc); - - /* Resolve length field class before appending to parent */ - resolve_field_class(ctx, - bt_field_class_dynamic_array_borrow_length_field_path_const( - fc->base.base.ir_fc), - fc->length_ref, &fc->length_is_before); - - append_to_parent_field_class(ctx, (void *) fc); - ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc, - (void *) fc); - if (ret) { - BT_LOGE_STR("Cannot translate dynamic array field class element."); - goto end; - } - - ret = translate_field_class(ctx); - if (ret) { - BT_LOGE_STR("Cannot translate dynamic array field class element."); - goto end; - } - - cur_path_stack_pop(ctx); - update_parent_field_class_alignment(ctx, fc->base.base.alignment); - -end: - return ret; -} - -static inline -int translate_integer_field_class(struct ctx *ctx) -{ - struct fs_sink_ctf_field_class_int *fc = - fs_sink_ctf_field_class_int_create( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - - BT_ASSERT(fc); - append_to_parent_field_class(ctx, (void *) fc); - return 0; -} - -static inline -int translate_real_field_class(struct ctx *ctx) -{ - struct fs_sink_ctf_field_class_float *fc = - fs_sink_ctf_field_class_float_create( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - - BT_ASSERT(fc); - append_to_parent_field_class(ctx, (void *) fc); - return 0; -} - -static inline -int translate_string_field_class(struct ctx *ctx) -{ - struct fs_sink_ctf_field_class_string *fc = - fs_sink_ctf_field_class_string_create( - cur_path_stack_top(ctx)->ir_fc, - cur_path_stack_top(ctx)->index_in_parent); - - BT_ASSERT(fc); - append_to_parent_field_class(ctx, (void *) fc); - return 0; -} - -/* - * Translates a field class, recursively. - * - * The field class's IR field class, parent field class, and index - * within its parent are in the context's current path's top element - * (cur_path_stack_top()). - */ -static -int translate_field_class(struct ctx *ctx) -{ - int ret; - - switch (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc)) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - ret = translate_integer_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_REAL: - ret = translate_real_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_STRING: - ret = translate_string_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_STRUCTURE: - ret = translate_structure_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - ret = translate_static_array_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - ret = translate_dynamic_array_field_class(ctx); - break; - case BT_FIELD_CLASS_TYPE_VARIANT: - ret = translate_variant_field_class(ctx); - break; - default: - abort(); - } - - return ret; -} - -static -int set_field_ref(struct fs_sink_ctf_field_class *fc, const char *fc_name, - struct fs_sink_ctf_field_class *parent_fc) -{ - int ret = 0; - GString *field_ref = NULL; - bool is_before; - const char *tgt_type; - struct fs_sink_ctf_field_class_struct *parent_struct_fc = - (void *) parent_fc; - uint64_t i; - unsigned int suffix = 0; - - if (!fc_name || !parent_fc || - parent_fc->type != FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { - /* Not supported */ - ret = -1; - goto end; - } - - switch (fc->type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct fs_sink_ctf_field_class_sequence *seq_fc = (void *) fc; - - field_ref = seq_fc->length_ref; - is_before = seq_fc->length_is_before; - tgt_type = "len"; - break; - } - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - { - struct fs_sink_ctf_field_class_variant *var_fc = (void *) fc; - - field_ref = var_fc->tag_ref; - is_before = var_fc->tag_is_before; - tgt_type = "tag"; - break; - } - default: - abort(); - } - - BT_ASSERT(field_ref); - - if (!is_before) { - goto end; - } - - /* Initial field ref */ - g_string_printf(field_ref, "__%s_%s", fc_name, tgt_type); - - /* - * Make sure field ref does not clash with an existing field - * class name within the same parent structure field class. - */ - while (true) { - bool name_ok = true; - - for (i = 0; i < parent_struct_fc->members->len; i++) { - struct fs_sink_ctf_named_field_class *named_fc = - fs_sink_ctf_field_class_struct_borrow_member_by_index( - parent_struct_fc, i); - - if (strcmp(field_ref->str, named_fc->name->str) == 0) { - /* Name clash */ - name_ok = false; - break; - } - } - - if (name_ok) { - /* No clash: we're done */ - break; - } - - /* Append suffix and try again */ - g_string_printf(field_ref, "__%s_%s_%u", fc_name, tgt_type, - suffix); - suffix++; - } - -end: - return ret; -} - -/* - * This function recursively sets field refs of sequence and variant - * field classes when they are immediately before, avoiding name clashes - * with existing field class names. - * - * It can fail at this point if, for example, a sequence field class of - * which to set the length's field ref has something else than a - * structure field class as its parent: in this case, there's no - * location to place the length field class immediately before the - * sequence field class. - */ -static -int set_field_refs(struct fs_sink_ctf_field_class * const fc, - const char *fc_name, struct fs_sink_ctf_field_class *parent_fc) -{ - int ret = 0; - enum fs_sink_ctf_field_class_type fc_type; - BT_ASSERT(fc); - - fc_type = fc->type; - - switch (fc_type) { - case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: - case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: - { - uint64_t i; - uint64_t len; - struct fs_sink_ctf_field_class_struct *struct_fc; - struct fs_sink_ctf_field_class_variant *var_fc = NULL; - struct fs_sink_ctf_named_field_class *named_fc; - - if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { - struct_fc = (void *) fc; - len = struct_fc->members->len; - } else { - var_fc = (void *) fc; - len = var_fc->options->len; - ret = set_field_ref(fc, fc_name, parent_fc); - if (ret) { - goto end; - } - } - - for (i = 0; i < len; i++) { - if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { - named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( - struct_fc, i); - } else { - named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( - var_fc, i); - } - - ret = set_field_refs(named_fc->fc, named_fc->name->str, - fc); - if (ret) { - goto end; - } - } - - break; - } - case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: - case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: - { - struct fs_sink_ctf_field_class_array_base *array_base_fc = - (void *) fc; - - if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { - ret = set_field_ref(fc, fc_name, parent_fc); - if (ret) { - goto end; - } - } - - ret = set_field_refs(array_base_fc->elem_fc, NULL, fc); - if (ret) { - goto end; - } - - break; - } - default: - break; - } - -end: - return ret; -} - -/* - * This function translates a root scope trace IR field class to - * a CTF IR field class. - * - * The resulting CTF IR field class is written to `*fc` so that it - * exists as the parent object's (stream class or event class) true root - * field class during the recursive translation for resolving purposes. - * This is also why this function creates the empty structure field - * class and then calls translate_structure_field_class_members() to - * fill it. - */ -static -int translate_scope_field_class(struct ctx *ctx, bt_scope scope, - struct fs_sink_ctf_field_class **fc, - const bt_field_class *ir_fc) -{ - int ret = 0; - - if (!ir_fc) { - goto end; - } - - BT_ASSERT(bt_field_class_get_type(ir_fc) == - BT_FIELD_CLASS_TYPE_STRUCTURE); - BT_ASSERT(fc); - *fc = (void *) fs_sink_ctf_field_class_struct_create_empty( - ir_fc, UINT64_C(-1)); - BT_ASSERT(*fc); - ctx->cur_scope = scope; - BT_ASSERT(ctx->cur_path->len == 0); - ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, ir_fc, NULL); - if (ret) { - BT_LOGE("Cannot translate scope structure field class: " - "scope=%d", scope); - goto end; - } - - ret = translate_structure_field_class_members(ctx, (void *) *fc, ir_fc); - if (ret) { - BT_LOGE("Cannot translate scope structure field class: " - "scope=%d", scope); - goto end; - } - - cur_path_stack_pop(ctx); - - /* Set field refs for preceding targets */ - ret = set_field_refs(*fc, NULL, NULL); - -end: - return ret; -} - -static inline -void ctx_init(struct ctx *ctx) -{ - memset(ctx, 0, sizeof(struct ctx)); - ctx->cur_path = g_array_new(FALSE, TRUE, - sizeof(struct field_path_elem)); - BT_ASSERT(ctx->cur_path); -} - -static inline -void ctx_fini(struct ctx *ctx) -{ - if (ctx->cur_path) { - g_array_free(ctx->cur_path, TRUE); - ctx->cur_path = NULL; - } -} - -static -int translate_event_class(struct fs_sink_ctf_stream_class *sc, - const bt_event_class *ir_ec, - struct fs_sink_ctf_event_class **out_ec) -{ - int ret = 0; - struct ctx ctx; - struct fs_sink_ctf_event_class *ec; - - BT_ASSERT(sc); - BT_ASSERT(ir_ec); - - ctx_init(&ctx); - ec = fs_sink_ctf_event_class_create(sc, ir_ec); - BT_ASSERT(ec); - ctx.cur_sc = sc; - ctx.cur_ec = ec; - ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_SPECIFIC_CONTEXT, - &ec->spec_context_fc, - bt_event_class_borrow_specific_context_field_class_const( - ir_ec)); - if (ret) { - goto end; - } - - ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_PAYLOAD, - &ec->payload_fc, - bt_event_class_borrow_payload_field_class_const(ir_ec)); - if (ret) { - goto end; - } - -end: - ctx_fini(&ctx); - *out_ec = ec; - return ret; -} - -BT_HIDDEN -int try_translate_event_class_trace_ir_to_ctf_ir( - struct fs_sink_ctf_stream_class *sc, - const bt_event_class *ir_ec, - struct fs_sink_ctf_event_class **out_ec) -{ - int ret = 0; - - BT_ASSERT(sc); - BT_ASSERT(ir_ec); - - /* Check in hash table first */ - *out_ec = g_hash_table_lookup(sc->event_classes_from_ir, ir_ec); - if (likely(*out_ec)) { - goto end; - } - - ret = translate_event_class(sc, ir_ec, out_ec); - -end: - return ret; -} - -bool default_clock_class_name_exists(struct fs_sink_ctf_trace_class *tc, - const char *name) -{ - bool exists = false; - uint64_t i; - - for (i = 0; i < tc->stream_classes->len; i++) { - struct fs_sink_ctf_stream_class *sc = - tc->stream_classes->pdata[i]; - - if (sc->default_clock_class_name->len == 0) { - /* No default clock class */ - continue; - } - - if (strcmp(sc->default_clock_class_name->str, name) == 0) { - exists = true; - goto end; - } - } - -end: - return exists; -} - -static -void make_unique_default_clock_class_name(struct fs_sink_ctf_stream_class *sc) -{ - unsigned int suffix = 0; - char buf[16]; - - g_string_assign(sc->default_clock_class_name, ""); - sprintf(buf, "default"); - - while (default_clock_class_name_exists(sc->tc, buf)) { - sprintf(buf, "default%u", suffix); - suffix++; - } - - g_string_assign(sc->default_clock_class_name, buf); -} - -static -int translate_stream_class(struct fs_sink_ctf_trace_class *tc, - const bt_stream_class *ir_sc, - struct fs_sink_ctf_stream_class **out_sc) -{ - int ret = 0; - struct ctx ctx; - - BT_ASSERT(tc); - BT_ASSERT(ir_sc); - ctx_init(&ctx); - *out_sc = fs_sink_ctf_stream_class_create(tc, ir_sc); - BT_ASSERT(*out_sc); - - /* Set default clock class's protected name, if any */ - if ((*out_sc)->default_clock_class) { - const char *name = bt_clock_class_get_name( - (*out_sc)->default_clock_class); - - if (name) { - /* Try original name, protected */ - g_string_assign((*out_sc)->default_clock_class_name, - name); - ret = fs_sink_ctf_protect_name( - (*out_sc)->default_clock_class_name); - if (ret) { - /* Invalid: create a new name */ - make_unique_default_clock_class_name(*out_sc); - ret = 0; - } - } else { - /* No name: create a name */ - make_unique_default_clock_class_name(*out_sc); - } - } - - ctx.cur_sc = *out_sc; - ret = translate_scope_field_class(&ctx, BT_SCOPE_PACKET_CONTEXT, - &(*out_sc)->packet_context_fc, - bt_stream_class_borrow_packet_context_field_class_const(ir_sc)); - if (ret) { - goto error; - } - - if ((*out_sc)->packet_context_fc) { - /* - * Make sure the structure field class's alignment is - * enough: 8 is what we use for our own special members - * in the packet context. - */ - fs_sink_ctf_field_class_struct_align_at_least( - (void *) (*out_sc)->packet_context_fc, 8); - } - - ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_COMMON_CONTEXT, - &(*out_sc)->event_common_context_fc, - bt_stream_class_borrow_event_common_context_field_class_const( - ir_sc)); - if (ret) { - goto error; - } - - goto end; - -error: - fs_sink_ctf_stream_class_destroy(*out_sc); - *out_sc = NULL; - -end: - ctx_fini(&ctx); - return ret; -} - -BT_HIDDEN -int try_translate_stream_class_trace_ir_to_ctf_ir( - struct fs_sink_ctf_trace_class *tc, - const bt_stream_class *ir_sc, - struct fs_sink_ctf_stream_class **out_sc) -{ - int ret = 0; - uint64_t i; - - BT_ASSERT(tc); - BT_ASSERT(ir_sc); - - for (i = 0; i < tc->stream_classes->len; i++) { - *out_sc = tc->stream_classes->pdata[i]; - - if ((*out_sc)->ir_sc == ir_sc) { - goto end; - } - } - - ret = translate_stream_class(tc, ir_sc, out_sc); - -end: - return ret; -} - -BT_HIDDEN -struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir( - const bt_trace_class *ir_tc) -{ - uint64_t count; - uint64_t i; - struct fs_sink_ctf_trace_class *tc = NULL; - - /* Check that trace class's environment is TSDL-compatible */ - count = bt_trace_class_get_environment_entry_count(ir_tc); - for (i = 0; i < count; i++) { - const char *name; - const bt_value *val; - - bt_trace_class_borrow_environment_entry_by_index_const( - ir_tc, i, &name, &val); - - if (!fs_sink_ctf_ist_valid_identifier(name)) { - BT_LOGE("Unsupported trace class's environment entry name: " - "name=\"%s\"", name); - goto end; - } - - switch (bt_value_get_type(val)) { - case BT_VALUE_TYPE_SIGNED_INTEGER: - case BT_VALUE_TYPE_STRING: - break; - default: - BT_LOGE("Unsupported trace class's environment entry value type: " - "type=%s", - bt_common_value_type_string( - bt_value_get_type(val))); - goto end; - } - } - - tc = fs_sink_ctf_trace_class_create(ir_tc); - BT_ASSERT(tc); - -end: - return tc; -} diff --git a/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h b/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h deleted file mode 100644 index 23cd5d98..00000000 --- a/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H -#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H - -/* - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "fs-sink-ctf-meta.h" - -BT_HIDDEN -int try_translate_event_class_trace_ir_to_ctf_ir( - struct fs_sink_ctf_stream_class *sc, - const bt_event_class *ir_ec, - struct fs_sink_ctf_event_class **out_ec); - -BT_HIDDEN -int try_translate_stream_class_trace_ir_to_ctf_ir( - struct fs_sink_ctf_trace_class *tc, - const bt_stream_class *ir_sc, - struct fs_sink_ctf_stream_class **out_sc); - -BT_HIDDEN -struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir( - const bt_trace_class *ir_tc); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H */ diff --git a/plugins/ctf/fs-src/Makefile.am b/plugins/ctf/fs-src/Makefile.am deleted file mode 100644 index 012f4eb9..00000000 --- a/plugins/ctf/fs-src/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-src.la - -libbabeltrace2_plugin_ctf_fs_src_la_SOURCES = \ - data-stream-file.c \ - data-stream-file.h \ - file.c \ - file.h \ - fs.c \ - fs.h \ - lttng-index.h \ - metadata.c \ - metadata.h \ - query.h \ - query.c \ - logging.h \ - logging.c diff --git a/plugins/ctf/fs-src/data-stream-file.c b/plugins/ctf/fs-src/data-stream-file.c deleted file mode 100644 index 99d3b472..00000000 --- a/plugins/ctf/fs-src/data-stream-file.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - * Copyright 2016-2017 - Philippe Proulx - * Copyright 2016 - Jérémie Galarneau - * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "file.h" -#include "metadata.h" -#include "../common/msg-iter/msg-iter.h" -#include -#include "data-stream-file.h" -#include - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SRC-DS" -#include "logging.h" - -static inline -size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file) -{ - return ds_file->mmap_len - ds_file->request_offset; -} - -static -int ds_file_munmap(struct ctf_fs_ds_file *ds_file) -{ - int ret = 0; - - if (!ds_file || !ds_file->mmap_addr) { - goto end; - } - - if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) { - BT_LOGE_ERRNO("Cannot memory-unmap file", - ": address=%p, size=%zu, file_path=\"%s\", file=%p", - ds_file->mmap_addr, ds_file->mmap_len, - ds_file->file ? ds_file->file->path->str : "NULL", - ds_file->file ? ds_file->file->fp : NULL); - ret = -1; - goto end; - } - - ds_file->mmap_addr = NULL; - -end: - return ret; -} - -static -enum bt_msg_iter_medium_status ds_file_mmap_next( - struct ctf_fs_ds_file *ds_file) -{ - enum bt_msg_iter_medium_status ret = - BT_MSG_ITER_MEDIUM_STATUS_OK; - - /* Unmap old region */ - if (ds_file->mmap_addr) { - if (ds_file_munmap(ds_file)) { - goto error; - } - - /* - * mmap_len is guaranteed to be page-aligned except on the - * last mapping where it may not be possible (since the file's - * size itself may not be a page multiple). - */ - ds_file->mmap_offset += ds_file->mmap_len; - ds_file->request_offset = 0; - } - - ds_file->mmap_len = MIN(ds_file->file->size - ds_file->mmap_offset, - ds_file->mmap_max_len); - if (ds_file->mmap_len == 0) { - ret = BT_MSG_ITER_MEDIUM_STATUS_EOF; - goto end; - } - /* Map new region */ - BT_ASSERT(ds_file->mmap_len); - ds_file->mmap_addr = bt_mmap((void *) 0, ds_file->mmap_len, - PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp), - ds_file->mmap_offset); - if (ds_file->mmap_addr == MAP_FAILED) { - BT_LOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s", - ds_file->mmap_len, ds_file->file->path->str, - ds_file->file->fp, (intmax_t) ds_file->mmap_offset, - strerror(errno)); - goto error; - } - - goto end; -error: - ds_file_munmap(ds_file); - ret = BT_MSG_ITER_MEDIUM_STATUS_ERROR; -end: - return ret; -} - -static -enum bt_msg_iter_medium_status medop_request_bytes( - size_t request_sz, uint8_t **buffer_addr, - size_t *buffer_sz, void *data) -{ - enum bt_msg_iter_medium_status status = - BT_MSG_ITER_MEDIUM_STATUS_OK; - struct ctf_fs_ds_file *ds_file = data; - - if (request_sz == 0) { - goto end; - } - - /* Check if we have at least one memory-mapped byte left */ - if (remaining_mmap_bytes(ds_file) == 0) { - /* Are we at the end of the file? */ - if (ds_file->mmap_offset >= ds_file->file->size) { - BT_LOGD("Reached end of file \"%s\" (%p)", - ds_file->file->path->str, ds_file->file->fp); - status = BT_MSG_ITER_MEDIUM_STATUS_EOF; - goto end; - } - - status = ds_file_mmap_next(ds_file); - switch (status) { - case BT_MSG_ITER_MEDIUM_STATUS_OK: - break; - case BT_MSG_ITER_MEDIUM_STATUS_EOF: - goto end; - default: - BT_LOGE("Cannot memory-map next region of file \"%s\" (%p)", - ds_file->file->path->str, - ds_file->file->fp); - goto error; - } - } - - *buffer_sz = MIN(remaining_mmap_bytes(ds_file), request_sz); - *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset; - ds_file->request_offset += *buffer_sz; - goto end; - -error: - status = BT_MSG_ITER_MEDIUM_STATUS_ERROR; - -end: - return status; -} - -static -bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, - void *data) -{ - struct ctf_fs_ds_file *ds_file = data; - bt_stream_class *ds_file_stream_class; - bt_stream *stream = NULL; - - ds_file_stream_class = bt_stream_borrow_class( - ds_file->stream); - - if (stream_class != ds_file_stream_class) { - /* - * Not supported: two packets described by two different - * stream classes within the same data stream file. - */ - goto end; - } - - stream = ds_file->stream; - -end: - return stream; -} - -static -enum bt_msg_iter_medium_status medop_seek(enum bt_msg_iter_seek_whence whence, - off_t offset, void *data) -{ - enum bt_msg_iter_medium_status ret = - BT_MSG_ITER_MEDIUM_STATUS_OK; - struct ctf_fs_ds_file *ds_file = data; - off_t file_size = ds_file->file->size; - - if (whence != BT_MSG_ITER_SEEK_WHENCE_SET || - offset < 0 || offset > file_size) { - BT_LOGE("Invalid medium seek request: whence=%d, offset=%jd, " - "file-size=%jd", (int) whence, offset, - file_size); - ret = BT_MSG_ITER_MEDIUM_STATUS_INVAL; - goto end; - } - - /* - * Determine whether or not the destination is contained within the - * current mapping. - */ - if (ds_file->mmap_addr && (offset < ds_file->mmap_offset || - offset >= ds_file->mmap_offset + ds_file->mmap_len)) { - int unmap_ret; - off_t offset_in_mapping = offset % bt_common_get_page_size(); - - BT_LOGD("Medium seek request cannot be accomodated by the current " - "file mapping: offset=%jd, mmap-offset=%jd, " - "mmap-len=%zu", offset, ds_file->mmap_offset, - ds_file->mmap_len); - unmap_ret = ds_file_munmap(ds_file); - if (unmap_ret) { - ret = BT_MSG_ITER_MEDIUM_STATUS_ERROR; - goto end; - } - - ds_file->mmap_offset = offset - offset_in_mapping; - ds_file->request_offset = offset_in_mapping; - ret = ds_file_mmap_next(ds_file); - if (ret != BT_MSG_ITER_MEDIUM_STATUS_OK) { - goto end; - } - } else { - ds_file->request_offset = offset - ds_file->mmap_offset; - } - - ds_file->end_reached = (offset == file_size); -end: - return ret; -} - -BT_HIDDEN -struct bt_msg_iter_medium_ops ctf_fs_ds_file_medops = { - .request_bytes = medop_request_bytes, - .borrow_stream = medop_borrow_stream, - .seek = medop_seek, -}; - -static -int convert_cycles_to_ns(struct ctf_clock_class *clock_class, - uint64_t cycles, int64_t *ns) -{ - return bt_util_clock_cycles_to_ns_from_origin(cycles, - clock_class->frequency, clock_class->offset_seconds, - clock_class->offset_cycles, ns); -} - -static -struct ctf_fs_ds_index *build_index_from_idx_file( - struct ctf_fs_ds_file *ds_file) -{ - int ret; - gchar *directory = NULL; - gchar *basename = NULL; - GString *index_basename = NULL; - gchar *index_file_path = NULL; - GMappedFile *mapped_file = NULL; - gsize filesize; - const char *mmap_begin = NULL, *file_pos = NULL; - const struct ctf_packet_index_file_hdr *header = NULL; - struct ctf_fs_ds_index *index = NULL; - struct ctf_fs_ds_index_entry *index_entry = NULL; - uint64_t total_packets_size = 0; - size_t file_index_entry_size; - size_t file_entry_count; - size_t i; - struct ctf_stream_class *sc; - struct bt_msg_iter_packet_properties props; - - BT_LOGD("Building index from .idx file of stream file %s", - ds_file->file->path->str); - ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props); - if (ret) { - BT_LOGD_STR("Cannot read first packet's header and context fields."); - goto error; - } - - sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, - props.stream_class_id); - BT_ASSERT(sc); - if (!sc->default_clock_class) { - BT_LOGD_STR("Cannot find stream class's default clock class."); - goto error; - } - - /* Look for index file in relative path index/name.idx. */ - basename = g_path_get_basename(ds_file->file->path->str); - if (!basename) { - BT_LOGE("Cannot get the basename of datastream file %s", - ds_file->file->path->str); - goto error; - } - - directory = g_path_get_dirname(ds_file->file->path->str); - if (!directory) { - BT_LOGE("Cannot get dirname of datastream file %s", - ds_file->file->path->str); - goto error; - } - - index_basename = g_string_new(basename); - if (!index_basename) { - BT_LOGE_STR("Cannot allocate index file basename string"); - goto error; - } - - g_string_append(index_basename, ".idx"); - index_file_path = g_build_filename(directory, "index", - index_basename->str, NULL); - mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL); - if (!mapped_file) { - BT_LOGD("Cannot create new mapped file %s", - index_file_path); - goto error; - } - - /* - * The g_mapped_file API limits us to 4GB files on 32-bit. - * Traces with such large indexes have never been seen in the wild, - * but this would need to be adjusted to support them. - */ - filesize = g_mapped_file_get_length(mapped_file); - if (filesize < sizeof(*header)) { - BT_LOGW("Invalid LTTng trace index file: " - "file size (%zu bytes) < header size (%zu bytes)", - filesize, sizeof(*header)); - goto error; - } - - mmap_begin = g_mapped_file_get_contents(mapped_file); - header = (struct ctf_packet_index_file_hdr *) mmap_begin; - - file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header); - if (be32toh(header->magic) != CTF_INDEX_MAGIC) { - BT_LOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed"); - goto error; - } - - file_index_entry_size = be32toh(header->packet_index_len); - file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size; - if ((filesize - sizeof(*header)) % file_index_entry_size) { - BT_LOGW("Invalid LTTng trace index: the index's size after the header " - "(%zu bytes) is not a multiple of the index entry size " - "(%zu bytes)", (filesize - sizeof(*header)), - sizeof(*header)); - goto error; - } - - index = ctf_fs_ds_index_create(); - if (!index) { - goto error; - } - - for (i = 0; i < file_entry_count; i++) { - struct ctf_packet_index *file_index = - (struct ctf_packet_index *) file_pos; - uint64_t packet_size = be64toh(file_index->packet_size); - - if (packet_size % CHAR_BIT) { - BT_LOGW("Invalid packet size encountered in LTTng trace index file"); - goto error; - } - - index_entry = g_new0(struct ctf_fs_ds_index_entry, 1); - if (!index_entry) { - goto error; - } - - /* Convert size in bits to bytes. */ - packet_size /= CHAR_BIT; - index_entry->packet_size = packet_size; - - index_entry->offset = be64toh(file_index->offset); - if (i != 0 && index_entry->offset < (index_entry - 1)->offset) { - BT_LOGW("Invalid, non-monotonic, packet offset encountered in LTTng trace index file: " - "previous offset=%" PRIu64 ", current offset=%" PRIu64, - (index_entry - 1)->offset, index_entry->offset); - goto error; - } - - index_entry->timestamp_begin = be64toh(file_index->timestamp_begin); - index_entry->timestamp_end = be64toh(file_index->timestamp_end); - if (index_entry->timestamp_end < index_entry->timestamp_begin) { - BT_LOGW("Invalid packet time bounds encountered in LTTng trace index file (begin > end): " - "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64, - index_entry->timestamp_begin, - index_entry->timestamp_end); - goto error; - } - - /* Convert the packet's bound to nanoseconds since Epoch. */ - ret = convert_cycles_to_ns(sc->default_clock_class, - index_entry->timestamp_begin, - &index_entry->timestamp_begin_ns); - if (ret) { - BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch during index parsing"); - goto error; - } - ret = convert_cycles_to_ns(sc->default_clock_class, - index_entry->timestamp_end, - &index_entry->timestamp_end_ns); - if (ret) { - BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing"); - goto error; - } - - total_packets_size += packet_size; - file_pos += file_index_entry_size; - - g_ptr_array_add(index->entries, index_entry); - } - - /* Validate that the index addresses the complete stream. */ - if (ds_file->file->size != total_packets_size) { - BT_LOGW("Invalid LTTng trace index file; indexed size != stream file size: " - "file-size=%" PRIu64 ", total-packets-size=%" PRIu64, - ds_file->file->size, total_packets_size); - goto error; - } -end: - g_free(directory); - g_free(basename); - g_free(index_file_path); - if (index_basename) { - g_string_free(index_basename, TRUE); - } - if (mapped_file) { - g_mapped_file_unref(mapped_file); - } - return index; -error: - ctf_fs_ds_index_destroy(index); - g_free(index_entry); - index = NULL; - goto end; -} - -static -int init_index_entry(struct ctf_fs_ds_index_entry *entry, - struct ctf_fs_ds_file *ds_file, - struct bt_msg_iter_packet_properties *props, - off_t packet_size, off_t packet_offset) -{ - int ret = 0; - struct ctf_stream_class *sc; - - sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, - props->stream_class_id); - BT_ASSERT(sc); - BT_ASSERT(packet_offset >= 0); - entry->offset = packet_offset; - BT_ASSERT(packet_size >= 0); - entry->packet_size = packet_size; - - if (props->snapshots.beginning_clock != UINT64_C(-1)) { - /* Convert the packet's bound to nanoseconds since Epoch. */ - ret = convert_cycles_to_ns(sc->default_clock_class, - props->snapshots.beginning_clock, - &entry->timestamp_begin_ns); - if (ret) { - BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); - goto end; - } - } else { - entry->timestamp_begin_ns = UINT64_C(-1); - } - - if (props->snapshots.end_clock != UINT64_C(-1)) { - ret = convert_cycles_to_ns(sc->default_clock_class, - props->snapshots.end_clock, - &entry->timestamp_end_ns); - if (ret) { - BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); - goto end; - } - } else { - entry->timestamp_end_ns = UINT64_C(-1); - } - -end: - return ret; -} - -static -struct ctf_fs_ds_index *build_index_from_stream_file( - struct ctf_fs_ds_file *ds_file) -{ - int ret; - struct ctf_fs_ds_index *index = NULL; - enum bt_msg_iter_status iter_status = BT_MSG_ITER_STATUS_OK; - off_t current_packet_offset_bytes = 0; - - BT_LOGD("Indexing stream file %s", ds_file->file->path->str); - - index = ctf_fs_ds_index_create(); - if (!index) { - goto error; - } - - do { - off_t current_packet_size_bytes; - struct ctf_fs_ds_index_entry *index_entry; - struct bt_msg_iter_packet_properties props; - - if (current_packet_offset_bytes < 0) { - BT_LOGE_STR("Cannot get the current packet's offset."); - goto error; - } else if (current_packet_offset_bytes > ds_file->file->size) { - BT_LOGE_STR("Unexpected current packet's offset (larger than file)."); - goto error; - } else if (current_packet_offset_bytes == ds_file->file->size) { - /* No more data */ - break; - } - - iter_status = bt_msg_iter_seek(ds_file->msg_iter, - current_packet_offset_bytes); - if (iter_status != BT_MSG_ITER_STATUS_OK) { - goto error; - } - - iter_status = bt_msg_iter_get_packet_properties( - ds_file->msg_iter, &props); - if (iter_status != BT_MSG_ITER_STATUS_OK) { - goto error; - } - - if (props.exp_packet_total_size >= 0) { - current_packet_size_bytes = - (uint64_t) props.exp_packet_total_size / 8; - } else { - current_packet_size_bytes = ds_file->file->size; - } - - if (current_packet_offset_bytes + current_packet_size_bytes > - ds_file->file->size) { - BT_LOGW("Invalid packet size reported in file: stream=\"%s\", " - "packet-offset=%jd, packet-size-bytes=%jd, " - "file-size=%jd", - ds_file->file->path->str, - current_packet_offset_bytes, - current_packet_size_bytes, - ds_file->file->size); - goto error; - } - - index_entry = g_new0(struct ctf_fs_ds_index_entry, 1); - if (!index_entry) { - BT_LOGE_STR("Failed to allocate a new index entry."); - goto error; - } - - ret = init_index_entry(index_entry, ds_file, &props, - current_packet_size_bytes, current_packet_offset_bytes); - if (ret) { - g_free(index_entry); - goto error; - } - - g_ptr_array_add(index->entries, index_entry); - - current_packet_offset_bytes += current_packet_size_bytes; - BT_LOGD("Seeking to next packet: current-packet-offset=%jd, " - "next-packet-offset=%jd", - current_packet_offset_bytes - current_packet_size_bytes, - current_packet_offset_bytes); - - } while (iter_status == BT_MSG_ITER_STATUS_OK); - - if (iter_status != BT_MSG_ITER_STATUS_OK) { - goto error; - } - -end: - return index; - -error: - ctf_fs_ds_index_destroy(index); - index = NULL; - goto end; -} - -BT_HIDDEN -struct ctf_fs_ds_file *ctf_fs_ds_file_create( - struct ctf_fs_trace *ctf_fs_trace, - bt_self_message_iterator *pc_msg_iter, - struct bt_msg_iter *msg_iter, - bt_stream *stream, const char *path) -{ - int ret; - const size_t page_size = bt_common_get_page_size(); - struct ctf_fs_ds_file *ds_file = g_new0(struct ctf_fs_ds_file, 1); - - if (!ds_file) { - goto error; - } - - ds_file->pc_msg_iter = pc_msg_iter; - ds_file->file = ctf_fs_file_create(); - if (!ds_file->file) { - goto error; - } - - ds_file->stream = stream; - bt_stream_get_ref(ds_file->stream); - ds_file->metadata = ctf_fs_trace->metadata; - g_string_assign(ds_file->file->path, path); - ret = ctf_fs_file_open(ds_file->file, "rb"); - if (ret) { - goto error; - } - - ds_file->msg_iter = msg_iter; - bt_msg_iter_set_medops_data(ds_file->msg_iter, ds_file); - if (!ds_file->msg_iter) { - goto error; - } - - ds_file->mmap_max_len = page_size * 2048; - - goto end; - -error: - /* Do not touch "borrowed" file. */ - ctf_fs_ds_file_destroy(ds_file); - ds_file = NULL; - -end: - return ds_file; -} - -BT_HIDDEN -struct ctf_fs_ds_index *ctf_fs_ds_file_build_index( - struct ctf_fs_ds_file *ds_file) -{ - struct ctf_fs_ds_index *index; - - index = build_index_from_idx_file(ds_file); - if (index) { - goto end; - } - - BT_LOGD("Failed to build index from .index file; " - "falling back to stream indexing."); - index = build_index_from_stream_file(ds_file); -end: - return index; -} - -BT_HIDDEN -struct ctf_fs_ds_index *ctf_fs_ds_index_create() -{ - struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1); - - if (!index) { - BT_LOGE_STR("Failed to allocate index"); - goto error; - } - - index->entries = g_ptr_array_new_with_free_func((GDestroyNotify) g_free); - if (!index->entries) { - BT_LOGE("Failed to allocate index entries."); - goto error; - } - - goto end; - -error: - ctf_fs_ds_index_destroy(index); - index = NULL; -end: - return index; -} - -BT_HIDDEN -void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file) -{ - if (!ds_file) { - return; - } - - bt_stream_put_ref(ds_file->stream); - (void) ds_file_munmap(ds_file); - - if (ds_file->file) { - ctf_fs_file_destroy(ds_file->file); - } - - g_free(ds_file); -} - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_ds_file_next( - struct ctf_fs_ds_file *ds_file, - bt_message **msg) -{ - enum bt_msg_iter_status msg_iter_status; - bt_self_message_iterator_status status; - - msg_iter_status = bt_msg_iter_get_next_message( - ds_file->msg_iter, ds_file->pc_msg_iter, msg); - - switch (msg_iter_status) { - case BT_MSG_ITER_STATUS_EOF: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - break; - case BT_MSG_ITER_STATUS_OK: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - break; - case BT_MSG_ITER_STATUS_AGAIN: - /* - * Should not make it this far as this is - * medium-specific; there is nothing for the user to do - * and it should have been handled upstream. - */ - abort(); - case BT_MSG_ITER_STATUS_INVAL: - case BT_MSG_ITER_STATUS_ERROR: - default: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - break; - } - return status; -} - -BT_HIDDEN -void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index) -{ - if (!index) { - return; - } - - if (index->entries) { - g_ptr_array_free(index->entries, TRUE); - } - g_free(index); -} diff --git a/plugins/ctf/fs-src/data-stream-file.h b/plugins/ctf/fs-src/data-stream-file.h deleted file mode 100644 index feeef675..00000000 --- a/plugins/ctf/fs-src/data-stream-file.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef CTF_FS_DS_FILE_H -#define CTF_FS_DS_FILE_H - -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -#include "../common/msg-iter/msg-iter.h" -#include "lttng-index.h" - -struct ctf_fs_component; -struct ctf_fs_file; -struct ctf_fs_trace; -struct ctf_fs_ds_file; - -struct ctf_fs_ds_file_info { - /* Owned by this. */ - GString *path; - - /* Guaranteed to be set, as opposed to the index. */ - int64_t begin_ns; -}; - -struct ctf_fs_metadata; - -struct ctf_fs_ds_file { - /* Weak */ - struct ctf_fs_metadata *metadata; - - /* Weak */ - bt_self_message_iterator *pc_msg_iter; - - /* Owned by this */ - struct ctf_fs_file *file; - - /* Owned by this */ - bt_stream *stream; - - /* Weak */ - struct bt_msg_iter *msg_iter; - - void *mmap_addr; - - /* - * Max length of chunk to mmap() when updating the current mapping. - * This value must be page-aligned. - */ - size_t mmap_max_len; - - /* Length of the current mapping. Never exceeds the file's length. */ - size_t mmap_len; - - /* Offset in the file where the current mapping starts. */ - off_t mmap_offset; - - /* - * Offset, in the current mapping, of the address to return on the next - * request. - */ - off_t request_offset; - - bool end_reached; -}; - -BT_HIDDEN -struct ctf_fs_ds_file *ctf_fs_ds_file_create( - struct ctf_fs_trace *ctf_fs_trace, - bt_self_message_iterator *pc_msg_iter, - struct bt_msg_iter *msg_iter, - bt_stream *stream, const char *path); - -BT_HIDDEN -void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *stream); - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_ds_file_next( - struct ctf_fs_ds_file *ds_file, - bt_message **msg); - -BT_HIDDEN -struct ctf_fs_ds_index *ctf_fs_ds_file_build_index( - struct ctf_fs_ds_file *ds_file); - -BT_HIDDEN -struct ctf_fs_ds_index *ctf_fs_ds_index_create(); - -BT_HIDDEN -void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index); - -extern struct bt_msg_iter_medium_ops ctf_fs_ds_file_medops; - -#endif /* CTF_FS_DS_FILE_H */ diff --git a/plugins/ctf/fs-src/file.c b/plugins/ctf/fs-src/file.c deleted file mode 100644 index 585faef8..00000000 --- a/plugins/ctf/fs-src/file.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include "file.h" - -#define BT_LOG_TAG "PLUGIN-CTF-FS-FILE-SRC" -#include "logging.h" - -BT_HIDDEN -void ctf_fs_file_destroy(struct ctf_fs_file *file) -{ - if (!file) { - return; - } - - if (file->fp) { - BT_LOGD("Closing file \"%s\" (%p)", - file->path ? file->path->str : NULL, file->fp); - - if (fclose(file->fp)) { - BT_LOGE("Cannot close file \"%s\": %s", - file->path ? file->path->str : "NULL", - strerror(errno)); - } - } - - if (file->path) { - g_string_free(file->path, TRUE); - } - - g_free(file); -} - -BT_HIDDEN -struct ctf_fs_file *ctf_fs_file_create(void) -{ - struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1); - - if (!file) { - goto error; - } - - file->path = g_string_new(NULL); - if (!file->path) { - goto error; - } - - goto end; - -error: - ctf_fs_file_destroy(file); - file = NULL; - -end: - return file; -} - -BT_HIDDEN -int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode) -{ - int ret = 0; - struct stat stat; - - BT_LOGD("Opening file \"%s\" with mode \"%s\"", file->path->str, mode); - file->fp = fopen(file->path->str, mode); - if (!file->fp) { - BT_LOGE("Cannot open file \"%s\" with mode \"%s\": %s", - file->path->str, mode, strerror(errno)); - goto error; - } - - BT_LOGD("Opened file: %p", file->fp); - - if (fstat(fileno(file->fp), &stat)) { - BT_LOGE("Cannot get file information: %s", strerror(errno)); - goto error; - } - - file->size = stat.st_size; - BT_LOGD("File is %jd bytes", (intmax_t) file->size); - goto end; - -error: - ret = -1; - - if (file->fp) { - if (fclose(file->fp)) { - BT_LOGE("Cannot close file \"%s\": %s", file->path->str, - strerror(errno)); - } - } - -end: - return ret; -} diff --git a/plugins/ctf/fs-src/file.h b/plugins/ctf/fs-src/file.h deleted file mode 100644 index 4e578af3..00000000 --- a/plugins/ctf/fs-src/file.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CTF_FS_FILE_H -#define CTF_FS_FILE_H - -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "fs.h" - -BT_HIDDEN -void ctf_fs_file_destroy(struct ctf_fs_file *file); - -BT_HIDDEN -struct ctf_fs_file *ctf_fs_file_create(void); - -BT_HIDDEN -int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode); - -#endif /* CTF_FS_FILE_H */ diff --git a/plugins/ctf/fs-src/fs.c b/plugins/ctf/fs-src/fs.c deleted file mode 100644 index 3cee435a..00000000 --- a/plugins/ctf/fs-src/fs.c +++ /dev/null @@ -1,1964 +0,0 @@ -/* - * fs.c - * - * Babeltrace CTF file system Reader Component - * - * Copyright 2015-2017 Philippe Proulx - * Copyright 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "fs.h" -#include "metadata.h" -#include "data-stream-file.h" -#include "file.h" -#include "../common/metadata/decoder.h" -#include "../common/msg-iter/msg-iter.h" -#include "../common/utils/utils.h" -#include "query.h" - -#define BT_LOG_TAG "PLUGIN-CTF-FS-SRC" -#include "logging.h" - -static -int msg_iter_data_set_current_ds_file(struct ctf_fs_msg_iter_data *msg_iter_data) -{ - struct ctf_fs_ds_file_info *ds_file_info; - int ret = 0; - - BT_ASSERT(msg_iter_data->ds_file_info_index < - msg_iter_data->ds_file_group->ds_file_infos->len); - ds_file_info = g_ptr_array_index( - msg_iter_data->ds_file_group->ds_file_infos, - msg_iter_data->ds_file_info_index); - - ctf_fs_ds_file_destroy(msg_iter_data->ds_file); - msg_iter_data->ds_file = ctf_fs_ds_file_create( - msg_iter_data->ds_file_group->ctf_fs_trace, - msg_iter_data->pc_msg_iter, - msg_iter_data->msg_iter, - msg_iter_data->ds_file_group->stream, - ds_file_info->path->str); - if (!msg_iter_data->ds_file) { - ret = -1; - } - - return ret; -} - -static -void ctf_fs_msg_iter_data_destroy( - struct ctf_fs_msg_iter_data *msg_iter_data) -{ - if (!msg_iter_data) { - return; - } - - ctf_fs_ds_file_destroy(msg_iter_data->ds_file); - - if (msg_iter_data->msg_iter) { - bt_msg_iter_destroy(msg_iter_data->msg_iter); - } - - g_free(msg_iter_data); -} - -static -void set_msg_iter_emits_stream_beginning_end_messages( - struct ctf_fs_msg_iter_data *msg_iter_data) -{ - bt_msg_iter_set_emit_stream_beginning_message( - msg_iter_data->ds_file->msg_iter, - msg_iter_data->ds_file_info_index == 0); - bt_msg_iter_set_emit_stream_end_message( - msg_iter_data->ds_file->msg_iter, - msg_iter_data->ds_file_info_index == - msg_iter_data->ds_file_group->ds_file_infos->len - 1); -} - -static -bt_self_message_iterator_status ctf_fs_iterator_next_one( - struct ctf_fs_msg_iter_data *msg_iter_data, - const bt_message **out_msg) -{ - bt_self_message_iterator_status status; - - BT_ASSERT(msg_iter_data->ds_file); - - while (true) { - bt_message *msg; - - status = ctf_fs_ds_file_next(msg_iter_data->ds_file, &msg); - switch (status) { - case BT_SELF_MESSAGE_ITERATOR_STATUS_OK: - *out_msg = msg; - msg = NULL; - goto end; - case BT_SELF_MESSAGE_ITERATOR_STATUS_END: - { - int ret; - - if (msg_iter_data->ds_file_info_index == - msg_iter_data->ds_file_group->ds_file_infos->len - 1) { - /* End of all group's stream files */ - goto end; - } - - msg_iter_data->ds_file_info_index++; - bt_msg_iter_reset_for_next_stream_file( - msg_iter_data->msg_iter); - set_msg_iter_emits_stream_beginning_end_messages( - msg_iter_data); - - /* - * Open and start reading the next stream file - * within our stream file group. - */ - ret = msg_iter_data_set_current_ds_file(msg_iter_data); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - /* Continue the loop to get the next message */ - break; - } - default: - goto end; - } - } - -end: - return status; -} - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_next( - bt_self_message_iterator *iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - struct ctf_fs_msg_iter_data *msg_iter_data = - bt_self_message_iterator_get_data(iterator); - uint64_t i = 0; - - while (i < capacity && status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]); - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - i++; - } - } - - if (i > 0) { - /* - * Even if ctf_fs_iterator_next_one() returned something - * else than BT_SELF_MESSAGE_ITERATOR_STATUS_OK, we - * accumulated message objects in the output - * message array, so we need to return - * BT_SELF_MESSAGE_ITERATOR_STATUS_OK so that they are - * transfered to downstream. This other status occurs - * again the next time muxer_msg_iter_do_next() is - * called, possibly without any accumulated - * message, in which case we'll return it. - */ - *count = i; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - } - - return status; -} - -static -int ctf_fs_iterator_reset(struct ctf_fs_msg_iter_data *msg_iter_data) -{ - int ret; - - msg_iter_data->ds_file_info_index = 0; - ret = msg_iter_data_set_current_ds_file(msg_iter_data); - if (ret) { - goto end; - } - - bt_msg_iter_reset(msg_iter_data->msg_iter); - set_msg_iter_emits_stream_beginning_end_messages(msg_iter_data); - -end: - return ret; -} - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_seek_beginning( - bt_self_message_iterator *it) -{ - struct ctf_fs_msg_iter_data *msg_iter_data = - bt_self_message_iterator_get_data(it); - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(msg_iter_data); - if (ctf_fs_iterator_reset(msg_iter_data)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } - - return status; -} - -BT_HIDDEN -void ctf_fs_iterator_finalize(bt_self_message_iterator *it) -{ - ctf_fs_msg_iter_data_destroy( - bt_self_message_iterator_get_data(it)); -} - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_source *self_comp, - bt_self_component_port_output *self_port) -{ - struct ctf_fs_port_data *port_data; - struct ctf_fs_msg_iter_data *msg_iter_data = NULL; - bt_self_message_iterator_status ret = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - port_data = bt_self_component_port_get_data( - bt_self_component_port_output_as_self_component_port( - self_port)); - BT_ASSERT(port_data); - msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1); - if (!msg_iter_data) { - ret = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - msg_iter_data->pc_msg_iter = self_msg_iter; - msg_iter_data->msg_iter = bt_msg_iter_create( - port_data->ds_file_group->ctf_fs_trace->metadata->tc, - bt_common_get_page_size() * 8, - ctf_fs_ds_file_medops, NULL); - if (!msg_iter_data->msg_iter) { - BT_LOGE_STR("Cannot create a CTF message iterator."); - ret = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - msg_iter_data->ds_file_group = port_data->ds_file_group; - if (ctf_fs_iterator_reset(msg_iter_data)) { - ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto error; - } - - bt_self_message_iterator_set_data(self_msg_iter, - msg_iter_data); - if (ret != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto error; - } - - msg_iter_data = NULL; - goto end; - -error: - bt_self_message_iterator_set_data(self_msg_iter, NULL); - -end: - ctf_fs_msg_iter_data_destroy(msg_iter_data); - return ret; -} - -BT_HIDDEN -void ctf_fs_destroy(struct ctf_fs_component *ctf_fs) -{ - if (!ctf_fs) { - return; - } - - if (ctf_fs->traces) { - g_ptr_array_free(ctf_fs->traces, TRUE); - } - - if (ctf_fs->port_data) { - g_ptr_array_free(ctf_fs->port_data, TRUE); - } - - g_free(ctf_fs); -} - -static -void port_data_destroy(struct ctf_fs_port_data *port_data) -{ - if (!port_data) { - return; - } - - g_free(port_data); -} - -static -void port_data_destroy_notifier(void *data) { - port_data_destroy(data); -} - -static -void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace) -{ - if (!ctf_fs_trace) { - return; - } - - if (ctf_fs_trace->ds_file_groups) { - g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE); - } - - BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace); - - if (ctf_fs_trace->path) { - g_string_free(ctf_fs_trace->path, TRUE); - } - - if (ctf_fs_trace->name) { - g_string_free(ctf_fs_trace->name, TRUE); - } - - if (ctf_fs_trace->metadata) { - ctf_fs_metadata_fini(ctf_fs_trace->metadata); - g_free(ctf_fs_trace->metadata); - } - - g_free(ctf_fs_trace); -} - -static -void ctf_fs_trace_destroy_notifier(void *data) -{ - struct ctf_fs_trace *trace = data; - ctf_fs_trace_destroy(trace); -} - -struct ctf_fs_component *ctf_fs_component_create(void) -{ - struct ctf_fs_component *ctf_fs; - - ctf_fs = g_new0(struct ctf_fs_component, 1); - if (!ctf_fs) { - goto error; - } - - ctf_fs->port_data = - g_ptr_array_new_with_free_func(port_data_destroy_notifier); - if (!ctf_fs->port_data) { - goto error; - } - - ctf_fs->traces = - g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier); - if (!ctf_fs->traces) { - goto error; - } - - goto end; - -error: - if (ctf_fs) { - ctf_fs_destroy(ctf_fs); - } - -end: - return ctf_fs; -} - -void ctf_fs_finalize(bt_self_component_source *component) -{ - ctf_fs_destroy(bt_self_component_get_data( - bt_self_component_source_as_self_component(component))); -} - -gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group) -{ - GString *name = g_string_new(NULL); - - /* - * The unique port name is generated by concatenating unique identifiers - * for: - * - * - the trace - * - the stream class - * - the stream - */ - - /* For the trace, use the uuid if present, else the path. */ - if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) { - char uuid_str[BABELTRACE_UUID_STR_LEN]; - - bt_uuid_unparse(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str); - g_string_assign(name, uuid_str); - } else { - g_string_assign(name, ds_file_group->ctf_fs_trace->path->str); - } - - /* - * For the stream class, use the id if present. We can omit this field - * otherwise, as there will only be a single stream class. - */ - if (ds_file_group->sc->id != UINT64_C(-1)) { - g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id); - } - - /* For the stream, use the id if present, else, use the path. */ - if (ds_file_group->stream_id != UINT64_C(-1)) { - g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id); - } else { - BT_ASSERT(ds_file_group->ds_file_infos->len == 1); - struct ctf_fs_ds_file_info *ds_file_info = - g_ptr_array_index(ds_file_group->ds_file_infos, 0); - g_string_append_printf(name, " | %s", ds_file_info->path->str); - } - - return g_string_free(name, FALSE); -} - -static -int create_one_port_for_trace(struct ctf_fs_component *ctf_fs, - struct ctf_fs_trace *ctf_fs_trace, - struct ctf_fs_ds_file_group *ds_file_group) -{ - int ret = 0; - struct ctf_fs_port_data *port_data = NULL; - gchar *port_name; - - port_name = ctf_fs_make_port_name(ds_file_group); - if (!port_name) { - goto error; - } - - BT_LOGD("Creating one port named `%s`", port_name); - - /* Create output port for this file */ - port_data = g_new0(struct ctf_fs_port_data, 1); - if (!port_data) { - goto error; - } - - port_data->ctf_fs = ctf_fs; - port_data->ds_file_group = ds_file_group; - ret = bt_self_component_source_add_output_port( - ctf_fs->self_comp, port_name, port_data, NULL); - if (ret) { - goto error; - } - - g_ptr_array_add(ctf_fs->port_data, port_data); - port_data = NULL; - goto end; - -error: - ret = -1; - -end: - if (port_name) { - g_free(port_name); - } - - port_data_destroy(port_data); - return ret; -} - -static -int create_ports_for_trace(struct ctf_fs_component *ctf_fs, - struct ctf_fs_trace *ctf_fs_trace) -{ - int ret = 0; - size_t i; - - /* Create one output port for each stream file group */ - for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { - struct ctf_fs_ds_file_group *ds_file_group = - g_ptr_array_index(ctf_fs_trace->ds_file_groups, i); - - ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace, - ds_file_group); - if (ret) { - BT_LOGE("Cannot create output port."); - goto end; - } - } - -end: - return ret; -} - -static -void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info) -{ - if (!ds_file_info) { - return; - } - - if (ds_file_info->path) { - g_string_free(ds_file_info->path, TRUE); - } - - g_free(ds_file_info); -} - -static -struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path, - int64_t begin_ns) -{ - struct ctf_fs_ds_file_info *ds_file_info; - - ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1); - if (!ds_file_info) { - goto end; - } - - ds_file_info->path = g_string_new(path); - if (!ds_file_info->path) { - ctf_fs_ds_file_info_destroy(ds_file_info); - ds_file_info = NULL; - goto end; - } - - ds_file_info->begin_ns = begin_ns; - -end: - return ds_file_info; -} - -static -void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group) -{ - if (!ds_file_group) { - return; - } - - if (ds_file_group->ds_file_infos) { - g_ptr_array_free(ds_file_group->ds_file_infos, TRUE); - } - - if (ds_file_group->index) { - if (ds_file_group->index->entries) { - g_ptr_array_free(ds_file_group->index->entries, TRUE); - } - g_free(ds_file_group->index); - } - - bt_stream_put_ref(ds_file_group->stream); - g_free(ds_file_group); -} - -static -struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create( - struct ctf_fs_trace *ctf_fs_trace, - struct ctf_stream_class *sc, - uint64_t stream_instance_id, - struct ctf_fs_ds_index *index) -{ - struct ctf_fs_ds_file_group *ds_file_group; - - ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1); - if (!ds_file_group) { - goto error; - } - - ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func( - (GDestroyNotify) ctf_fs_ds_file_info_destroy); - if (!ds_file_group->ds_file_infos) { - goto error; - } - - ds_file_group->index = index; - - ds_file_group->stream_id = stream_instance_id; - BT_ASSERT(sc); - ds_file_group->sc = sc; - ds_file_group->ctf_fs_trace = ctf_fs_trace; - goto end; - -error: - ctf_fs_ds_file_group_destroy(ds_file_group); - ctf_fs_ds_index_destroy(index); - ds_file_group = NULL; - -end: - return ds_file_group; -} - -/* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */ -static -void array_insert(GPtrArray *array, gpointer element, size_t pos) -{ - size_t original_array_len = array->len; - - /* Allocate an unused element at the end of the array. */ - g_ptr_array_add(array, NULL); - - /* If we are not inserting at the end, move the elements by one. */ - if (pos < original_array_len) { - memmove(&(array->pdata[pos + 1]), - &(array->pdata[pos]), - (original_array_len - pos) * sizeof(gpointer)); - } - - /* Insert the value. */ - array->pdata[pos] = element; -} - -/* - * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right - * place to keep it sorted. - */ - -static -void ds_file_group_insert_ds_file_info_sorted( - struct ctf_fs_ds_file_group *ds_file_group, - struct ctf_fs_ds_file_info *ds_file_info) -{ - guint i; - - /* Find the spot where to insert this ds_file_info. */ - for (i = 0; i < ds_file_group->ds_file_infos->len; i++) { - struct ctf_fs_ds_file_info *other_ds_file_info = - g_ptr_array_index(ds_file_group->ds_file_infos, i); - - if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) { - break; - } - } - - array_insert(ds_file_group->ds_file_infos, ds_file_info, i); -} - -static -void ds_file_group_insert_ds_index_entry_sorted( - struct ctf_fs_ds_file_group *ds_file_group, - struct ctf_fs_ds_index_entry *entry) -{ - guint i; - - /* Find the spot where to insert this index entry. */ - for (i = 0; i < ds_file_group->index->entries->len; i++) { - struct ctf_fs_ds_index_entry *other_entry = g_ptr_array_index( - ds_file_group->index->entries, i); - - if (entry->timestamp_begin_ns < other_entry->timestamp_begin_ns) { - break; - } - } - - array_insert(ds_file_group->index->entries, entry, i); -} - -/* - * Create a new ds_file_info using the provided path, begin_ns and index, then - * add it to ds_file_group's list of ds_file_infos. - */ - -static -int ctf_fs_ds_file_group_add_ds_file_info( - struct ctf_fs_ds_file_group *ds_file_group, - const char *path, int64_t begin_ns) -{ - struct ctf_fs_ds_file_info *ds_file_info; - int ret = 0; - - ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns); - if (!ds_file_info) { - goto error; - } - - ds_file_group_insert_ds_file_info_sorted(ds_file_group, ds_file_info); - - ds_file_info = NULL; - goto end; - -error: - ctf_fs_ds_file_info_destroy(ds_file_info); - ret = -1; -end: - return ret; -} - -static -int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, - const char *path) -{ - int64_t stream_instance_id = -1; - int64_t begin_ns = -1; - struct ctf_fs_ds_file_group *ds_file_group = NULL; - bool add_group = false; - int ret; - size_t i; - struct ctf_fs_ds_file *ds_file = NULL; - struct ctf_fs_ds_index *index = NULL; - struct bt_msg_iter *msg_iter = NULL; - struct ctf_stream_class *sc = NULL; - struct bt_msg_iter_packet_properties props; - - msg_iter = bt_msg_iter_create(ctf_fs_trace->metadata->tc, - bt_common_get_page_size() * 8, ctf_fs_ds_file_medops, NULL); - if (!msg_iter) { - BT_LOGE_STR("Cannot create a CTF message iterator."); - goto error; - } - - ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, msg_iter, - NULL, path); - if (!ds_file) { - goto error; - } - - ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props); - if (ret) { - BT_LOGE("Cannot get stream file's first packet's header and context fields (`%s`).", - path); - goto error; - } - - sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, - props.stream_class_id); - BT_ASSERT(sc); - stream_instance_id = props.data_stream_id; - - if (props.snapshots.beginning_clock != UINT64_C(-1)) { - BT_ASSERT(sc->default_clock_class); - ret = bt_util_clock_cycles_to_ns_from_origin( - props.snapshots.beginning_clock, - sc->default_clock_class->frequency, - sc->default_clock_class->offset_seconds, - sc->default_clock_class->offset_cycles, &begin_ns); - if (ret) { - BT_LOGE("Cannot convert clock cycles to nanoseconds from origin (`%s`).", - path); - goto error; - } - } - - index = ctf_fs_ds_file_build_index(ds_file); - if (!index) { - BT_LOGW("Failed to index CTF stream file \'%s\'", - ds_file->file->path->str); - } - - if (begin_ns == -1) { - /* - * No beggining timestamp to sort the stream files - * within a stream file group, so consider that this - * file must be the only one within its group. - */ - stream_instance_id = -1; - } - - if (stream_instance_id == -1) { - /* - * No stream instance ID or no beginning timestamp: - * create a unique stream file group for this stream - * file because, even if there's a stream instance ID, - * there's no timestamp to order the file within its - * group. - */ - ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, - sc, UINT64_C(-1), index); - /* Ownership of index is transferred. */ - index = NULL; - - if (!ds_file_group) { - goto error; - } - - ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, - path, begin_ns); - if (ret) { - goto error; - } - - add_group = true; - goto end; - } - - BT_ASSERT(stream_instance_id != -1); - BT_ASSERT(begin_ns != -1); - - /* Find an existing stream file group with this ID */ - for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { - ds_file_group = g_ptr_array_index( - ctf_fs_trace->ds_file_groups, i); - - if (ds_file_group->sc == sc && - ds_file_group->stream_id == - stream_instance_id) { - break; - } - - ds_file_group = NULL; - } - - if (!ds_file_group) { - ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, - sc, stream_instance_id, index); - /* Ownership of index is transferred. */ - index = NULL; - if (!ds_file_group) { - goto error; - } - - add_group = true; - } - - ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, path, - begin_ns); - if (ret) { - goto error; - } - - goto end; - -error: - ctf_fs_ds_file_group_destroy(ds_file_group); - ds_file_group = NULL; - ret = -1; - -end: - if (add_group && ds_file_group) { - g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group); - } - - ctf_fs_ds_file_destroy(ds_file); - - if (msg_iter) { - bt_msg_iter_destroy(msg_iter); - } - - ctf_fs_ds_index_destroy(index); - return ret; -} - -static -int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace) -{ - int ret = 0; - const char *basename; - GError *error = NULL; - GDir *dir = NULL; - - /* Check each file in the path directory, except specific ones */ - dir = g_dir_open(ctf_fs_trace->path->str, 0, &error); - if (!dir) { - BT_LOGE("Cannot open directory `%s`: %s (code %d)", - ctf_fs_trace->path->str, error->message, - error->code); - goto error; - } - - while ((basename = g_dir_read_name(dir))) { - struct ctf_fs_file *file; - - if (!strcmp(basename, CTF_FS_METADATA_FILENAME)) { - /* Ignore the metadata stream. */ - BT_LOGD("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`", - ctf_fs_trace->path->str, basename); - continue; - } - - if (basename[0] == '.') { - BT_LOGD("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`", - ctf_fs_trace->path->str, basename); - continue; - } - - /* Create the file. */ - file = ctf_fs_file_create(); - if (!file) { - BT_LOGE("Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`", - ctf_fs_trace->path->str, basename); - goto error; - } - - /* Create full path string. */ - g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s", - ctf_fs_trace->path->str, basename); - if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) { - BT_LOGD("Ignoring non-regular file `%s`", - file->path->str); - ctf_fs_file_destroy(file); - file = NULL; - continue; - } - - ret = ctf_fs_file_open(file, "rb"); - if (ret) { - BT_LOGE("Cannot open stream file `%s`", file->path->str); - goto error; - } - - if (file->size == 0) { - /* Skip empty stream. */ - BT_LOGD("Ignoring empty file `%s`", file->path->str); - ctf_fs_file_destroy(file); - continue; - } - - ret = add_ds_file_to_ds_file_group(ctf_fs_trace, - file->path->str); - if (ret) { - BT_LOGE("Cannot add stream file `%s` to stream file group", - file->path->str); - ctf_fs_file_destroy(file); - goto error; - } - - ctf_fs_file_destroy(file); - } - - goto end; - -error: - ret = -1; - -end: - if (dir) { - g_dir_close(dir); - dir = NULL; - } - - if (error) { - g_error_free(error); - } - - return ret; -} - -static -int set_trace_name(bt_trace *trace, const char *name_suffix) -{ - int ret = 0; - const bt_trace_class *tc = bt_trace_borrow_class_const(trace); - const bt_value *val; - GString *name; - - name = g_string_new(NULL); - if (!name) { - BT_LOGE_STR("Failed to allocate a GString."); - ret = -1; - goto end; - } - - /* - * Check if we have a trace environment string value named `hostname`. - * If so, use it as the trace name's prefix. - */ - val = bt_trace_class_borrow_environment_entry_value_by_name_const( - tc, "hostname"); - if (val && bt_value_is_string(val)) { - g_string_append(name, bt_value_string_get(val)); - - if (name_suffix) { - g_string_append_c(name, G_DIR_SEPARATOR); - } - } - - if (name_suffix) { - g_string_append(name, name_suffix); - } - - ret = bt_trace_set_name(trace, name->str); - if (ret) { - goto end; - } - - goto end; - -end: - if (name) { - g_string_free(name, TRUE); - } - - return ret; -} - -static -struct ctf_fs_trace *ctf_fs_trace_create(bt_self_component_source *self_comp, - const char *path, const char *name, - struct ctf_fs_metadata_config *metadata_config) -{ - struct ctf_fs_trace *ctf_fs_trace; - int ret; - - ctf_fs_trace = g_new0(struct ctf_fs_trace, 1); - if (!ctf_fs_trace) { - goto end; - } - - ctf_fs_trace->path = g_string_new(path); - if (!ctf_fs_trace->path) { - goto error; - } - - ctf_fs_trace->name = g_string_new(name); - if (!ctf_fs_trace->name) { - goto error; - } - - ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1); - if (!ctf_fs_trace->metadata) { - goto error; - } - - ctf_fs_metadata_init(ctf_fs_trace->metadata); - ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func( - (GDestroyNotify) ctf_fs_ds_file_group_destroy); - if (!ctf_fs_trace->ds_file_groups) { - goto error; - } - - ret = ctf_fs_metadata_set_trace_class(self_comp, - ctf_fs_trace, metadata_config); - if (ret) { - goto error; - } - - if (ctf_fs_trace->metadata->trace_class) { - ctf_fs_trace->trace = - bt_trace_create(ctf_fs_trace->metadata->trace_class); - if (!ctf_fs_trace->trace) { - goto error; - } - } - - if (ctf_fs_trace->trace) { - ret = set_trace_name(ctf_fs_trace->trace, name); - if (ret) { - goto error; - } - } - - ret = create_ds_file_groups(ctf_fs_trace); - if (ret) { - goto error; - } - - goto end; - -error: - ctf_fs_trace_destroy(ctf_fs_trace); - ctf_fs_trace = NULL; - -end: - return ctf_fs_trace; -} - -static -int path_is_ctf_trace(const char *path) -{ - GString *metadata_path = g_string_new(NULL); - int ret = 0; - - if (!metadata_path) { - ret = -1; - goto end; - } - - g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME); - - if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) { - ret = 1; - goto end; - } - -end: - g_string_free(metadata_path, TRUE); - return ret; -} - -static -int add_trace_path(GList **trace_paths, const char *path) -{ - GString *norm_path = NULL; - int ret = 0; - - norm_path = bt_common_normalize_path(path, NULL); - if (!norm_path) { - BT_LOGE("Failed to normalize path `%s`.", path); - ret = -1; - goto end; - } - - // FIXME: Remove or ifdef for __MINGW32__ - if (strcmp(norm_path->str, "/") == 0) { - BT_LOGE("Opening a trace in `/` is not supported."); - ret = -1; - goto end; - } - - *trace_paths = g_list_prepend(*trace_paths, norm_path); - BT_ASSERT(*trace_paths); - norm_path = NULL; - -end: - if (norm_path) { - g_string_free(norm_path, TRUE); - } - - return ret; -} - -static -int ctf_fs_find_traces(GList **trace_paths, const char *start_path) -{ - int ret; - GError *error = NULL; - GDir *dir = NULL; - const char *basename = NULL; - - /* Check if the starting path is a CTF trace itself */ - ret = path_is_ctf_trace(start_path); - if (ret < 0) { - goto end; - } - - if (ret) { - /* - * Stop recursion: a CTF trace cannot contain another - * CTF trace. - */ - ret = add_trace_path(trace_paths, start_path); - goto end; - } - - /* Look for subdirectories */ - if (!g_file_test(start_path, G_FILE_TEST_IS_DIR)) { - /* Starting path is not a directory: end of recursion */ - goto end; - } - - dir = g_dir_open(start_path, 0, &error); - if (!dir) { - if (error->code == G_FILE_ERROR_ACCES) { - BT_LOGD("Cannot open directory `%s`: %s (code %d): continuing", - start_path, error->message, error->code); - goto end; - } - - BT_LOGE("Cannot open directory `%s`: %s (code %d)", - start_path, error->message, error->code); - ret = -1; - goto end; - } - - while ((basename = g_dir_read_name(dir))) { - GString *sub_path = g_string_new(NULL); - - if (!sub_path) { - ret = -1; - goto end; - } - - g_string_printf(sub_path, "%s" G_DIR_SEPARATOR_S "%s", start_path, basename); - ret = ctf_fs_find_traces(trace_paths, sub_path->str); - g_string_free(sub_path, TRUE); - if (ret) { - goto end; - } - } - -end: - if (dir) { - g_dir_close(dir); - } - - if (error) { - g_error_free(error); - } - - return ret; -} - -static -GList *ctf_fs_create_trace_names(GList *trace_paths, const char *base_path) { - GList *trace_names = NULL; - GList *node; - const char *last_sep; - size_t base_dist; - - /* - * At this point we know that all the trace paths are - * normalized, and so is the base path. This means that - * they are absolute and they don't end with a separator. - * We can simply find the location of the last separator - * in the base path, which gives us the name of the actual - * directory to look into, and use this location as the - * start of each trace name within each trace path. - * - * For example: - * - * Base path: /home/user/my-traces/some-trace - * Trace paths: - * - /home/user/my-traces/some-trace/host1/trace1 - * - /home/user/my-traces/some-trace/host1/trace2 - * - /home/user/my-traces/some-trace/host2/trace - * - /home/user/my-traces/some-trace/other-trace - * - * In this case the trace names are: - * - * - some-trace/host1/trace1 - * - some-trace/host1/trace2 - * - some-trace/host2/trace - * - some-trace/other-trace - */ - last_sep = strrchr(base_path, G_DIR_SEPARATOR); - - /* We know there's at least one separator */ - BT_ASSERT(last_sep); - - /* Distance to base */ - base_dist = last_sep - base_path + 1; - - /* Create the trace names */ - for (node = trace_paths; node; node = g_list_next(node)) { - GString *trace_name = g_string_new(NULL); - GString *trace_path = node->data; - - BT_ASSERT(trace_name); - g_string_assign(trace_name, &trace_path->str[base_dist]); - trace_names = g_list_append(trace_names, trace_name); - } - - return trace_names; -} - -/* Helper for ctf_fs_component_create_ctf_fs_traces, to handle a single path/root. */ - -static -int ctf_fs_component_create_ctf_fs_traces_one_root(bt_self_component_source *self_comp, - struct ctf_fs_component *ctf_fs, - const char *path_param) -{ - struct ctf_fs_trace *ctf_fs_trace = NULL; - int ret = 0; - GString *norm_path = NULL; - GList *trace_paths = NULL; - GList *trace_names = NULL; - GList *tp_node; - GList *tn_node; - - norm_path = bt_common_normalize_path(path_param, NULL); - if (!norm_path) { - BT_LOGE("Failed to normalize path: `%s`.", - path_param); - goto error; - } - - ret = ctf_fs_find_traces(&trace_paths, norm_path->str); - if (ret) { - goto error; - } - - if (!trace_paths) { - BT_LOGE("No CTF traces recursively found in `%s`.", - path_param); - goto error; - } - - trace_names = ctf_fs_create_trace_names(trace_paths, norm_path->str); - if (!trace_names) { - BT_LOGE("Cannot create trace names from trace paths."); - goto error; - } - - for (tp_node = trace_paths, tn_node = trace_names; tp_node; - tp_node = g_list_next(tp_node), - tn_node = g_list_next(tn_node)) { - GString *trace_path = tp_node->data; - GString *trace_name = tn_node->data; - - ctf_fs_trace = ctf_fs_trace_create(self_comp, - trace_path->str, trace_name->str, - &ctf_fs->metadata_config); - if (!ctf_fs_trace) { - BT_LOGE("Cannot create trace for `%s`.", - trace_path->str); - goto error; - } - - g_ptr_array_add(ctf_fs->traces, ctf_fs_trace); - ctf_fs_trace = NULL; - } - - goto end; - -error: - ret = -1; - ctf_fs_trace_destroy(ctf_fs_trace); - -end: - for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) { - if (tp_node->data) { - g_string_free(tp_node->data, TRUE); - } - } - - for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) { - if (tn_node->data) { - g_string_free(tn_node->data, TRUE); - } - } - - if (trace_paths) { - g_list_free(trace_paths); - } - - if (trace_names) { - g_list_free(trace_names); - } - - if (norm_path) { - g_string_free(norm_path, TRUE); - } - - return ret; -} - -/* GCompareFunc to sort traces by UUID. */ - -static -gint sort_traces_by_uuid(gconstpointer a, gconstpointer b) -{ - const struct ctf_fs_trace *trace_a = *((const struct ctf_fs_trace **) a); - const struct ctf_fs_trace *trace_b = *((const struct ctf_fs_trace **) b); - - bool trace_a_has_uuid = trace_a->metadata->tc->is_uuid_set; - bool trace_b_has_uuid = trace_b->metadata->tc->is_uuid_set; - gint ret; - - /* Order traces without uuid first. */ - if (!trace_a_has_uuid && trace_b_has_uuid) { - ret = -1; - } else if (trace_a_has_uuid && !trace_b_has_uuid) { - ret = 1; - } else if (!trace_a_has_uuid && !trace_b_has_uuid) { - ret = 0; - } else { - ret = bt_uuid_compare(trace_a->metadata->tc->uuid, trace_b->metadata->tc->uuid); - } - - return ret; -} - -/* - * Count the number of stream and event classes defined by this trace's metadata. - * - * This is used to determine which metadata is the "latest", out of multiple - * traces sharing the same UUID. It is assumed that amongst all these metadatas, - * a bigger metadata is a superset of a smaller metadata. Therefore, it is - * enough to just count the classes. - */ - -static -unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace) -{ - unsigned int num = trace->metadata->tc->stream_classes->len; - guint i; - - for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) { - struct ctf_stream_class *sc = trace->metadata->tc->stream_classes->pdata[i]; - num += sc->event_classes->len; - } - - return num; -} - -/* - * Merge the src ds_file_group into dest. This consists of merging their - * ds_file_infos, making sure to keep the result sorted. - */ - -static -void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest, struct ctf_fs_ds_file_group *src) -{ - guint i; - - for (i = 0; i < src->ds_file_infos->len; i++) { - struct ctf_fs_ds_file_info *ds_file_info = - g_ptr_array_index(src->ds_file_infos, i); - - /* Ownership of the ds_file_info is transferred to dest. */ - g_ptr_array_index(src->ds_file_infos, i) = NULL; - - ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info); - } - - /* Merge both indexes. */ - for (i = 0; i < src->index->entries->len; i++) { - struct ctf_fs_ds_index_entry *entry = g_ptr_array_index( - src->index->entries, i); - - /* - * Ownership of the ctf_fs_ds_index_entry is transferred to - * dest. - */ - g_ptr_array_index(src->index->entries, i) = NULL; - - ds_file_group_insert_ds_index_entry_sorted(dest, entry); - } -} -/* Merge src_trace's data stream file groups into dest_trace's. */ - -static -int merge_matching_ctf_fs_ds_file_groups( - struct ctf_fs_trace *dest_trace, - struct ctf_fs_trace *src_trace) -{ - - GPtrArray *dest = dest_trace->ds_file_groups; - GPtrArray *src = src_trace->ds_file_groups; - guint s_i; - int ret = 0; - - /* - * Save the initial length of dest: we only want to check against the - * original elements in the inner loop. - */ - const guint dest_len = dest->len; - - for (s_i = 0; s_i < src->len; s_i++) { - struct ctf_fs_ds_file_group *src_group = g_ptr_array_index(src, s_i); - struct ctf_fs_ds_file_group *dest_group = NULL; - - /* A stream instance without ID can't match a stream in the other trace. */ - if (src_group->stream_id != -1) { - guint d_i; - - /* Let's search for a matching ds_file_group in the destination. */ - for (d_i = 0; d_i < dest_len; d_i++) { - struct ctf_fs_ds_file_group *candidate_dest = g_ptr_array_index(dest, d_i); - - /* Can't match a stream instance without ID. */ - if (candidate_dest->stream_id == -1) { - continue; - } - - /* - * If the two groups have the same stream instance id - * and belong to the same stream class (stream instance - * ids are per-stream class), they represent the same - * stream instance. - */ - if (candidate_dest->stream_id != src_group->stream_id || - candidate_dest->sc->id != src_group->sc->id) { - continue; - } - - dest_group = candidate_dest; - break; - } - } - - /* - * Didn't find a friend in dest to merge our src_group into? - * Create a new empty one. This can happen if a stream was - * active in the source trace chunk but not in the destination - * trace chunk. - */ - if (!dest_group) { - struct ctf_stream_class *sc; - struct ctf_fs_ds_index *index; - - sc = ctf_trace_class_borrow_stream_class_by_id( - dest_trace->metadata->tc, src_group->sc->id); - BT_ASSERT(sc); - - index = ctf_fs_ds_index_create(); - if (!index) { - ret = -1; - goto end; - } - - dest_group = ctf_fs_ds_file_group_create(dest_trace, sc, - src_group->stream_id, index); - /* Ownership of index is transferred. */ - index = NULL; - if (!dest_group) { - ret = -1; - goto end; - } - - g_ptr_array_add(dest_trace->ds_file_groups, dest_group); - } - - BT_ASSERT(dest_group); - merge_ctf_fs_ds_file_groups(dest_group, src_group); - } - -end: - return ret; -} - -/* - * Collapse the given traces, which must all share the same UUID, in a single - * one. - * - * The trace with the most expansive metadata is chosen and all other traces - * are merged into that one. The array slots of all the traces that get merged - * in the chosen one are set to NULL, so only the slot of the chosen trace - * remains non-NULL. - */ - -static -int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces) -{ - unsigned int winner_count; - struct ctf_fs_trace *winner; - guint i; - int ret = 0; - char uuid_str[BABELTRACE_UUID_STR_LEN]; - - BT_ASSERT(num_traces >= 2); - - winner_count = metadata_count_stream_and_event_classes(traces[0]); - winner = traces[0]; - - /* Find the trace with the largest metadata. */ - for (i = 1; i < num_traces; i++) { - struct ctf_fs_trace *candidate; - unsigned int candidate_count; - - candidate = traces[i]; - - /* A bit of sanity check. */ - BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0); - - candidate_count = metadata_count_stream_and_event_classes(candidate); - - if (candidate_count > winner_count) { - winner_count = candidate_count; - winner = candidate; - } - } - - /* Merge all the other traces in the winning trace. */ - for (i = 0; i < num_traces; i++) { - struct ctf_fs_trace *trace = traces[i]; - - /* Don't merge the winner into itself. */ - if (trace == winner) { - continue; - } - - /* Merge trace's data stream file groups into winner's. */ - ret = merge_matching_ctf_fs_ds_file_groups(winner, trace); - if (ret) { - goto end; - } - - /* Free the trace that got merged into winner, clear the slot in the array. */ - ctf_fs_trace_destroy(trace); - traces[i] = NULL; - } - - /* Use the string representation of the UUID as the trace name. */ - bt_uuid_unparse(winner->metadata->tc->uuid, uuid_str); - g_string_printf(winner->name, "%s", uuid_str); - -end: - return ret; -} - -/* - * Merge all traces of `ctf_fs` that share the same UUID in a single trace. - * Traces with no UUID are not merged. - */ - -static -int merge_traces_with_same_uuid(struct ctf_fs_component *ctf_fs) -{ - GPtrArray *traces = ctf_fs->traces; - guint range_start_idx = 0; - unsigned int num_traces = 0; - guint i; - int ret = 0; - - /* Sort the traces by uuid, then collapse traces with the same uuid in a single one. */ - g_ptr_array_sort(traces, sort_traces_by_uuid); - - /* Find ranges of consecutive traces that share the same UUID. */ - while (range_start_idx < traces->len) { - guint range_len; - struct ctf_fs_trace *range_start_trace = g_ptr_array_index(traces, range_start_idx); - - /* Exclusive end of range. */ - guint range_end_exc_idx = range_start_idx + 1; - - while (range_end_exc_idx < traces->len) { - struct ctf_fs_trace *this_trace = g_ptr_array_index(traces, range_end_exc_idx); - - if (!range_start_trace->metadata->tc->is_uuid_set || - (bt_uuid_compare(range_start_trace->metadata->tc->uuid, this_trace->metadata->tc->uuid) != 0)) { - break; - } - - range_end_exc_idx++; - } - - /* If we have two or more traces with matching UUIDs, merge them. */ - range_len = range_end_exc_idx - range_start_idx; - if (range_len > 1) { - struct ctf_fs_trace **range_start = (struct ctf_fs_trace **) &traces->pdata[range_start_idx]; - ret = merge_ctf_fs_traces(range_start, range_len); - if (ret) { - goto end; - } - } - - num_traces++; - range_start_idx = range_end_exc_idx; - } - - /* Clear any NULL slot (traces that got merged in another one) in the array. */ - for (i = 0; i < traces->len;) { - if (g_ptr_array_index(traces, i) == NULL) { - g_ptr_array_remove_index_fast(traces, i); - } else { - i++; - } - } - - BT_ASSERT(num_traces == traces->len); - -end: - return ret; -} - -int ctf_fs_component_create_ctf_fs_traces(bt_self_component_source *self_comp, - struct ctf_fs_component *ctf_fs, - const bt_value *paths_value) -{ - int ret = 0; - uint64_t i; - - for (i = 0; i < bt_value_array_get_size(paths_value); i++) { - const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i); - const char *path = bt_value_string_get(path_value); - - ret = ctf_fs_component_create_ctf_fs_traces_one_root(self_comp, ctf_fs, path); - if (ret) { - goto end; - } - } - - ret = merge_traces_with_same_uuid(ctf_fs); - -end: - return ret; -} - -static -GString *get_stream_instance_unique_name( - struct ctf_fs_ds_file_group *ds_file_group) -{ - GString *name; - struct ctf_fs_ds_file_info *ds_file_info; - - name = g_string_new(NULL); - if (!name) { - goto end; - } - - /* - * If there's more than one stream file in the stream file - * group, the first (earliest) stream file's path is used as - * the stream's unique name. - */ - BT_ASSERT(ds_file_group->ds_file_infos->len > 0); - ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0); - g_string_assign(name, ds_file_info->path->str); - -end: - return name; -} - -/* Create the IR stream objects for ctf_fs_trace. */ - -static -int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace) -{ - int ret; - GString *name = NULL; - guint i; - - for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { - struct ctf_fs_ds_file_group *ds_file_group = - g_ptr_array_index(ctf_fs_trace->ds_file_groups, i); - name = get_stream_instance_unique_name(ds_file_group); - - if (!name) { - goto error; - } - - if (ds_file_group->sc->ir_sc) { - BT_ASSERT(ctf_fs_trace->trace); - - if (ds_file_group->stream_id == UINT64_C(-1)) { - /* No stream ID: use 0 */ - ds_file_group->stream = bt_stream_create_with_id( - ds_file_group->sc->ir_sc, - ctf_fs_trace->trace, - ctf_fs_trace->next_stream_id); - ctf_fs_trace->next_stream_id++; - } else { - /* Specific stream ID */ - ds_file_group->stream = bt_stream_create_with_id( - ds_file_group->sc->ir_sc, - ctf_fs_trace->trace, - (uint64_t) ds_file_group->stream_id); - } - } else { - ds_file_group->stream = NULL; - } - - if (!ds_file_group->stream) { - BT_LOGE("Cannot create stream for DS file group: " - "addr=%p, stream-name=\"%s\"", - ds_file_group, name->str); - goto error; - } - - ret = bt_stream_set_name(ds_file_group->stream, - name->str); - if (ret) { - BT_LOGE("Cannot set stream's name: " - "addr=%p, stream-name=\"%s\"", - ds_file_group->stream, name->str); - goto error; - } - - g_string_free(name, TRUE); - name = NULL; - } - - ret = 0; - goto end; - -error: - ret = -1; - -end: - - if (name) { - g_string_free(name, TRUE); - } - return ret; -} - -/* - * Validate the "paths" parameter passed to this component. It must be - * present, and it must be an array of strings. - */ - -static -bool validate_paths_parameter(const bt_value *paths) -{ - bool ret; - bt_value_type type; - uint64_t i; - - if (!paths) { - BT_LOGE("missing \"paths\" parameter"); - goto error; - } - - type = bt_value_get_type(paths); - if (type != BT_VALUE_TYPE_ARRAY) { - BT_LOGE("`paths` parameter: expecting array value: type=%s", - bt_common_value_type_string(type)); - goto error; - } - - for (i = 0; i < bt_value_array_get_size(paths); i++) { - const bt_value *elem; - - elem = bt_value_array_borrow_element_by_index_const(paths, i); - type = bt_value_get_type(elem); - if (type != BT_VALUE_TYPE_STRING) { - BT_LOGE("`paths` parameter: expecting string value: index=%" PRIu64 ", type=%s", - i, bt_common_value_type_string(type)); - goto error; - } - } - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -bool read_src_fs_parameters(const bt_value *params, - const bt_value **paths, struct ctf_fs_component *ctf_fs) { - bool ret; - const bt_value *value; - - /* paths parameter */ - *paths = bt_value_map_borrow_entry_value_const(params, "paths"); - if (!validate_paths_parameter(*paths)) { - goto error; - } - - /* clock-class-offset-s parameter */ - value = bt_value_map_borrow_entry_value_const(params, - "clock-class-offset-s"); - if (value) { - if (!bt_value_is_signed_integer(value)) { - BT_LOGE("clock-class-offset-s must be an integer"); - goto error; - } - ctf_fs->metadata_config.clock_class_offset_s = - bt_value_signed_integer_get(value); - } - - /* clock-class-offset-ns parameter */ - value = bt_value_map_borrow_entry_value_const(params, - "clock-class-offset-ns"); - if (value) { - if (!bt_value_is_signed_integer(value)) { - BT_LOGE("clock-class-offset-ns must be an integer"); - goto error; - } - ctf_fs->metadata_config.clock_class_offset_ns = - bt_value_signed_integer_get(value); - } - - - ret = true; - goto end; - -error: - ret = false; - -end: - return ret; -} - -static -struct ctf_fs_component *ctf_fs_create( - bt_self_component_source *self_comp, - const bt_value *params) -{ - struct ctf_fs_component *ctf_fs = NULL; - guint i; - const bt_value *paths_value; - - ctf_fs = ctf_fs_component_create(); - if (!ctf_fs) { - goto error; - } - - if (!read_src_fs_parameters(params, &paths_value, ctf_fs)) { - goto error; - } - - bt_self_component_set_data( - bt_self_component_source_as_self_component(self_comp), - ctf_fs); - - /* - * We don't need to get a new reference here because as long as - * our private ctf_fs_component object exists, the containing - * private component should also exist. - */ - ctf_fs->self_comp = self_comp; - - if (ctf_fs_component_create_ctf_fs_traces(self_comp, ctf_fs, paths_value)) { - goto error; - } - - for (i = 0; i < ctf_fs->traces->len; i++) { - struct ctf_fs_trace *trace = g_ptr_array_index(ctf_fs->traces, i); - - if (create_streams_for_trace(trace)) { - goto error; - } - - if (create_ports_for_trace(ctf_fs, trace)) { - goto error; - } - } - - goto end; - -error: - ctf_fs_destroy(ctf_fs); - ctf_fs = NULL; - bt_self_component_set_data( - bt_self_component_source_as_self_component(self_comp), - NULL); - -end: - return ctf_fs; -} - -BT_HIDDEN -bt_self_component_status ctf_fs_init( - bt_self_component_source *self_comp, - const bt_value *params, UNUSED_VAR void *init_method_data) -{ - struct ctf_fs_component *ctf_fs; - bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; - - ctf_fs = ctf_fs_create(self_comp, params); - if (!ctf_fs) { - ret = BT_SELF_COMPONENT_STATUS_ERROR; - } - - return ret; -} - -BT_HIDDEN -bt_query_status ctf_fs_query( - bt_self_component_class_source *comp_class, - const bt_query_executor *query_exec, - const char *object, const bt_value *params, - const bt_value **result) -{ - bt_query_status status = BT_QUERY_STATUS_OK; - - if (!strcmp(object, "metadata-info")) { - status = metadata_info_query(comp_class, params, result); - } else if (!strcmp(object, "trace-info")) { - status = trace_info_query(comp_class, params, result); - } else { - BT_LOGE("Unknown query object `%s`", object); - status = BT_QUERY_STATUS_INVALID_OBJECT; - goto end; - } -end: - return status; -} diff --git a/plugins/ctf/fs-src/fs.h b/plugins/ctf/fs-src/fs.h deleted file mode 100644 index 9e9ee84d..00000000 --- a/plugins/ctf/fs-src/fs.h +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_H -#define BABELTRACE_PLUGIN_CTF_FS_H - -/* - * BabelTrace - CTF on File System Component - * - * Copyright 2016 Jérémie Galarneau - * Copyright 2016 Philippe Proulx - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "data-stream-file.h" -#include "metadata.h" -#include "../common/metadata/decoder.h" - -BT_HIDDEN -extern bool ctf_fs_debug; - -struct ctf_fs_file { - /* Owned by this */ - GString *path; - - /* Owned by this */ - FILE *fp; - - off_t size; -}; - -struct ctf_fs_metadata { - /* Owned by this */ - struct ctf_metadata_decoder *decoder; - - /* Owned by this */ - bt_trace_class *trace_class; - - /* Weak (owned by `decoder` above) */ - struct ctf_trace_class *tc; - - /* Owned by this */ - - /* Owned by this */ - char *text; - - int bo; -}; - -struct ctf_fs_component { - /* Weak, guaranteed to exist */ - bt_self_component_source *self_comp; - - /* Array of struct ctf_fs_port_data *, owned by this */ - GPtrArray *port_data; - - /* Array of struct ctf_fs_trace *, owned by this */ - GPtrArray *traces; - - struct ctf_fs_metadata_config metadata_config; -}; - -struct ctf_fs_trace { - /* Owned by this */ - struct ctf_fs_metadata *metadata; - - /* Owned by this */ - bt_trace *trace; - - /* Array of struct ctf_fs_ds_file_group *, owned by this */ - GPtrArray *ds_file_groups; - - /* Owned by this */ - GString *path; - - /* Owned by this */ - GString *name; - - /* Next automatic stream ID when not provided by packet header */ - uint64_t next_stream_id; -}; - -struct ctf_fs_ds_index_entry { - /* Position, in bytes, of the packet from the beginning of the file. */ - uint64_t offset; - - /* Size of the packet, in bytes. */ - uint64_t packet_size; - - /* - * Extracted from the packet context, relative to the respective fields' - * mapped clock classes (in cycles). - */ - uint64_t timestamp_begin, timestamp_end; - - /* - * Converted from the packet context, relative to the trace's EPOCH - * (in ns since EPOCH). - */ - int64_t timestamp_begin_ns, timestamp_end_ns; -}; - -struct ctf_fs_ds_index { - /* Array of pointer to struct ctf_fs_fd_index_entry. */ - GPtrArray *entries; -}; - -struct ctf_fs_ds_file_group { - /* - * Array of struct ctf_fs_ds_file_info, owned by this. - * - * This is an _ordered_ array of data stream file infos which - * belong to this group (a single stream instance). - * - * You can call ctf_fs_ds_file_create() with one of those paths - * and the trace IR stream below. - */ - GPtrArray *ds_file_infos; - - /* Owned by this */ - struct ctf_stream_class *sc; - - /* Owned by this */ - bt_stream *stream; - - /* Stream (instance) ID; -1ULL means none */ - uint64_t stream_id; - - /* Weak, belongs to component */ - struct ctf_fs_trace *ctf_fs_trace; - - /* - * Owned by this. May be NULL. - * - * A stream cannot be assumed to be indexed as the indexing might have - * been skipped. Moreover, the index's fields may not all be available - * depending on the producer (e.g. timestamp_begin/end are not - * mandatory). - */ - struct ctf_fs_ds_index *index; -}; - -struct ctf_fs_port_data { - /* Weak, belongs to ctf_fs_trace */ - struct ctf_fs_ds_file_group *ds_file_group; - - /* Weak */ - struct ctf_fs_component *ctf_fs; -}; - -struct ctf_fs_msg_iter_data { - /* Weak */ - bt_self_message_iterator *pc_msg_iter; - - /* Weak, belongs to ctf_fs_trace */ - struct ctf_fs_ds_file_group *ds_file_group; - - /* Owned by this */ - struct ctf_fs_ds_file *ds_file; - - /* Which file the iterator is _currently_ operating on */ - size_t ds_file_info_index; - - /* Owned by this */ - struct bt_msg_iter *msg_iter; -}; - -BT_HIDDEN -bt_self_component_status ctf_fs_init( - bt_self_component_source *source, - const bt_value *params, void *init_method_data); - -BT_HIDDEN -void ctf_fs_finalize(bt_self_component_source *component); - -BT_HIDDEN -bt_query_status ctf_fs_query( - bt_self_component_class_source *comp_class, - const bt_query_executor *query_exec, - const char *object, const bt_value *params, - const bt_value **result); - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_source *self_comp, - bt_self_component_port_output *self_port); - -BT_HIDDEN -void ctf_fs_iterator_finalize(bt_self_message_iterator *it); - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_next( - bt_self_message_iterator *iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -BT_HIDDEN -bt_self_message_iterator_status ctf_fs_iterator_seek_beginning( - bt_self_message_iterator *message_iterator); - -/* Create and initialize a new, empty ctf_fs_component. */ - -BT_HIDDEN -struct ctf_fs_component *ctf_fs_component_create(void); - -/* - * Search recursively under all paths in `paths_value` (an array of strings), - * for CTF traces. For each CTF trace found, create a ctf_fs_trace in - * `ctf_fs` representing that trace. - */ - -BT_HIDDEN -int ctf_fs_component_create_ctf_fs_traces(bt_self_component_source *self_comp, - struct ctf_fs_component *ctf_fs, - const bt_value *paths_value); - -/* Free `ctf_fs` and everything it owns. */ - -BT_HIDDEN -void ctf_fs_destroy(struct ctf_fs_component *ctf_fs); - -/* - * Read and validate parameters taken by the src.ctf.fs plugin. - * - * - The mandatory `paths` parameter is returned in `*paths`. - * - The optional `clock-class-offset-s` and `clock-class-offset-ns`, if - * present, are recorded in the `ctf_fs` structure. - * - * Return true on success, false if any parameter didn't pass validation. - */ - -BT_HIDDEN -bool read_src_fs_parameters(const bt_value *params, - const bt_value **paths, struct ctf_fs_component *ctf_fs); - -/* - * Generate the port name to be used for a given data stream file group. - * - * The result must be freed using g_free by the caller. - */ - -BT_HIDDEN -gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_H */ diff --git a/plugins/ctf/fs-src/logging.c b/plugins/ctf/fs-src/logging.c deleted file mode 100644 index 6f24ce76..00000000 --- a/plugins/ctf/fs-src/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL ctf_fs_src_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(BT_LOG_OUTPUT_LEVEL, "BABELTRACE_SRC_CTF_FS_LOG_LEVEL"); diff --git a/plugins/ctf/fs-src/logging.h b/plugins/ctf/fs-src/logging.h deleted file mode 100644 index 8a249dcc..00000000 --- a/plugins/ctf/fs-src/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CTF_FS_SRC_LOGGING_H -#define CTF_FS_SRC_LOGGING_H - -#define BT_LOG_OUTPUT_LEVEL ctf_fs_src_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_fs_src_log_level); - -#endif /* CTF_FS_SRC_LOGGING_H */ diff --git a/plugins/ctf/fs-src/lttng-index.h b/plugins/ctf/fs-src/lttng-index.h deleted file mode 100644 index 037010d7..00000000 --- a/plugins/ctf/fs-src/lttng-index.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 - Julien Desfossez - * Mathieu Desnoyers - * David Goulet - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef LTTNG_INDEX_H -#define LTTNG_INDEX_H - -#include - -#define CTF_INDEX_MAGIC 0xC1F1DCC1 -#define CTF_INDEX_MAJOR 1 -#define CTF_INDEX_MINOR 1 - -/* - * Header at the beginning of each index file. - * All integer fields are stored in big endian. - */ -struct ctf_packet_index_file_hdr { - uint32_t magic; - uint32_t index_major; - uint32_t index_minor; - /* size of struct ctf_packet_index, in bytes. */ - uint32_t packet_index_len; -} __attribute__((__packed__)); - -/* - * Packet index generated for each trace packet store in a trace file. - * All integer fields are stored in big endian. - */ -struct ctf_packet_index { - uint64_t offset; /* offset of the packet in the file, in bytes */ - uint64_t packet_size; /* packet size, in bits */ - uint64_t content_size; /* content size, in bits */ - uint64_t timestamp_begin; - uint64_t timestamp_end; - uint64_t events_discarded; - uint64_t stream_id; - /* CTF_INDEX 1.0 limit */ - uint64_t stream_instance_id; /* ID of the channel instance */ - uint64_t packet_seq_num; /* packet sequence number */ -} __attribute__((__packed__)); - -#endif /* LTTNG_INDEX_H */ diff --git a/plugins/ctf/fs-src/metadata.c b/plugins/ctf/fs-src/metadata.c deleted file mode 100644 index 75043dc1..00000000 --- a/plugins/ctf/fs-src/metadata.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2016 - Philippe Proulx - * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation - * - * Some functions are based on older functions written by Mathieu Desnoyers. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fs.h" -#include "file.h" -#include "metadata.h" -#include "../common/metadata/decoder.h" - -#define BT_LOG_TAG "PLUGIN-CTF-FS-METADATA-SRC" -#include "logging.h" - -BT_HIDDEN -FILE *ctf_fs_metadata_open_file(const char *trace_path) -{ - GString *metadata_path; - FILE *fp = NULL; - - metadata_path = g_string_new(trace_path); - if (!metadata_path) { - goto end; - } - - g_string_append(metadata_path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME); - fp = fopen(metadata_path->str, "rb"); - g_string_free(metadata_path, TRUE); -end: - return fp; -} - -static struct ctf_fs_file *get_file(const char *trace_path) -{ - struct ctf_fs_file *file = ctf_fs_file_create(); - - if (!file) { - goto error; - } - - g_string_append(file->path, trace_path); - g_string_append(file->path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME); - - if (ctf_fs_file_open(file, "rb")) { - goto error; - } - - goto end; - -error: - if (file) { - ctf_fs_file_destroy(file); - file = NULL; - } - -end: - return file; -} - -BT_HIDDEN -int ctf_fs_metadata_set_trace_class( - bt_self_component_source *self_comp, - struct ctf_fs_trace *ctf_fs_trace, - struct ctf_fs_metadata_config *config) -{ - int ret = 0; - struct ctf_fs_file *file = NULL; - struct ctf_metadata_decoder_config decoder_config = { - .clock_class_offset_s = config ? config->clock_class_offset_s : 0, - .clock_class_offset_ns = config ? config->clock_class_offset_ns : 0, - }; - - file = get_file(ctf_fs_trace->path->str); - if (!file) { - BT_LOGE("Cannot create metadata file object"); - ret = -1; - goto end; - } - - ctf_fs_trace->metadata->decoder = ctf_metadata_decoder_create(self_comp, - config ? &decoder_config : NULL); - if (!ctf_fs_trace->metadata->decoder) { - BT_LOGE("Cannot create metadata decoder object"); - ret = -1; - goto end; - } - - ret = ctf_metadata_decoder_decode(ctf_fs_trace->metadata->decoder, - file->fp); - if (ret) { - BT_LOGE("Cannot decode metadata file"); - goto end; - } - - ctf_fs_trace->metadata->trace_class = - ctf_metadata_decoder_get_ir_trace_class( - ctf_fs_trace->metadata->decoder); - BT_ASSERT(!self_comp || ctf_fs_trace->metadata->trace_class); - ctf_fs_trace->metadata->tc = - ctf_metadata_decoder_borrow_ctf_trace_class( - ctf_fs_trace->metadata->decoder); - BT_ASSERT(ctf_fs_trace->metadata->tc); - -end: - ctf_fs_file_destroy(file); - return ret; -} - -BT_HIDDEN -int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata) -{ - /* Nothing to initialize for the moment. */ - return 0; -} - -BT_HIDDEN -void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata) -{ - if (metadata->text) { - free(metadata->text); - } - - if (metadata->trace_class) { - BT_TRACE_CLASS_PUT_REF_AND_RESET(metadata->trace_class); - } - - if (metadata->decoder) { - ctf_metadata_decoder_destroy(metadata->decoder); - } -} diff --git a/plugins/ctf/fs-src/metadata.h b/plugins/ctf/fs-src/metadata.h deleted file mode 100644 index 6ddc0291..00000000 --- a/plugins/ctf/fs-src/metadata.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CTF_FS_METADATA_H -#define CTF_FS_METADATA_H - -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#define CTF_FS_METADATA_FILENAME "metadata" - -struct ctf_fs_trace; -struct ctf_fs_metadata; - -struct ctf_fs_metadata_config { - int64_t clock_class_offset_s; - int64_t clock_class_offset_ns; -}; - -BT_HIDDEN -int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata); - -BT_HIDDEN -void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata); - -BT_HIDDEN -int ctf_fs_metadata_set_trace_class(bt_self_component_source *self_comp, - struct ctf_fs_trace *ctf_fs_trace, - struct ctf_fs_metadata_config *config); - -BT_HIDDEN -FILE *ctf_fs_metadata_open_file(const char *trace_path); - -BT_HIDDEN -bool ctf_metadata_is_packetized(FILE *fp, int *byte_order); - -#endif /* CTF_FS_METADATA_H */ diff --git a/plugins/ctf/fs-src/query.c b/plugins/ctf/fs-src/query.c deleted file mode 100644 index e2018b15..00000000 --- a/plugins/ctf/fs-src/query.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * query.c - * - * Babeltrace CTF file system Reader Component queries - * - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "query.h" -#include -#include -#include "metadata.h" -#include "../common/metadata/decoder.h" -#include -#include -#include -#include "fs.h" - -#define BT_LOG_TAG "PLUGIN-CTF-FS-QUERY-SRC" -#include "logging.h" - -#define METADATA_TEXT_SIG "/* CTF 1.8" - -struct range { - int64_t begin_ns; - int64_t end_ns; - bool set; -}; - -BT_HIDDEN -bt_query_status metadata_info_query( - bt_self_component_class_source *comp_class, - const bt_value *params, - const bt_value **user_result) -{ - bt_query_status status = BT_QUERY_STATUS_OK; - bt_value *result = NULL; - const bt_value *path_value = NULL; - char *metadata_text = NULL; - FILE *metadata_fp = NULL; - GString *g_metadata_text = NULL; - int ret; - int bo; - const char *path; - bool is_packetized; - - result = bt_value_map_create(); - if (!result) { - status = BT_QUERY_STATUS_NOMEM; - goto error; - } - - BT_ASSERT(params); - - if (!bt_value_is_map(params)) { - BT_LOGE_STR("Query parameters is not a map value object."); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - path_value = bt_value_map_borrow_entry_value_const(params, "path"); - if (!path_value) { - BT_LOGE_STR("Mandatory `path` parameter missing"); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - if (!bt_value_is_string(path_value)) { - BT_LOGE_STR("`path` parameter is required to be a string value"); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - path = bt_value_string_get(path_value); - - BT_ASSERT(path); - metadata_fp = ctf_fs_metadata_open_file(path); - if (!metadata_fp) { - BT_LOGE("Cannot open trace metadata: path=\"%s\".", path); - goto error; - } - - is_packetized = ctf_metadata_decoder_is_packetized(metadata_fp, - &bo); - - if (is_packetized) { - ret = ctf_metadata_decoder_packetized_file_stream_to_buf( - metadata_fp, &metadata_text, bo); - if (ret) { - BT_LOGE("Cannot decode packetized metadata file: path=\"%s\"", - path); - goto error; - } - } else { - long filesize; - - ret = fseek(metadata_fp, 0, SEEK_END); - if (ret) { - BT_LOGE_ERRNO("Failed to seek to the end of the metadata file", - ": path=\"%s\"", path); - goto error; - } - filesize = ftell(metadata_fp); - if (filesize < 0) { - BT_LOGE_ERRNO("Failed to get the current position in the metadata file", - ": path=\"%s\"", path); - goto error; - } - rewind(metadata_fp); - metadata_text = malloc(filesize + 1); - if (!metadata_text) { - BT_LOGE_STR("Cannot allocate buffer for metadata text."); - goto error; - } - - if (fread(metadata_text, filesize, 1, metadata_fp) != 1) { - BT_LOGE_ERRNO("Cannot read metadata file", ": path=\"%s\"", - path); - goto error; - } - - metadata_text[filesize] = '\0'; - } - - g_metadata_text = g_string_new(NULL); - if (!g_metadata_text) { - goto error; - } - - if (strncmp(metadata_text, METADATA_TEXT_SIG, - sizeof(METADATA_TEXT_SIG) - 1) != 0) { - g_string_assign(g_metadata_text, METADATA_TEXT_SIG); - g_string_append(g_metadata_text, " */\n\n"); - } - - g_string_append(g_metadata_text, metadata_text); - - ret = bt_value_map_insert_string_entry(result, "text", - g_metadata_text->str); - if (ret) { - BT_LOGE_STR("Cannot insert metadata text into query result."); - goto error; - } - - ret = bt_value_map_insert_bool_entry(result, "is-packetized", - is_packetized); - if (ret) { - BT_LOGE_STR("Cannot insert \"is-packetized\" attribute into query result."); - goto error; - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(result); - result = NULL; - - if (status >= 0) { - status = BT_QUERY_STATUS_ERROR; - } - -end: - free(metadata_text); - - if (g_metadata_text) { - g_string_free(g_metadata_text, TRUE); - } - - if (metadata_fp) { - fclose(metadata_fp); - } - - *user_result = result; - return status; -} - -static -int add_range(bt_value *info, struct range *range, - const char *range_name) -{ - int ret = 0; - bt_value_status status; - bt_value *range_map = NULL; - - if (!range->set) { - /* Not an error. */ - goto end; - } - - range_map = bt_value_map_create(); - if (!range_map) { - ret = -1; - goto end; - } - - status = bt_value_map_insert_signed_integer_entry(range_map, "begin", - range->begin_ns); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - - status = bt_value_map_insert_signed_integer_entry(range_map, "end", - range->end_ns); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - - status = bt_value_map_insert_entry(info, range_name, - range_map); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - -end: - bt_value_put_ref(range_map); - return ret; -} - -static -int add_stream_ids(bt_value *info, struct ctf_fs_ds_file_group *ds_file_group) -{ - int ret = 0; - bt_value_status status; - - if (ds_file_group->stream_id != UINT64_C(-1)) { - status = bt_value_map_insert_unsigned_integer_entry(info, "id", - ds_file_group->stream_id); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - } - - status = bt_value_map_insert_unsigned_integer_entry(info, "class-id", - ds_file_group->sc->id); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - -end: - return ret; -} - -static -int populate_stream_info(struct ctf_fs_ds_file_group *group, - bt_value *group_info, struct range *stream_range) -{ - int ret = 0; - size_t file_idx; - bt_value_status status; - bt_value *file_paths; - struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry; - gchar *port_name = NULL; - - file_paths = bt_value_array_create(); - if (!file_paths) { - ret = -1; - goto end; - } - - for (file_idx = 0; file_idx < group->ds_file_infos->len; file_idx++) { - struct ctf_fs_ds_file_info *info = - g_ptr_array_index(group->ds_file_infos, - file_idx); - - status = bt_value_array_append_string_element(file_paths, - info->path->str); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - } - - /* - * Since each `struct ctf_fs_ds_file_group` has a sorted array of - * `struct ctf_fs_ds_index_entry`, we can compute the stream range from - * the timestamp_begin of the first index entry and the timestamp_end - * of the last index entry. - */ - BT_ASSERT(group->index); - BT_ASSERT(group->index->entries); - BT_ASSERT(group->index->entries->len > 0); - - /* First entry. */ - first_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index( - group->index->entries, 0); - - /* Last entry. */ - last_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index( - group->index->entries, group->index->entries->len - 1); - - stream_range->begin_ns = first_ds_index_entry->timestamp_begin_ns; - stream_range->end_ns = last_ds_index_entry->timestamp_end_ns; - - /* - * If any of the begin and end timestamps is not set it means that - * packets don't include `timestamp_begin` _and_ `timestamp_end` fields - * in their packet context so we can't set the range. - */ - stream_range->set = stream_range->begin_ns != UINT64_C(-1) && - stream_range->end_ns != UINT64_C(-1); - - ret = add_range(group_info, stream_range, "range-ns"); - if (ret) { - goto end; - } - - status = bt_value_map_insert_entry(group_info, "paths", - file_paths); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - - ret = add_stream_ids(group_info, group); - if (ret) { - goto end; - } - - port_name = ctf_fs_make_port_name(group); - if (!port_name) { - ret = -1; - goto end; - } - - status = bt_value_map_insert_string_entry(group_info, "port-name", - port_name); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - -end: - bt_value_put_ref(file_paths); - return ret; -} - -static -int populate_trace_info(const struct ctf_fs_trace *trace, bt_value *trace_info) -{ - int ret = 0; - size_t group_idx; - bt_value_status status; - bt_value *file_groups = NULL; - struct range trace_range = { - .begin_ns = INT64_MAX, - .end_ns = 0, - .set = false, - }; - struct range trace_intersection = { - .begin_ns = 0, - .end_ns = INT64_MAX, - .set = false, - }; - - BT_ASSERT(trace->ds_file_groups); - /* Add trace range info only if it contains streams. */ - if (trace->ds_file_groups->len == 0) { - ret = -1; - goto end; - } - - file_groups = bt_value_array_create(); - if (!file_groups) { - goto end; - } - - status = bt_value_map_insert_string_entry(trace_info, "name", - trace->name->str); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - status = bt_value_map_insert_string_entry(trace_info, "path", - trace->path->str); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - - /* Find range of all stream groups, and of the trace. */ - for (group_idx = 0; group_idx < trace->ds_file_groups->len; - group_idx++) { - bt_value *group_info; - struct range group_range = { .set = false }; - struct ctf_fs_ds_file_group *group = g_ptr_array_index( - trace->ds_file_groups, group_idx); - - group_info = bt_value_map_create(); - if (!group_info) { - ret = -1; - goto end; - } - - ret = populate_stream_info(group, group_info, &group_range); - if (ret) { - bt_value_put_ref(group_info); - goto end; - } - - status = bt_value_array_append_element(file_groups, group_info); - bt_value_put_ref(group_info); - if (status != BT_VALUE_STATUS_OK) { - goto end; - } - - if (group_range.set) { - trace_range.begin_ns = min(trace_range.begin_ns, - group_range.begin_ns); - trace_range.end_ns = max(trace_range.end_ns, - group_range.end_ns); - trace_range.set = true; - - trace_intersection.begin_ns = max(trace_intersection.begin_ns, - group_range.begin_ns); - trace_intersection.end_ns = min(trace_intersection.end_ns, - group_range.end_ns); - trace_intersection.set = true; - } - } - - ret = add_range(trace_info, &trace_range, "range-ns"); - if (ret) { - goto end; - } - - if (trace_intersection.begin_ns < trace_intersection.end_ns) { - ret = add_range(trace_info, &trace_intersection, - "intersection-range-ns"); - if (ret) { - goto end; - } - } - - status = bt_value_map_insert_entry(trace_info, "streams", - file_groups); - BT_VALUE_PUT_REF_AND_RESET(file_groups); - if (status != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - -end: - bt_value_put_ref(file_groups); - return ret; -} - -BT_HIDDEN -bt_query_status trace_info_query( - bt_self_component_class_source *comp_class, - const bt_value *params, - const bt_value **user_result) -{ - struct ctf_fs_component *ctf_fs = NULL; - bt_query_status status = BT_QUERY_STATUS_OK; - bt_value *result = NULL; - const bt_value *paths_value = NULL; - int ret = 0; - guint i; - - BT_ASSERT(params); - - if (!bt_value_is_map(params)) { - BT_LOGE("Query parameters is not a map value object."); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - ctf_fs = ctf_fs_component_create(); - if (!ctf_fs) { - goto error; - } - - if (!read_src_fs_parameters(params, &paths_value, ctf_fs)) { - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - if (ctf_fs_component_create_ctf_fs_traces(NULL, ctf_fs, paths_value)) { - goto error; - } - - result = bt_value_array_create(); - if (!result) { - status = BT_QUERY_STATUS_NOMEM; - goto error; - } - - for (i = 0; i < ctf_fs->traces->len; i++) { - struct ctf_fs_trace *trace; - bt_value *trace_info; - bt_value_status status; - - trace = g_ptr_array_index(ctf_fs->traces, i); - BT_ASSERT(trace); - - trace_info = bt_value_map_create(); - if (!trace_info) { - BT_LOGE("Failed to create trace info map."); - goto error; - } - - ret = populate_trace_info(trace, trace_info); - if (ret) { - bt_value_put_ref(trace_info); - goto error; - } - - status = bt_value_array_append_element(result, trace_info); - bt_value_put_ref(trace_info); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(result); - result = NULL; - - if (status >= 0) { - status = BT_QUERY_STATUS_ERROR; - } - -end: - if (ctf_fs) { - ctf_fs_destroy(ctf_fs); - ctf_fs = NULL; - } - - *user_result = result; - return status; -} diff --git a/plugins/ctf/fs-src/query.h b/plugins/ctf/fs-src/query.h deleted file mode 100644 index d19a1394..00000000 --- a/plugins/ctf/fs-src/query.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_FS_QUERY_H -#define BABELTRACE_PLUGIN_CTF_FS_QUERY_H - -/* - * BabelTrace - CTF on File System Component - * - * Copyright 2017 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -BT_HIDDEN -bt_query_status metadata_info_query( - bt_self_component_class_source *comp_class, - const bt_value *params, const bt_value **result); - -BT_HIDDEN -bt_query_status trace_info_query( - bt_self_component_class_source *comp_class, - const bt_value *params, const bt_value **result); - -#endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */ diff --git a/plugins/ctf/lttng-live/Makefile.am b/plugins/ctf/lttng-live/Makefile.am deleted file mode 100644 index aecd8391..00000000 --- a/plugins/ctf/lttng-live/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -libbabeltrace2_plugin_ctf_lttng_live_la_SOURCES = \ - lttng-live.c \ - lttng-live.h \ - data-stream.c \ - data-stream.h \ - metadata.c \ - metadata.h \ - viewer-connection.c \ - viewer-connection.h \ - lttng-viewer-abi.h \ - logging.c \ - logging.h - -if BABELTRACE_BUILD_WITH_MINGW -libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD = -lws2_32 -endif - -noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-lttng-live.la diff --git a/plugins/ctf/lttng-live/data-stream.c b/plugins/ctf/lttng-live/data-stream.c deleted file mode 100644 index d617c302..00000000 --- a/plugins/ctf/lttng-live/data-stream.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2019 Francis Deslauriers - * Copyright 2016 - Philippe Proulx - * Copyright 2016 - Jérémie Galarneau - * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-DS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include "../common/msg-iter/msg-iter.h" -#include - -#include "data-stream.h" - -#define STREAM_NAME_PREFIX "stream-" - -static -enum bt_msg_iter_medium_status medop_request_bytes( - size_t request_sz, uint8_t **buffer_addr, - size_t *buffer_sz, void *data) -{ - enum bt_msg_iter_medium_status status = - BT_MSG_ITER_MEDIUM_STATUS_OK; - struct lttng_live_stream_iterator *stream = data; - struct lttng_live_trace *trace = stream->trace; - struct lttng_live_session *session = trace->session; - struct lttng_live_msg_iter *live_msg_iter = session->lttng_live_msg_iter; - uint64_t recv_len = 0; - uint64_t len_left; - uint64_t read_len; - - len_left = stream->base_offset + stream->len - stream->offset; - if (!len_left) { - stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; - status = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; - return status; - } - read_len = MIN(request_sz, stream->buflen); - read_len = MIN(read_len, len_left); - status = lttng_live_get_stream_bytes(live_msg_iter, - stream, stream->buf, stream->offset, - read_len, &recv_len); - *buffer_addr = stream->buf; - *buffer_sz = recv_len; - stream->offset += recv_len; - return status; -} - -static -bt_stream *medop_borrow_stream(bt_stream_class *stream_class, - int64_t stream_id, void *data) -{ - struct lttng_live_stream_iterator *lttng_live_stream = data; - - if (!lttng_live_stream->stream) { - uint64_t stream_class_id = bt_stream_class_get_id(stream_class); - - BT_LOGD("Creating stream %s (ID: %" PRIu64 ") out of stream " - "class %" PRId64, lttng_live_stream->name->str, - stream_id, stream_class_id); - - if (stream_id < 0) { - /* - * No stream instance ID in the stream. It's possible - * to encounter this situation with older version of - * LTTng. In these cases, use the viewer_stream_id that - * is unique for a live viewer session. - */ - lttng_live_stream->stream = bt_stream_create_with_id( - stream_class, lttng_live_stream->trace->trace, - lttng_live_stream->viewer_stream_id); - } else { - lttng_live_stream->stream = bt_stream_create_with_id( - stream_class, lttng_live_stream->trace->trace, - (uint64_t) stream_id); - } - - if (!lttng_live_stream->stream) { - BT_LOGE("Cannot create stream %s (stream class ID " - "%" PRId64 ", stream ID %" PRIu64 ")", - lttng_live_stream->name->str, - stream_class_id, stream_id); - } - bt_stream_set_name(lttng_live_stream->stream, - lttng_live_stream->name->str); - } - - return lttng_live_stream->stream; -} - -static struct bt_msg_iter_medium_ops medops = { - .request_bytes = medop_request_bytes, - .seek = NULL, - .borrow_stream = medop_borrow_stream, -}; - -BT_HIDDEN -enum lttng_live_iterator_status lttng_live_lazy_msg_init( - struct lttng_live_session *session) -{ - struct lttng_live_component *lttng_live = - session->lttng_live_msg_iter->lttng_live_comp; - uint64_t trace_idx, stream_iter_idx; - - if (!session->lazy_stream_msg_init) { - return LTTNG_LIVE_ITERATOR_STATUS_OK; - } - - for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - - for (stream_iter_idx = 0; - stream_iter_idx < trace->stream_iterators->len; - stream_iter_idx++) { - struct ctf_trace_class *ctf_tc; - struct lttng_live_stream_iterator *stream_iter = - g_ptr_array_index(trace->stream_iterators, - stream_iter_idx); - - if (stream_iter->msg_iter) { - continue; - } - ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class( - trace->metadata->decoder); - stream_iter->msg_iter = bt_msg_iter_create(ctf_tc, - lttng_live->max_query_size, medops, - stream_iter); - if (!stream_iter->msg_iter) { - goto error; - } - } - } - - session->lazy_stream_msg_init = false; - - return LTTNG_LIVE_ITERATOR_STATUS_OK; - -error: - return LTTNG_LIVE_ITERATOR_STATUS_ERROR; -} - -BT_HIDDEN -struct lttng_live_stream_iterator *lttng_live_stream_iterator_create( - struct lttng_live_session *session, - uint64_t ctf_trace_id, - uint64_t stream_id) -{ - struct lttng_live_stream_iterator *stream_iter; - struct lttng_live_component *lttng_live; - struct lttng_live_trace *trace; - - BT_ASSERT(session); - BT_ASSERT(session->lttng_live_msg_iter); - BT_ASSERT(session->lttng_live_msg_iter->lttng_live_comp); - - lttng_live = session->lttng_live_msg_iter->lttng_live_comp; - - stream_iter = g_new0(struct lttng_live_stream_iterator, 1); - if (!stream_iter) { - goto error; - } - - trace = lttng_live_borrow_trace(session, ctf_trace_id); - if (!trace) { - goto error; - } - - stream_iter->trace = trace; - stream_iter->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; - stream_iter->viewer_stream_id = stream_id; - stream_iter->ctf_stream_class_id = -1ULL; - stream_iter->last_inactivity_ts = INT64_MIN; - - if (trace->trace) { - struct ctf_trace_class *ctf_tc = - ctf_metadata_decoder_borrow_ctf_trace_class( - trace->metadata->decoder); - BT_ASSERT(!stream_iter->msg_iter); - stream_iter->msg_iter = bt_msg_iter_create(ctf_tc, - lttng_live->max_query_size, medops, - stream_iter); - if (!stream_iter->msg_iter) { - goto error; - } - } - stream_iter->buf = g_new0(uint8_t, lttng_live->max_query_size); - if (!stream_iter->buf) { - goto error; - } - - stream_iter->buflen = lttng_live->max_query_size; - stream_iter->name = g_string_new(NULL); - if (!stream_iter->name) { - goto error; - } - - g_string_printf(stream_iter->name, STREAM_NAME_PREFIX "%" PRIu64, - stream_iter->viewer_stream_id); - g_ptr_array_add(trace->stream_iterators, stream_iter); - - /* Track the number of active stream iterator. */ - session->lttng_live_msg_iter->active_stream_iter++; - - goto end; -error: - lttng_live_stream_iterator_destroy(stream_iter); - stream_iter = NULL; -end: - return stream_iter; -} - -BT_HIDDEN -void lttng_live_stream_iterator_destroy( - struct lttng_live_stream_iterator *stream_iter) -{ - if (!stream_iter) { - return; - } - - if (stream_iter->stream) { - BT_STREAM_PUT_REF_AND_RESET(stream_iter->stream); - } - - if (stream_iter->msg_iter) { - bt_msg_iter_destroy(stream_iter->msg_iter); - } - if (stream_iter->buf) { - g_free(stream_iter->buf); - } - if (stream_iter->name) { - g_string_free(stream_iter->name, TRUE); - } - - bt_message_put_ref(stream_iter->current_msg); - - /* Track the number of active stream iterator. */ - stream_iter->trace->session->lttng_live_msg_iter->active_stream_iter--; - - /* - * Ensure we poke the trace metadata in the future, which is - * required to release the metadata reference on the trace. - */ - stream_iter->trace->new_metadata_needed = true; - g_free(stream_iter); -} diff --git a/plugins/ctf/lttng-live/data-stream.h b/plugins/ctf/lttng-live/data-stream.h deleted file mode 100644 index 8ae5e59e..00000000 --- a/plugins/ctf/lttng-live/data-stream.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef LTTNG_LIVE_DATA_STREAM_H -#define LTTNG_LIVE_DATA_STREAM_H - -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#include "lttng-live.h" -#include "../common/msg-iter/msg-iter.h" - -enum lttng_live_iterator_status lttng_live_lazy_msg_init( - struct lttng_live_session *session); - -struct lttng_live_stream_iterator *lttng_live_stream_iterator_create( - struct lttng_live_session *session, - uint64_t ctf_trace_id, - uint64_t stream_id); - -void lttng_live_stream_iterator_destroy( - struct lttng_live_stream_iterator *stream); - -#endif /* LTTNG_LIVE_DATA_STREAM_H */ diff --git a/plugins/ctf/lttng-live/logging.c b/plugins/ctf/lttng-live/logging.c deleted file mode 100644 index bb081590..00000000 --- a/plugins/ctf/lttng-live/logging.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL lttng_live_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(lttng_live_log_level, "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL"); diff --git a/plugins/ctf/lttng-live/logging.h b/plugins/ctf/lttng-live/logging.h deleted file mode 100644 index cd9b071d..00000000 --- a/plugins/ctf/lttng-live/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef LTTNG_LIVE_LOGGING_H -#define LTTNG_LIVE_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL lttng_live_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(lttng_live_log_level); - -#endif /* LTTNG_LIVE_LOGGING_H */ diff --git a/plugins/ctf/lttng-live/lttng-live.c b/plugins/ctf/lttng-live/lttng-live.c deleted file mode 100644 index 24544a30..00000000 --- a/plugins/ctf/lttng-live/lttng-live.c +++ /dev/null @@ -1,1601 +0,0 @@ -/* - * lttng-live.c - * - * Babeltrace CTF LTTng-live Client Component - * - * Copyright 2019 Francis Deslauriers - * Copyright 2016 Jérémie Galarneau - * Copyright 2016 Mathieu Desnoyers - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC" -#include "logging.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "data-stream.h" -#include "metadata.h" -#include "lttng-live.h" - -#define MAX_QUERY_SIZE (256*1024) -#define URL_PARAM "url" -#define SESS_NOT_FOUND_ACTION_PARAM "session-not-found-action" -#define SESS_NOT_FOUND_ACTION_CONTINUE_STR "continue" -#define SESS_NOT_FOUND_ACTION_FAIL_STR "fail" -#define SESS_NOT_FOUND_ACTION_END_STR "end" - -#define print_dbg(fmt, ...) BT_LOGD(fmt, ## __VA_ARGS__) - -static -const char *print_live_iterator_status(enum lttng_live_iterator_status status) -{ - switch (status) { - case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE: - return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE"; - case LTTNG_LIVE_ITERATOR_STATUS_AGAIN: - return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN"; - case LTTNG_LIVE_ITERATOR_STATUS_END: - return "LTTNG_LIVE_ITERATOR_STATUS_END"; - case LTTNG_LIVE_ITERATOR_STATUS_OK: - return "LTTNG_LIVE_ITERATOR_STATUS_OK"; - case LTTNG_LIVE_ITERATOR_STATUS_INVAL: - return "LTTNG_LIVE_ITERATOR_STATUS_INVAL"; - case LTTNG_LIVE_ITERATOR_STATUS_ERROR: - return "LTTNG_LIVE_ITERATOR_STATUS_ERROR"; - case LTTNG_LIVE_ITERATOR_STATUS_NOMEM: - return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM"; - case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED: - return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED"; - default: - abort(); - } -} - -static -const char *print_state(struct lttng_live_stream_iterator *s) -{ - switch (s->state) { - case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA: - return "ACTIVE_NO_DATA"; - case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA: - return "QUIESCENT_NO_DATA"; - case LTTNG_LIVE_STREAM_QUIESCENT: - return "QUIESCENT"; - case LTTNG_LIVE_STREAM_ACTIVE_DATA: - return "ACTIVE_DATA"; - case LTTNG_LIVE_STREAM_EOF: - return "EOF"; - default: - return "ERROR"; - } -} - -#define print_stream_state(live_stream_iter) \ - do { \ - BT_LOGD("stream state %s last_inact_ts %" PRId64 \ - ", curr_inact_ts %" PRId64, \ - print_state(live_stream_iter), \ - live_stream_iter->last_inactivity_ts, \ - live_stream_iter->current_inactivity_ts); \ - } while (0); - -BT_HIDDEN -bool lttng_live_graph_is_canceled(struct lttng_live_component *lttng_live) -{ - const bt_component *component; - bool ret; - - if (!lttng_live) { - ret = false; - goto end; - } - - component = bt_component_source_as_component_const( - bt_self_component_source_as_component_source( - lttng_live->self_comp)); - - ret = bt_component_graph_is_canceled(component); - -end: - return ret; -} - -static -struct lttng_live_trace *lttng_live_find_trace(struct lttng_live_session *session, - uint64_t trace_id) -{ - uint64_t trace_idx; - struct lttng_live_trace *ret_trace = NULL; - - for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - if (trace->id == trace_id) { - ret_trace = trace; - goto end; - } - } - -end: - return ret_trace; -} - -static -void lttng_live_destroy_trace(struct lttng_live_trace *trace) -{ - BT_LOGD("Destroy lttng_live_trace"); - - BT_ASSERT(trace->stream_iterators); - g_ptr_array_free(trace->stream_iterators, TRUE); - - BT_TRACE_PUT_REF_AND_RESET(trace->trace); - BT_TRACE_CLASS_PUT_REF_AND_RESET(trace->trace_class); - - lttng_live_metadata_fini(trace); - g_free(trace); -} - -static -struct lttng_live_trace *lttng_live_create_trace(struct lttng_live_session *session, - uint64_t trace_id) -{ - struct lttng_live_trace *trace = NULL; - - trace = g_new0(struct lttng_live_trace, 1); - if (!trace) { - goto error; - } - trace->session = session; - trace->id = trace_id; - trace->trace_class = NULL; - trace->trace = NULL; - trace->stream_iterators = g_ptr_array_new_with_free_func( - (GDestroyNotify) lttng_live_stream_iterator_destroy); - BT_ASSERT(trace->stream_iterators); - trace->new_metadata_needed = true; - g_ptr_array_add(session->traces, trace); - - BT_LOGI("Create trace"); - goto end; -error: - g_free(trace); - trace = NULL; -end: - return trace; -} - -BT_HIDDEN -struct lttng_live_trace *lttng_live_borrow_trace( - struct lttng_live_session *session, uint64_t trace_id) -{ - struct lttng_live_trace *trace; - - trace = lttng_live_find_trace(session, trace_id); - if (trace) { - goto end; - } - - /* The session is the owner of the newly created trace. */ - trace = lttng_live_create_trace(session, trace_id); - -end: - return trace; -} - -BT_HIDDEN -int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, - uint64_t session_id, const char *hostname, - const char *session_name) -{ - int ret = 0; - struct lttng_live_session *session; - - session = g_new0(struct lttng_live_session, 1); - if (!session) { - goto error; - } - - session->id = session_id; - session->traces = g_ptr_array_new_with_free_func( - (GDestroyNotify) lttng_live_destroy_trace); - BT_ASSERT(session->traces); - session->lttng_live_msg_iter = lttng_live_msg_iter; - session->new_streams_needed = true; - session->hostname = g_string_new(hostname); - BT_ASSERT(session->hostname); - - session->session_name = g_string_new(session_name); - BT_ASSERT(session->session_name); - - BT_LOGI("Reading from session: %" PRIu64 " hostname: %s session_name: %s", - session->id, hostname, session_name); - g_ptr_array_add(lttng_live_msg_iter->sessions, session); - goto end; -error: - BT_LOGE("Error adding session"); - g_free(session); - ret = -1; -end: - return ret; -} - -static -void lttng_live_destroy_session(struct lttng_live_session *session) -{ - struct lttng_live_component *live_comp; - - if (!session) { - goto end; - } - - BT_LOGD("Destroy lttng live session"); - if (session->id != -1ULL) { - if (lttng_live_detach_session(session)) { - live_comp = session->lttng_live_msg_iter->lttng_live_comp; - if (session->lttng_live_msg_iter && - !lttng_live_graph_is_canceled(live_comp)) { - /* Old relayd cannot detach sessions. */ - BT_LOGD("Unable to detach lttng live session %" PRIu64, - session->id); - } - } - session->id = -1ULL; - } - - if (session->traces) { - g_ptr_array_free(session->traces, TRUE); - } - - if (session->hostname) { - g_string_free(session->hostname, TRUE); - } - if (session->session_name) { - g_string_free(session->session_name, TRUE); - } - g_free(session); - -end: - return; -} - -static -void lttng_live_msg_iter_destroy(struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - if (!lttng_live_msg_iter) { - goto end; - } - - if (lttng_live_msg_iter->sessions) { - g_ptr_array_free(lttng_live_msg_iter->sessions, TRUE); - } - - BT_OBJECT_PUT_REF_AND_RESET(lttng_live_msg_iter->viewer_connection); - BT_ASSERT(lttng_live_msg_iter->lttng_live_comp); - BT_ASSERT(lttng_live_msg_iter->lttng_live_comp->has_msg_iter); - - /* All stream iterators must be destroyed at this point. */ - BT_ASSERT(lttng_live_msg_iter->active_stream_iter == 0); - lttng_live_msg_iter->lttng_live_comp->has_msg_iter = false; - - g_free(lttng_live_msg_iter); - -end: - return; -} - -BT_HIDDEN -void lttng_live_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) -{ - struct lttng_live_msg_iter *lttng_live_msg_iter; - - BT_ASSERT(self_msg_iter); - - lttng_live_msg_iter = bt_self_message_iterator_get_data(self_msg_iter); - BT_ASSERT(lttng_live_msg_iter); - lttng_live_msg_iter_destroy(lttng_live_msg_iter); -} - -static -enum lttng_live_iterator_status lttng_live_iterator_next_check_stream_state( - struct lttng_live_stream_iterator *lttng_live_stream) -{ - switch (lttng_live_stream->state) { - case LTTNG_LIVE_STREAM_QUIESCENT: - case LTTNG_LIVE_STREAM_ACTIVE_DATA: - break; - case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA: - /* Invalid state. */ - BT_LOGF("Unexpected stream state \"ACTIVE_NO_DATA\""); - abort(); - case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA: - /* Invalid state. */ - BT_LOGF("Unexpected stream state \"QUIESCENT_NO_DATA\""); - abort(); - case LTTNG_LIVE_STREAM_EOF: - break; - } - return LTTNG_LIVE_ITERATOR_STATUS_OK; -} - -/* - * For active no data stream, fetch next data. It can be either: - * - quiescent: need to put it in the prio heap at quiescent end - * timestamp, - * - have data: need to wire up first event into the prio heap, - * - have no data on this stream at this point: need to retry (AGAIN) or - * return EOF. - */ -static -enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_data_stream( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *lttng_live_stream) -{ - enum lttng_live_iterator_status ret = - LTTNG_LIVE_ITERATOR_STATUS_OK; - struct packet_index index; - enum lttng_live_stream_state orig_state = lttng_live_stream->state; - - if (lttng_live_stream->trace->new_metadata_needed) { - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - goto end; - } - if (lttng_live_stream->trace->session->new_streams_needed) { - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - goto end; - } - if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_NO_DATA && - lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA) { - goto end; - } - ret = lttng_live_get_next_index(lttng_live_msg_iter, lttng_live_stream, - &index); - if (ret != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - BT_ASSERT(lttng_live_stream->state != LTTNG_LIVE_STREAM_EOF); - if (lttng_live_stream->state == LTTNG_LIVE_STREAM_QUIESCENT) { - uint64_t last_inact_ts = lttng_live_stream->last_inactivity_ts, - curr_inact_ts = lttng_live_stream->current_inactivity_ts; - - if (orig_state == LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA && - last_inact_ts == curr_inact_ts) { - ret = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - print_stream_state(lttng_live_stream); - } else { - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - } - goto end; - } - lttng_live_stream->base_offset = index.offset; - lttng_live_stream->offset = index.offset; - lttng_live_stream->len = index.packet_size / CHAR_BIT; -end: - if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK) { - ret = lttng_live_iterator_next_check_stream_state(lttng_live_stream); - } - return ret; -} - -/* - * Creation of the message requires the ctf trace class to be created - * beforehand, but the live protocol gives us all streams (including metadata) - * at once. So we split it in three steps: getting streams, getting metadata - * (which creates the ctf trace class), and then creating the per-stream - * messages. - */ -static -enum lttng_live_iterator_status lttng_live_get_session( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_session *session) -{ - enum lttng_live_iterator_status status; - uint64_t trace_idx; - int ret = 0; - - if (!session->attached) { - ret = lttng_live_attach_session(session); - if (ret) { - if (lttng_live_msg_iter && lttng_live_graph_is_canceled( - lttng_live_msg_iter->lttng_live_comp)) { - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - } else { - status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - } - goto end; - } - } - - status = lttng_live_get_new_streams(session); - if (status != LTTNG_LIVE_ITERATOR_STATUS_OK && - status != LTTNG_LIVE_ITERATOR_STATUS_END) { - goto end; - } - for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - - status = lttng_live_metadata_update(trace); - if (status != LTTNG_LIVE_ITERATOR_STATUS_OK && - status != LTTNG_LIVE_ITERATOR_STATUS_END) { - goto end; - } - } - status = lttng_live_lazy_msg_init(session); - -end: - return status; -} - -BT_HIDDEN -void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - uint64_t session_idx; - - for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; - session_idx++) { - struct lttng_live_session *session = - g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); - session->new_streams_needed = true; - } -} - -static -void lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - uint64_t session_idx, trace_idx; - - for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; - session_idx++) { - struct lttng_live_session *session = - g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); - session->new_streams_needed = true; - for (trace_idx = 0; trace_idx < session->traces->len; - trace_idx++) { - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - trace->new_metadata_needed = true; - } - } -} - -static -enum lttng_live_iterator_status -lttng_live_iterator_handle_new_streams_and_metadata( - struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - enum lttng_live_iterator_status ret = - LTTNG_LIVE_ITERATOR_STATUS_OK; - uint64_t session_idx = 0, nr_sessions_opened = 0; - struct lttng_live_session *session; - enum session_not_found_action sess_not_found_act = - lttng_live_msg_iter->lttng_live_comp->params.sess_not_found_act; - - /* - * In a remotely distant future, we could add a "new - * session" flag to the protocol, which would tell us that we - * need to query for new sessions even though we have sessions - * currently ongoing. - */ - if (lttng_live_msg_iter->sessions->len == 0) { - if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) { - ret = LTTNG_LIVE_ITERATOR_STATUS_END; - goto end; - } else { - /* - * Retry to create a viewer session for the requested - * session name. - */ - if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { - ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - goto end; - } - } - } - - for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; - session_idx++) { - session = g_ptr_array_index(lttng_live_msg_iter->sessions, - session_idx); - ret = lttng_live_get_session(lttng_live_msg_iter, session); - switch (ret) { - case LTTNG_LIVE_ITERATOR_STATUS_OK: - break; - case LTTNG_LIVE_ITERATOR_STATUS_END: - ret = LTTNG_LIVE_ITERATOR_STATUS_OK; - break; - default: - goto end; - } - if (!session->closed) { - nr_sessions_opened++; - } - } -end: - if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK && - sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE && - nr_sessions_opened == 0) { - ret = LTTNG_LIVE_ITERATOR_STATUS_END; - } - return ret; -} - -static -enum lttng_live_iterator_status emit_inactivity_message( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream_iter, - bt_message **message, uint64_t timestamp) -{ - enum lttng_live_iterator_status ret = - LTTNG_LIVE_ITERATOR_STATUS_OK; - bt_message *msg = NULL; - - BT_ASSERT(stream_iter->trace->clock_class); - - msg = bt_message_message_iterator_inactivity_create( - lttng_live_msg_iter->self_msg_iter, - stream_iter->trace->clock_class, - timestamp); - if (!msg) { - goto error; - } - - *message = msg; -end: - return ret; - -error: - ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - bt_message_put_ref(msg); - goto end; -} - -static -enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quiescent_stream( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *lttng_live_stream, - bt_message **message) -{ - enum lttng_live_iterator_status ret = - LTTNG_LIVE_ITERATOR_STATUS_OK; - - if (lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT) { - return LTTNG_LIVE_ITERATOR_STATUS_OK; - } - - if (lttng_live_stream->current_inactivity_ts == - lttng_live_stream->last_inactivity_ts) { - lttng_live_stream->state = LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA; - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - goto end; - } - - ret = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream, - message, lttng_live_stream->current_inactivity_ts); - - lttng_live_stream->last_inactivity_ts = - lttng_live_stream->current_inactivity_ts; -end: - return ret; -} - -static -int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter, - struct lttng_live_msg_iter *lttng_live_msg_iter, - const bt_message *msg, int64_t last_msg_ts_ns, - int64_t *ts_ns) -{ - const bt_clock_class *clock_class = NULL; - const bt_clock_snapshot *clock_snapshot = NULL; - int ret = 0; - bt_message_stream_activity_clock_snapshot_state sa_cs_state; - - BT_ASSERT(msg); - BT_ASSERT(ts_ns); - - BT_LOGV("Getting message's timestamp: iter-data-addr=%p, msg-addr=%p, " - "last-msg-ts=%" PRId64, lttng_live_msg_iter, msg, - last_msg_ts_ns); - - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_EVENT: - clock_class = - bt_message_event_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - clock_class = - bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_PACKET_END: - clock_class = - bt_message_packet_end_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - clock_class = - bt_message_discarded_events_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - clock_class = - bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - clock_class = - bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - clock_class = - bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( - msg); - BT_ASSERT(clock_class); - - sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - clock_snapshot = - bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - msg); - break; - default: - /* All the other messages have a higher priority */ - BT_LOGV_STR("Message has no timestamp: using the last message timestamp."); - *ts_ns = last_msg_ts_ns; - goto end; - } - - clock_class = bt_clock_snapshot_borrow_clock_class_const(clock_snapshot); - BT_ASSERT(clock_class); - - ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns); - if (ret) { - BT_LOGE("Cannot get nanoseconds from Epoch of clock snapshot: " - "clock-snapshot-addr=%p", clock_snapshot); - goto error; - } - - goto end; - -no_clock_snapshot: - BT_LOGV_STR("Message's default clock snapshot is missing: " - "using the last message timestamp."); - *ts_ns = last_msg_ts_ns; - goto end; - -error: - ret = -1; - -end: - if (ret == 0) { - BT_LOGV("Found message's timestamp: " - "iter-data-addr=%p, msg-addr=%p, " - "last-msg-ts=%" PRId64 ", ts=%" PRId64, - lttng_live_msg_iter, msg, last_msg_ts_ns, *ts_ns); - } - - return ret; -} - -static -enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_active_data_stream( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *lttng_live_stream, - bt_message **message) -{ - enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK; - enum bt_msg_iter_status status; - uint64_t session_idx, trace_idx; - - for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; - session_idx++) { - struct lttng_live_session *session = - g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); - - if (session->new_streams_needed) { - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - goto end; - } - for (trace_idx = 0; trace_idx < session->traces->len; - trace_idx++) { - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - if (trace->new_metadata_needed) { - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - goto end; - } - } - } - - if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_DATA) { - ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - goto end; - } - - status = bt_msg_iter_get_next_message(lttng_live_stream->msg_iter, - lttng_live_msg_iter->self_msg_iter, message); - switch (status) { - case BT_MSG_ITER_STATUS_EOF: - ret = LTTNG_LIVE_ITERATOR_STATUS_END; - break; - case BT_MSG_ITER_STATUS_OK: - ret = LTTNG_LIVE_ITERATOR_STATUS_OK; - break; - case BT_MSG_ITER_STATUS_AGAIN: - /* - * Continue immediately (end of packet). The next - * get_index may return AGAIN to delay the following - * attempt. - */ - ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - break; - case BT_MSG_ITER_STATUS_INVAL: - /* No argument provided by the user, so don't return INVAL. */ - case BT_MSG_ITER_STATUS_ERROR: - default: - ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - BT_LOGW("CTF msg iterator return an error or failed msg_iter=%p", - lttng_live_stream->msg_iter); - break; - } - -end: - return ret; -} - -/* - * helper function: - * handle_no_data_streams() - * retry: - * - for each ACTIVE_NO_DATA stream: - * - query relayd for stream data, or quiescence info. - * - if need metadata, get metadata, goto retry. - * - if new stream, get new stream as ACTIVE_NO_DATA, goto retry - * - if quiescent, move to QUIESCENT streams - * - if fetched data, move to ACTIVE_DATA streams - * (at this point each stream either has data, or is quiescent) - * - * - * iterator_next: - * handle_new_streams_and_metadata() - * - query relayd for known streams, add them as ACTIVE_NO_DATA - * - query relayd for metadata - * - * call handle_active_no_data_streams() - * - * handle_quiescent_streams() - * - if at least one stream is ACTIVE_DATA: - * - peek stream event with lowest timestamp -> next_ts - * - for each quiescent stream - * - if next_ts >= quiescent end - * - set state to ACTIVE_NO_DATA - * - else - * - for each quiescent stream - * - set state to ACTIVE_NO_DATA - * - * call handle_active_no_data_streams() - * - * handle_active_data_streams() - * - if at least one stream is ACTIVE_DATA: - * - get stream event with lowest timestamp from heap - * - make that stream event the current message. - * - move this stream heap position to its next event - * - if we need to fetch data from relayd, move - * stream to ACTIVE_NO_DATA. - * - return OK - * - return AGAIN - * - * end criterion: ctrl-c on client. If relayd exits or the session - * closes on the relay daemon side, we keep on waiting for streams. - * Eventually handle --end timestamp (also an end criterion). - * - * When disconnected from relayd: try to re-connect endlessly. - */ -static -enum lttng_live_iterator_status lttng_live_iterator_next_on_stream( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream_iter, - bt_message **curr_msg) -{ - enum lttng_live_iterator_status live_status; - -retry: - print_stream_state(stream_iter); - live_status = lttng_live_iterator_handle_new_streams_and_metadata( - lttng_live_msg_iter); - if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - live_status = lttng_live_iterator_next_handle_one_no_data_stream( - lttng_live_msg_iter, stream_iter); - if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - live_status = lttng_live_iterator_next_handle_one_quiescent_stream( - lttng_live_msg_iter, stream_iter, curr_msg); - if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - BT_ASSERT(*curr_msg == NULL); - goto end; - } - if (*curr_msg) { - goto end; - } - live_status = lttng_live_iterator_next_handle_one_active_data_stream( - lttng_live_msg_iter, stream_iter, curr_msg); - if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - BT_ASSERT(*curr_msg == NULL); - } - -end: - if (live_status == LTTNG_LIVE_ITERATOR_STATUS_CONTINUE) { - goto retry; - } - - return live_status; -} - -static -enum lttng_live_iterator_status next_stream_iterator_for_trace( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_trace *live_trace, - struct lttng_live_stream_iterator **candidate_stream_iter) -{ - struct lttng_live_stream_iterator *curr_candidate_stream_iter = NULL; - enum lttng_live_iterator_status stream_iter_status;; - int64_t curr_candidate_msg_ts = INT64_MAX; - uint64_t stream_iter_idx; - - BT_ASSERT(live_trace); - BT_ASSERT(live_trace->stream_iterators); - /* - * Update the current message of every stream iterators of this trace. - * The current msg of every stream must have a timestamp equal or - * larger than the last message returned by this iterator. We must - * ensure monotonicity. - */ - stream_iter_idx = 0; - while (stream_iter_idx < live_trace->stream_iterators->len) { - bool stream_iter_is_ended = false; - struct lttng_live_stream_iterator *stream_iter = - g_ptr_array_index(live_trace->stream_iterators, - stream_iter_idx); - - /* - * Find if there is are now current message for this stream - * iterator get it. - */ - while (!stream_iter->current_msg) { - bt_message *msg = NULL; - int64_t curr_msg_ts_ns = INT64_MAX; - stream_iter_status = lttng_live_iterator_next_on_stream( - lttng_live_msg_iter, stream_iter, &msg); - - BT_LOGD("live stream iterator returned status :%s", - print_live_iterator_status(stream_iter_status)); - if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { - stream_iter_is_ended = true; - break; - } - - if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - - BT_ASSERT(msg); - - /* - * Get the timestamp in nanoseconds from origin of this - * messsage. - */ - live_get_msg_ts_ns(stream_iter, lttng_live_msg_iter, - msg, lttng_live_msg_iter->last_msg_ts_ns, - &curr_msg_ts_ns); - - /* - * Check if the message of the current live stream - * iterator occured at the exact same time or after the - * last message returned by this component's message - * iterator. If not, we return an error. - */ - if (curr_msg_ts_ns >= lttng_live_msg_iter->last_msg_ts_ns) { - stream_iter->current_msg = msg; - stream_iter->current_msg_ts_ns = curr_msg_ts_ns; - } else { - /* - * We received a message in the past. To ensure - * monotonicity, we can't send it forward. - */ - BT_LOGE("Message's timestamp is less than " - "lttng-live's message iterator's last " - "returned timestamp: " - "lttng-live-msg-iter-addr=%p, ts=%" PRId64 ", " - "last-msg-ts=%" PRId64, - lttng_live_msg_iter, curr_msg_ts_ns, - lttng_live_msg_iter->last_msg_ts_ns); - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - goto end; - } - } - - if (!stream_iter_is_ended && - stream_iter->current_msg_ts_ns <= curr_candidate_msg_ts) { - /* - * Update the current best candidate message for the - * stream iterator of thise live trace to be forwarded - * downstream. - */ - curr_candidate_msg_ts = stream_iter->current_msg_ts_ns; - curr_candidate_stream_iter = stream_iter; - } - - if (stream_iter_is_ended) { - /* - * The live stream iterator is ENDed. We remove that - * iterator from the list and we restart the iteration - * at the beginning of the live stream iterator array - * to because the removal will shuffle the array. - */ - g_ptr_array_remove_index_fast(live_trace->stream_iterators, - stream_iter_idx); - stream_iter_idx = 0; - } else { - stream_iter_idx++; - } - } - - if (curr_candidate_stream_iter) { - *candidate_stream_iter = curr_candidate_stream_iter; - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; - } else { - /* - * The only case where we don't have a candidate for this trace - * is if we reached the end of all the iterators. - */ - BT_ASSERT(live_trace->stream_iterators->len == 0); - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END; - } - -end: - return stream_iter_status; -} - -static -enum lttng_live_iterator_status next_stream_iterator_for_session( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_session *session, - struct lttng_live_stream_iterator **candidate_session_stream_iter) -{ - enum lttng_live_iterator_status stream_iter_status; - uint64_t trace_idx = 0; - int64_t curr_candidate_msg_ts = INT64_MAX; - struct lttng_live_stream_iterator *curr_candidate_stream_iter = NULL; - - /* - * Make sure we are attached to the session and look for new streams - * and metadata. - */ - stream_iter_status = lttng_live_get_session(lttng_live_msg_iter, session); - if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK && - stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_CONTINUE && - stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_END) { - goto end; - } - - BT_ASSERT(session->traces); - - /* - * Use while loops here rather then for loops so we can restart the - * iteration if an element is removed from the array during the - * looping. - */ - while (trace_idx < session->traces->len) { - bool trace_is_ended = false; - struct lttng_live_stream_iterator *stream_iter; - struct lttng_live_trace *trace = - g_ptr_array_index(session->traces, trace_idx); - - stream_iter_status = next_stream_iterator_for_trace( - lttng_live_msg_iter, trace, &stream_iter); - if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { - /* - * All the live stream iterators for this trace are - * ENDed. Remove the trace from this session. - */ - trace_is_ended = true; - } else if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - - if (!trace_is_ended) { - BT_ASSERT(stream_iter); - - if (stream_iter->current_msg_ts_ns <= curr_candidate_msg_ts) { - curr_candidate_msg_ts = stream_iter->current_msg_ts_ns; - curr_candidate_stream_iter = stream_iter; - } - trace_idx++; - } else { - g_ptr_array_remove_index_fast(session->traces, trace_idx); - trace_idx = 0; - } - } - if (curr_candidate_stream_iter) { - *candidate_session_stream_iter = curr_candidate_stream_iter; - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; - } else { - /* - * The only cases where we don't have a candidate for this - * trace is: - * 1. if we reached the end of all the iterators of all the - * traces of this session, - * 2. if we never had live stream iterator in the first place. - * - * In either cases, we return END. - */ - BT_ASSERT(session->traces->len == 0); - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END; - } -end: - return stream_iter_status; -} - -static inline -void put_messages(bt_message_array_const msgs, uint64_t count) -{ - uint64_t i; - - for (i = 0; i < count; i++) { - BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); - } -} - -BT_HIDDEN -bt_self_message_iterator_status lttng_live_msg_iter_next( - bt_self_message_iterator *self_msg_it, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status; - struct lttng_live_msg_iter *lttng_live_msg_iter = - bt_self_message_iterator_get_data(self_msg_it); - struct lttng_live_component *lttng_live = - lttng_live_msg_iter->lttng_live_comp; - enum lttng_live_iterator_status stream_iter_status; - uint64_t session_idx; - - *count = 0; - - BT_ASSERT(lttng_live_msg_iter); - - /* - * Clear all the invalid message reference that might be left over in - * the output array. - */ - memset(msgs, 0, capacity * sizeof(*msgs)); - - /* - * If no session are exposed on the relay found at the url provided by - * the user, session count will be 0. In this case, we return status - * end to return gracefully. - */ - if (lttng_live_msg_iter->sessions->len == 0) { - if (lttng_live->params.sess_not_found_act != - SESSION_NOT_FOUND_ACTION_CONTINUE) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - goto no_session; - } else { - /* - * The are no more active session for this session - * name. Retry to create a viewer session for the - * requested session name. - */ - if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto no_session; - } - } - } - - if (lttng_live_msg_iter->active_stream_iter == 0) { - lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter); - } - - /* - * Here the muxing of message is done. - * - * We need to iterate over all the streams of all the traces of all the - * viewer sessions in order to get the message with the smallest - * timestamp. In this case, a session is a viewer session and there is - * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or - * kernel). Each viewer session can have multiple traces, for example, - * 64bit UST viewer sessions could have multiple per-pid traces. - * - * We iterate over the streams of each traces to update and see what is - * their next message's timestamp. From those timestamps, we select the - * message with the smallest timestamp as the best candidate message - * for that trace and do the same thing across all the sessions. - * - * We then compare the timestamp of best candidate message of all the - * sessions to pick the message with the smallest timestamp and we - * return it. - */ - while (*count < capacity) { - struct lttng_live_stream_iterator *next_stream_iter = NULL, - *candidate_stream_iter = NULL; - int64_t next_msg_ts_ns = INT64_MAX; - - BT_ASSERT(lttng_live_msg_iter->sessions); - session_idx = 0; - /* - * Use a while loop instead of a for loop so we can restart the - * iteration if we remove an element. We can safely call - * next_stream_iterator_for_session() multiple times on the - * same session as we only fetch a new message if there is no - * current next message for each live stream iterator. - * If all live stream iterator of that session already have a - * current next message, the function will simply exit return - * the same candidate live stream iterator every time. - */ - while (session_idx < lttng_live_msg_iter->sessions->len) { - struct lttng_live_session *session = - g_ptr_array_index(lttng_live_msg_iter->sessions, - session_idx); - - /* Find the best candidate message to send downstream. */ - stream_iter_status = next_stream_iterator_for_session( - lttng_live_msg_iter, session, - &candidate_stream_iter); - - /* If we receive an END status, it means that either: - * - Those traces never had active streams (UST with no - * data produced yet), - * - All live stream iterators have ENDed.*/ - if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { - if (session->closed && session->traces->len == 0) { - /* - * Remove the session from the list and restart the - * iteration at the beginning of the array since the - * removal shuffle the elements of the array. - */ - g_ptr_array_remove_index_fast( - lttng_live_msg_iter->sessions, - session_idx); - session_idx = 0; - } else { - session_idx++; - } - continue; - } - - if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { - goto end; - } - - if (candidate_stream_iter->current_msg_ts_ns <= next_msg_ts_ns) { - next_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns; - next_stream_iter = candidate_stream_iter; - } - - session_idx++; - } - - if (!next_stream_iter) { - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - goto end; - } - - BT_ASSERT(next_stream_iter->current_msg); - /* Ensure monotonicity. */ - BT_ASSERT(lttng_live_msg_iter->last_msg_ts_ns <= - next_stream_iter->current_msg_ts_ns); - - /* - * Insert the next message to the message batch. This will set - * stream iterator current messsage to NULL so that next time - * we fetch the next message of that stream iterator - */ - BT_MESSAGE_MOVE_REF(msgs[*count], next_stream_iter->current_msg); - (*count)++; - - /* Update the last timestamp in nanoseconds sent downstream. */ - lttng_live_msg_iter->last_msg_ts_ns = next_msg_ts_ns; - next_stream_iter->current_msg_ts_ns = INT64_MAX; - - stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; - } -end: - switch (stream_iter_status) { - case LTTNG_LIVE_ITERATOR_STATUS_OK: - case LTTNG_LIVE_ITERATOR_STATUS_AGAIN: - if (*count > 0) { - /* - * We received a again status but we have some messages - * to send downstream. We send them and return OK for - * now. On the next call we return again if there are - * still no new message to send. - */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - } else { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; - } - break; - case LTTNG_LIVE_ITERATOR_STATUS_END: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - break; - case LTTNG_LIVE_ITERATOR_STATUS_NOMEM: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - break; - case LTTNG_LIVE_ITERATOR_STATUS_ERROR: - case LTTNG_LIVE_ITERATOR_STATUS_INVAL: - case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - /* Put all existing messages on error. */ - put_messages(msgs, *count); - break; - default: - abort(); - } - -no_session: - return status; -} - -BT_HIDDEN -bt_self_message_iterator_status lttng_live_msg_iter_init( - bt_self_message_iterator *self_msg_it, - bt_self_component_source *self_comp_src, - bt_self_component_port_output *self_port) -{ - bt_self_message_iterator_status ret = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - bt_self_component *self_comp = - bt_self_component_source_as_self_component(self_comp_src); - struct lttng_live_component *lttng_live; - struct lttng_live_msg_iter *lttng_live_msg_iter; - - BT_ASSERT(self_msg_it); - - lttng_live = bt_self_component_get_data(self_comp); - - /* There can be only one downstream iterator at the same time. */ - BT_ASSERT(!lttng_live->has_msg_iter); - lttng_live->has_msg_iter = true; - - lttng_live_msg_iter = g_new0(struct lttng_live_msg_iter, 1); - if (!lttng_live_msg_iter) { - ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - lttng_live_msg_iter->lttng_live_comp = lttng_live; - lttng_live_msg_iter->self_msg_iter = self_msg_it; - - lttng_live_msg_iter->active_stream_iter = 0; - lttng_live_msg_iter->last_msg_ts_ns = INT64_MIN; - lttng_live_msg_iter->sessions = g_ptr_array_new_with_free_func( - (GDestroyNotify) lttng_live_destroy_session); - BT_ASSERT(lttng_live_msg_iter->sessions); - - lttng_live_msg_iter->viewer_connection = - live_viewer_connection_create(lttng_live->params.url->str, false, - lttng_live_msg_iter); - if (!lttng_live_msg_iter->viewer_connection) { - goto error; - } - - if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { - goto error; - } - if (lttng_live_msg_iter->sessions->len == 0) { - switch (lttng_live->params.sess_not_found_act) { - case SESSION_NOT_FOUND_ACTION_CONTINUE: - BT_LOGI("Unable to connect to the requested live viewer " - "session. Keep trying to connect because of " - "%s=\"%s\" component parameter: url=\"%s\"", - SESS_NOT_FOUND_ACTION_PARAM, - SESS_NOT_FOUND_ACTION_CONTINUE_STR, - lttng_live->params.url->str); - break; - case SESSION_NOT_FOUND_ACTION_FAIL: - BT_LOGE("Unable to connect to the requested live viewer " - "session. Fail the message iterator" - "initialization because of %s=\"%s\" " - "component parameter: url =\"%s\"", - SESS_NOT_FOUND_ACTION_PARAM, - SESS_NOT_FOUND_ACTION_FAIL_STR, - lttng_live->params.url->str); - goto error; - case SESSION_NOT_FOUND_ACTION_END: - BT_LOGI("Unable to connect to the requested live viewer " - "session. End gracefully at the first _next() " - "call because of %s=\"%s\" component parameter: " - "url=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM, - SESS_NOT_FOUND_ACTION_END_STR, - lttng_live->params.url->str); - break; - } - } - - bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter); - - goto end; -error: - ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - lttng_live_msg_iter_destroy(lttng_live_msg_iter); -end: - return ret; -} - -static -bt_query_status lttng_live_query_list_sessions(const bt_value *params, - const bt_value **result) -{ - bt_query_status status = BT_QUERY_STATUS_OK; - const bt_value *url_value = NULL; - const char *url; - struct live_viewer_connection *viewer_connection = NULL; - - url_value = bt_value_map_borrow_entry_value_const(params, URL_PARAM); - if (!url_value) { - BT_LOGW("Mandatory `%s` parameter missing", URL_PARAM); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - if (!bt_value_is_string(url_value)) { - BT_LOGW("`%s` parameter is required to be a string value", - URL_PARAM); - status = BT_QUERY_STATUS_INVALID_PARAMS; - goto error; - } - - url = bt_value_string_get(url_value); - - viewer_connection = live_viewer_connection_create(url, true, NULL); - if (!viewer_connection) { - goto error; - } - - status = live_viewer_connection_list_sessions(viewer_connection, - result); - if (status != BT_QUERY_STATUS_OK) { - goto error; - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(*result); - - if (status >= 0) { - status = BT_QUERY_STATUS_ERROR; - } - -end: - if (viewer_connection) { - live_viewer_connection_destroy(viewer_connection); - } - return status; -} - -BT_HIDDEN -bt_query_status lttng_live_query(bt_self_component_class_source *comp_class, - const bt_query_executor *query_exec, - const char *object, const bt_value *params, - const bt_value **result) -{ - bt_query_status status = BT_QUERY_STATUS_OK; - - if (strcmp(object, "sessions") == 0) { - status = lttng_live_query_list_sessions(params, result); - } else { - BT_LOGW("Unknown query object `%s`", object); - status = BT_QUERY_STATUS_INVALID_OBJECT; - goto end; - } - -end: - return status; -} - -static -void lttng_live_component_destroy_data(struct lttng_live_component *lttng_live) -{ - if (!lttng_live) { - return; - } - if (lttng_live->params.url) { - g_string_free(lttng_live->params.url, TRUE); - } - g_free(lttng_live); -} - -BT_HIDDEN -void lttng_live_component_finalize(bt_self_component_source *component) -{ - void *data = bt_self_component_get_data( - bt_self_component_source_as_self_component(component)); - - if (!data) { - return; - } - lttng_live_component_destroy_data(data); -} - -static -enum session_not_found_action parse_session_not_found_action_param( - const bt_value *no_session_param) -{ - enum session_not_found_action action; - const char *no_session_act_str; - no_session_act_str = bt_value_string_get(no_session_param); - if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_CONTINUE_STR) == 0) { - action = SESSION_NOT_FOUND_ACTION_CONTINUE; - } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_FAIL_STR) == 0) { - action = SESSION_NOT_FOUND_ACTION_FAIL; - } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_END_STR) == 0) { - action = SESSION_NOT_FOUND_ACTION_END; - } else { - action = -1; - } - - return action; -} - -struct lttng_live_component *lttng_live_component_create(const bt_value *params) -{ - struct lttng_live_component *lttng_live; - const bt_value *value = NULL; - const char *url; - - lttng_live = g_new0(struct lttng_live_component, 1); - if (!lttng_live) { - goto end; - } - lttng_live->max_query_size = MAX_QUERY_SIZE; - lttng_live->has_msg_iter = false; - - value = bt_value_map_borrow_entry_value_const(params, URL_PARAM); - if (!value || !bt_value_is_string(value)) { - BT_LOGW("Mandatory `%s` parameter missing or not a string", - URL_PARAM); - goto error; - } - url = bt_value_string_get(value); - lttng_live->params.url = g_string_new(url); - if (!lttng_live->params.url) { - goto error; - } - - value = bt_value_map_borrow_entry_value_const(params, - SESS_NOT_FOUND_ACTION_PARAM); - - if (value && bt_value_is_string(value)) { - lttng_live->params.sess_not_found_act = - parse_session_not_found_action_param(value); - if (lttng_live->params.sess_not_found_act == -1) { - BT_LOGE("Unexpected value for `%s` parameter: " - "value=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM, - bt_value_string_get(value)); - goto error; - } - } else { - BT_LOGW("Optional `%s` parameter is missing or " - "not a string value. Defaulting to %s=\"%s\".", - SESS_NOT_FOUND_ACTION_PARAM, - SESS_NOT_FOUND_ACTION_PARAM, - SESS_NOT_FOUND_ACTION_CONTINUE_STR); - lttng_live->params.sess_not_found_act = - SESSION_NOT_FOUND_ACTION_CONTINUE; - } - - goto end; - -error: - lttng_live_component_destroy_data(lttng_live); - lttng_live = NULL; -end: - return lttng_live; -} - -BT_HIDDEN -bt_self_component_status lttng_live_component_init( - bt_self_component_source *self_comp, - const bt_value *params, UNUSED_VAR void *init_method_data) -{ - struct lttng_live_component *lttng_live; - bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; - - lttng_live = lttng_live_component_create(params); - if (!lttng_live) { - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto error; - } - lttng_live->self_comp = self_comp; - - if (lttng_live_graph_is_canceled(lttng_live)) { - ret = BT_SELF_COMPONENT_STATUS_END; - goto error; - } - - ret = bt_self_component_source_add_output_port( - lttng_live->self_comp, "out", - NULL, NULL); - if (ret != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - bt_self_component_set_data( - bt_self_component_source_as_self_component(self_comp), - lttng_live); - goto end; - -error: - lttng_live_component_destroy_data(lttng_live); - lttng_live = NULL; -end: - return ret; -} diff --git a/plugins/ctf/lttng-live/lttng-live.h b/plugins/ctf/lttng-live/lttng-live.h deleted file mode 100644 index 67f021c6..00000000 --- a/plugins/ctf/lttng-live/lttng-live.h +++ /dev/null @@ -1,300 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H -#define BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H - -/* - * BabelTrace - LTTng-live client Component - * - * Copyright 2019 Francis Deslauriers - * Copyright 2016 Jérémie Galarneau - * Copyright 2016 Mathieu Desnoyers - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include -#include - -#include "../common/metadata/decoder.h" -#include "../common/msg-iter/msg-iter.h" - -#include "viewer-connection.h" - -struct lttng_live_component; -struct lttng_live_session; -struct lttng_live_msg_iter; - -enum lttng_live_stream_state { - /* This stream won't have data until some known time in the future. */ - LTTNG_LIVE_STREAM_QUIESCENT, - /* - * This stream won't have data until some known time in the future and - * the message iterator inactivity message was already sent downstream. - */ - LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA, /* */ - /* This stream has data ready to be consumed. */ - LTTNG_LIVE_STREAM_ACTIVE_DATA, - /* - * This stream has no data left to consume. We should asked the relay - * for more. - */ - LTTNG_LIVE_STREAM_ACTIVE_NO_DATA, - /* This stream won't have anymore data, ever. */ - LTTNG_LIVE_STREAM_EOF, -}; - -/* Iterator over a live stream. */ -struct lttng_live_stream_iterator { - /* Owned by this. */ - bt_stream *stream; - - /* Weak reference. */ - struct lttng_live_trace *trace; - - /* - * Since only a single iterator per viewer connection, we have - * only a single message iterator per stream. - */ - struct bt_msg_iter *msg_iter; - - uint64_t viewer_stream_id; - - uint64_t ctf_stream_class_id; - - /* base offset in current index. */ - uint64_t base_offset; - /* len to read in current index. */ - uint64_t len; - /* offset in current index. */ - uint64_t offset; - - /* - * Clock Snapshot value of the last message iterator inactivity message - * sent downstream. - */ - uint64_t last_inactivity_ts; - - /* - * Clock Snapshot value of the current message iterator inactivity - * message we might want to send downstream. - */ - uint64_t current_inactivity_ts; - - enum lttng_live_stream_state state; - - /* - * The current message produced by this live stream iterator. Owned by - * this. - */ - bt_message *current_msg; - - /* Timestamp in nanoseconds of the current message (current_msg). */ - int64_t current_msg_ts_ns; - - /* Owned by this. */ - uint8_t *buf; - size_t buflen; - - /* Owned by this. */ - GString *name; -}; - -struct lttng_live_metadata { - /* Weak reference. */ - struct lttng_live_trace *trace; - - uint64_t stream_id; - /* Weak reference. */ - struct ctf_metadata_decoder *decoder; - - bool closed; -}; - -struct lttng_live_trace { - /* Back reference to session. */ - struct lttng_live_session *session; - - /* ctf trace ID within the session. */ - uint64_t id; - - /* Owned by this. */ - bt_trace *trace; - - /* Weak reference. */ - bt_trace_class *trace_class; - - struct lttng_live_metadata *metadata; - - const bt_clock_class *clock_class; - - /* Array of pointers to struct lttng_live_stream_iterator. */ - /* Owned by this. */ - GPtrArray *stream_iterators; - - bool new_metadata_needed; -}; - -struct lttng_live_session { - /* Weak reference. */ - struct lttng_live_msg_iter *lttng_live_msg_iter; - - /* Owned by this. */ - GString *hostname; - - /* Owned by this. */ - GString *session_name; - - uint64_t id; - - /* Array of pointers to struct lttng_live_trace. */ - GPtrArray *traces; - - bool attached; - bool new_streams_needed; - bool lazy_stream_msg_init; - bool closed; -}; - -enum session_not_found_action { - SESSION_NOT_FOUND_ACTION_CONTINUE, - SESSION_NOT_FOUND_ACTION_FAIL, - SESSION_NOT_FOUND_ACTION_END, -}; - -/* - * A component instance is an iterator on a single session. - */ -struct lttng_live_component { - /* Weak reference. */ - bt_self_component_source *self_comp; - - struct { - GString *url; - enum session_not_found_action sess_not_found_act; - } params; - - size_t max_query_size; - - /* - * Keeps track of whether the downstream component already has a - * message iterator on this component. - */ - bool has_msg_iter; -}; - -struct lttng_live_msg_iter { - /* Weak reference. */ - struct lttng_live_component *lttng_live_comp; - - /* Weak reference. */ - bt_self_message_iterator *self_msg_iter; - - /* Owned by this. */ - struct live_viewer_connection *viewer_connection; - - /* Array of pointers to struct lttng_live_session. */ - GPtrArray *sessions; - - /* Number of live stream iterator this message iterator has.*/ - uint64_t active_stream_iter; - - /* Timestamp in nanosecond of the last message sent downstream. */ - int64_t last_msg_ts_ns; -}; - -enum lttng_live_iterator_status { - /** Iterator state has progressed. Continue iteration immediately. */ - LTTNG_LIVE_ITERATOR_STATUS_CONTINUE = 3, - /** No message available for now. Try again later. */ - LTTNG_LIVE_ITERATOR_STATUS_AGAIN = 2, - /** No more CTF_LTTNG_LIVEs to be delivered. */ - LTTNG_LIVE_ITERATOR_STATUS_END = 1, - /** No error, okay. */ - LTTNG_LIVE_ITERATOR_STATUS_OK = 0, - /** Invalid arguments. */ - LTTNG_LIVE_ITERATOR_STATUS_INVAL = -1, - /** General error. */ - LTTNG_LIVE_ITERATOR_STATUS_ERROR = -2, - /** Out of memory. */ - LTTNG_LIVE_ITERATOR_STATUS_NOMEM = -3, - /** Unsupported iterator feature. */ - LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED = -4, -}; - -bt_self_component_status lttng_live_component_init( - bt_self_component_source *self_comp, - const bt_value *params, void *init_method_data); - -bt_query_status lttng_live_query( - bt_self_component_class_source *comp_class, - const bt_query_executor *query_exec, - const char *object, const bt_value *params, - const bt_value **result); - -void lttng_live_component_finalize(bt_self_component_source *component); - -bt_self_message_iterator_status lttng_live_msg_iter_next( - bt_self_message_iterator *iterator, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -bt_self_message_iterator_status lttng_live_msg_iter_init( - bt_self_message_iterator *self_msg_it, - bt_self_component_source *self_comp, - bt_self_component_port_output *self_port); - -void lttng_live_msg_iter_finalize(bt_self_message_iterator *it); - -int lttng_live_create_viewer_session(struct lttng_live_msg_iter *lttng_live_msg_iter); -int lttng_live_attach_session(struct lttng_live_session *session); -int lttng_live_detach_session(struct lttng_live_session *session); -enum lttng_live_iterator_status lttng_live_get_new_streams( - struct lttng_live_session *session); - -int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, - uint64_t session_id, - const char *hostname, - const char *session_name); - -ssize_t lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, - FILE *fp); -enum lttng_live_iterator_status lttng_live_get_next_index( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream, - struct packet_index *index); - -enum bt_msg_iter_medium_status lttng_live_get_stream_bytes( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream, uint8_t *buf, - uint64_t offset, uint64_t req_len, uint64_t *recv_len); -void lttng_live_add_stream_iterator(struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream_iter); -void lttng_live_remove_stream_iterator(struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream_iter); - -struct lttng_live_trace *lttng_live_borrow_trace( - struct lttng_live_session *session, uint64_t trace_id); -void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter); - -bool lttng_live_graph_is_canceled(struct lttng_live_component *lttng_live); - -#endif /* BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H */ diff --git a/plugins/ctf/lttng-live/lttng-viewer-abi.h b/plugins/ctf/lttng-live/lttng-viewer-abi.h deleted file mode 100644 index a05ed61f..00000000 --- a/plugins/ctf/lttng-live/lttng-viewer-abi.h +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef LTTNG_VIEWER_ABI_H -#define LTTNG_VIEWER_ABI_H - -/* - * Copyright (C) 2013 - Julien Desfossez - * Mathieu Desnoyers - * David Goulet - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#define LTTNG_VIEWER_PATH_MAX 4096 -#define LTTNG_VIEWER_NAME_MAX 255 -#define LTTNG_VIEWER_HOST_NAME_MAX 64 - -/* Flags in reply to get_next_index and get_packet. */ -enum { - /* New metadata is required to read this packet. */ - LTTNG_VIEWER_FLAG_NEW_METADATA = (1 << 0), - /* New stream got added to the trace. */ - LTTNG_VIEWER_FLAG_NEW_STREAM = (1 << 1), -}; - -enum lttng_viewer_command { - LTTNG_VIEWER_CONNECT = 1, - LTTNG_VIEWER_LIST_SESSIONS = 2, - LTTNG_VIEWER_ATTACH_SESSION = 3, - LTTNG_VIEWER_GET_NEXT_INDEX = 4, - LTTNG_VIEWER_GET_PACKET = 5, - LTTNG_VIEWER_GET_METADATA = 6, - LTTNG_VIEWER_GET_NEW_STREAMS = 7, - LTTNG_VIEWER_CREATE_SESSION = 8, - LTTNG_VIEWER_DETACH_SESSION = 9, -}; - -enum lttng_viewer_attach_return_code { - LTTNG_VIEWER_ATTACH_OK = 1, /* The attach command succeeded. */ - LTTNG_VIEWER_ATTACH_ALREADY = 2, /* A viewer is already attached. */ - LTTNG_VIEWER_ATTACH_UNK = 3, /* The session ID is unknown. */ - LTTNG_VIEWER_ATTACH_NOT_LIVE = 4, /* The session is not live. */ - LTTNG_VIEWER_ATTACH_SEEK_ERR = 5, /* Seek error. */ - LTTNG_VIEWER_ATTACH_NO_SESSION = 6, /* No viewer session created. */ -}; - -enum lttng_viewer_detach_session_return_code { - LTTNG_VIEWER_DETACH_SESSION_OK = 1, - LTTNG_VIEWER_DETACH_SESSION_UNK = 2, - LTTNG_VIEWER_DETACH_SESSION_ERR = 3, -}; - -enum lttng_viewer_next_index_return_code { - LTTNG_VIEWER_INDEX_OK = 1, /* Index is available. */ - LTTNG_VIEWER_INDEX_RETRY = 2, /* Index not yet available. */ - LTTNG_VIEWER_INDEX_HUP = 3, /* Index closed (trace destroyed). */ - LTTNG_VIEWER_INDEX_ERR = 4, /* Unknow error. */ - LTTNG_VIEWER_INDEX_INACTIVE = 5, /* Inactive stream beacon. */ - LTTNG_VIEWER_INDEX_EOF = 6, /* End of index file. */ -}; - -enum lttng_viewer_get_packet_return_code { - LTTNG_VIEWER_GET_PACKET_OK = 1, - LTTNG_VIEWER_GET_PACKET_RETRY = 2, - LTTNG_VIEWER_GET_PACKET_ERR = 3, - LTTNG_VIEWER_GET_PACKET_EOF = 4, -}; - -enum lttng_viewer_get_metadata_return_code { - LTTNG_VIEWER_METADATA_OK = 1, - LTTNG_VIEWER_NO_NEW_METADATA = 2, - LTTNG_VIEWER_METADATA_ERR = 3, -}; - -enum lttng_viewer_connection_type { - LTTNG_VIEWER_CLIENT_COMMAND = 1, - LTTNG_VIEWER_CLIENT_MESSAGE = 2, -}; - -enum lttng_viewer_seek { - /* Receive the trace packets from the beginning. */ - LTTNG_VIEWER_SEEK_BEGINNING = 1, - /* Receive the trace packets from now. */ - LTTNG_VIEWER_SEEK_LAST = 2, -}; - -enum lttng_viewer_new_streams_return_code { - LTTNG_VIEWER_NEW_STREAMS_OK = 1, /* If new streams are being sent. */ - LTTNG_VIEWER_NEW_STREAMS_NO_NEW = 2, /* If no new streams are available. */ - LTTNG_VIEWER_NEW_STREAMS_ERR = 3, /* Error. */ - LTTNG_VIEWER_NEW_STREAMS_HUP = 4, /* Session closed. */ -}; - -enum lttng_viewer_create_session_return_code { - LTTNG_VIEWER_CREATE_SESSION_OK = 1, - LTTNG_VIEWER_CREATE_SESSION_ERR = 2, -}; - -struct lttng_viewer_session { - uint64_t id; - uint32_t live_timer; - uint32_t clients; - uint32_t streams; - char hostname[LTTNG_VIEWER_HOST_NAME_MAX]; - char session_name[LTTNG_VIEWER_NAME_MAX]; -} __attribute__((__packed__)); - -struct lttng_viewer_stream { - uint64_t id; - uint64_t ctf_trace_id; - uint32_t metadata_flag; - char path_name[LTTNG_VIEWER_PATH_MAX]; - char channel_name[LTTNG_VIEWER_NAME_MAX]; -} __attribute__((__packed__)); - -struct lttng_viewer_cmd { - uint64_t data_size; /* data size following this header */ - uint32_t cmd; /* enum lttcomm_relayd_command */ - uint32_t cmd_version; /* command version */ -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_CONNECT payload. - */ -struct lttng_viewer_connect { - /* session ID assigned by the relay for command connections */ - uint64_t viewer_session_id; - uint32_t major; - uint32_t minor; - uint32_t type; /* enum lttng_viewer_connection_type */ -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_LIST_SESSIONS payload. - */ -struct lttng_viewer_list_sessions { - uint32_t sessions_count; - char session_list[]; /* struct lttng_viewer_session */ -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_ATTACH_SESSION payload. - */ -struct lttng_viewer_attach_session_request { - uint64_t session_id; - uint64_t offset; /* unused for now */ - uint32_t seek; /* enum lttng_viewer_seek */ -} __attribute__((__packed__)); - -struct lttng_viewer_attach_session_response { - /* enum lttng_viewer_attach_return_code */ - uint32_t status; - uint32_t streams_count; - /* struct lttng_viewer_stream */ - char stream_list[]; -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_GET_NEXT_INDEX payload. - */ -struct lttng_viewer_get_next_index { - uint64_t stream_id; -} __attribute__ ((__packed__)); - -struct lttng_viewer_index { - uint64_t offset; - uint64_t packet_size; - uint64_t content_size; - uint64_t timestamp_begin; - uint64_t timestamp_end; - uint64_t events_discarded; - uint64_t stream_id; - uint32_t status; /* enum lttng_viewer_next_index_return_code */ - uint32_t flags; /* LTTNG_VIEWER_FLAG_* */ -} __attribute__ ((__packed__)); - -/* - * LTTNG_VIEWER_GET_PACKET payload. - */ -struct lttng_viewer_get_packet { - uint64_t stream_id; - uint64_t offset; - uint32_t len; -} __attribute__((__packed__)); - -struct lttng_viewer_trace_packet { - uint32_t status; /* enum lttng_viewer_get_packet_return_code */ - uint32_t len; - uint32_t flags; /* LTTNG_VIEWER_FLAG_* */ - char data[]; -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_GET_METADATA payload. - */ -struct lttng_viewer_get_metadata { - uint64_t stream_id; -} __attribute__((__packed__)); - -struct lttng_viewer_metadata_packet { - uint64_t len; - uint32_t status; /* enum lttng_viewer_get_metadata_return_code */ - char data[]; -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_GET_NEW_STREAMS payload. - */ -struct lttng_viewer_new_streams_request { - uint64_t session_id; -} __attribute__((__packed__)); - -struct lttng_viewer_new_streams_response { - /* enum lttng_viewer_new_streams_return_code */ - uint32_t status; - uint32_t streams_count; - /* struct lttng_viewer_stream */ - char stream_list[]; -} __attribute__((__packed__)); - -struct lttng_viewer_create_session_response { - /* enum lttng_viewer_create_session_return_code */ - uint32_t status; -} __attribute__((__packed__)); - -/* - * LTTNG_VIEWER_DETACH_SESSION payload. - */ -struct lttng_viewer_detach_session_request { - uint64_t session_id; -} __attribute__((__packed__)); - -struct lttng_viewer_detach_session_response { - /* enum lttng_viewer_detach_session_return_code */ - uint32_t status; -} __attribute__((__packed__)); - -#endif /* LTTNG_VIEWER_ABI_H */ diff --git a/plugins/ctf/lttng-live/metadata.c b/plugins/ctf/lttng-live/metadata.c deleted file mode 100644 index 14088bbd..00000000 --- a/plugins/ctf/lttng-live/metadata.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2019 - Francis Deslauriers - * Copyright 2016 - Philippe Proulx - * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation - * - * Some functions are based on older functions written by Mathieu Desnoyers. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-METADATA" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "metadata.h" -#include "../common/metadata/decoder.h" - -#define TSDL_MAGIC 0x75d11d57 - -struct packet_header { - uint32_t magic; - uint8_t uuid[16]; - uint32_t checksum; - uint32_t content_size; - uint32_t packet_size; - uint8_t compression_scheme; - uint8_t encryption_scheme; - uint8_t checksum_scheme; - uint8_t major; - uint8_t minor; -} __attribute__((__packed__)); - - -static -bool stream_classes_all_have_default_clock_class(bt_trace_class *tc) -{ - uint64_t i, sc_count; - const bt_clock_class *cc = NULL; - const bt_stream_class *sc; - bool ret = true; - - sc_count = bt_trace_class_get_stream_class_count(tc); - for (i = 0; i < sc_count; i++) { - sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i); - - BT_ASSERT(sc); - - cc = bt_stream_class_borrow_default_clock_class_const(sc); - if (!cc) { - ret = false; - BT_LOGE("Stream class doesn't have a default clock class: " - "sc-id=%" PRIu64 ", sc-name=\"%s\"", - bt_stream_class_get_id(sc), - bt_stream_class_get_name(sc)); - goto end; - } - } - -end: - return ret; -} -/* - * Iterate over the stream classes and returns the first clock class - * encountered. This is useful to create message iterator inactivity message as - * we don't need a particular clock class. - */ -static -const bt_clock_class *borrow_any_clock_class(bt_trace_class *tc) -{ - uint64_t i, sc_count; - const bt_clock_class *cc = NULL; - const bt_stream_class *sc; - - sc_count = bt_trace_class_get_stream_class_count(tc); - for (i = 0; i < sc_count; i++) { - sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i); - BT_ASSERT(sc); - - cc = bt_stream_class_borrow_default_clock_class_const(sc); - if (cc) { - goto end; - } - } -end: - BT_ASSERT(cc); - return cc; -} - -BT_HIDDEN -enum lttng_live_iterator_status lttng_live_metadata_update( - struct lttng_live_trace *trace) -{ - struct lttng_live_session *session = trace->session; - struct lttng_live_metadata *metadata = trace->metadata; - struct lttng_live_component *lttng_live = - session->lttng_live_msg_iter->lttng_live_comp; - ssize_t ret = 0; - size_t size, len_read = 0; - char *metadata_buf = NULL; - FILE *fp = NULL; - enum ctf_metadata_decoder_status decoder_status; - enum lttng_live_iterator_status status = - LTTNG_LIVE_ITERATOR_STATUS_OK; - - /* No metadata stream yet. */ - if (!metadata) { - if (session->new_streams_needed) { - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - } else { - session->new_streams_needed = true; - status = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; - } - goto end; - } - - if (!metadata->trace) { - trace->new_metadata_needed = false; - } - - if (!trace->new_metadata_needed) { - goto end; - } - - /* Open for writing */ - fp = bt_open_memstream(&metadata_buf, &size); - if (!fp) { - BT_LOGE("Metadata open_memstream: %s", strerror(errno)); - goto error; - } - - /* Grab all available metadata. */ - do { - /* - * get_one_metadata_packet returns the number of bytes - * received, 0 when we have received everything, a - * negative value on error. - */ - ret = lttng_live_get_one_metadata_packet(trace, fp); - if (ret > 0) { - len_read += ret; - } - } while (ret > 0); - - /* - * Consider metadata closed as soon as we get an error reading - * it (e.g. cannot be found). - */ - if (ret < 0) { - if (!metadata->closed) { - metadata->closed = true; - /* - * Release our reference on the trace as soon as - * we know the metadata stream is not available - * anymore. This won't necessarily teardown the - * metadata objects immediately, but only when - * the data streams are done. - */ - metadata->trace = NULL; - } - if (errno == EINTR) { - if (lttng_live_graph_is_canceled(lttng_live)) { - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - goto end; - } - } - } - - if (bt_close_memstream(&metadata_buf, &size, fp)) { - BT_LOGE("bt_close_memstream: %s", strerror(errno)); - } - ret = 0; - fp = NULL; - - if (len_read == 0) { - if (!trace->trace) { - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - goto end; - } - trace->new_metadata_needed = false; - goto end; - } - - fp = bt_fmemopen(metadata_buf, len_read, "rb"); - if (!fp) { - BT_LOGE("Cannot memory-open metadata buffer: %s", - strerror(errno)); - goto error; - } - - /* - * The call to ctf_metadata_decoder_decode will append new metadata to - * our current trace class. - */ - decoder_status = ctf_metadata_decoder_decode(metadata->decoder, fp); - switch (decoder_status) { - case CTF_METADATA_DECODER_STATUS_OK: - if (!trace->trace_class) { - trace->trace_class = - ctf_metadata_decoder_get_ir_trace_class( - metadata->decoder); - trace->trace = bt_trace_create(trace->trace_class); - if (!stream_classes_all_have_default_clock_class( - trace->trace_class)) { - /* Error logged in function. */ - goto error; - } - trace->clock_class = - borrow_any_clock_class(trace->trace_class); - } - trace->new_metadata_needed = false; - - break; - case CTF_METADATA_DECODER_STATUS_INCOMPLETE: - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - break; - case CTF_METADATA_DECODER_STATUS_ERROR: - case CTF_METADATA_DECODER_STATUS_INVAL_VERSION: - case CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR: - goto error; - } - - goto end; -error: - status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; -end: - if (fp) { - int closeret; - - closeret = fclose(fp); - if (closeret) { - BT_LOGE("Error on fclose"); - } - } - free(metadata_buf); - return status; -} - -BT_HIDDEN -int lttng_live_metadata_create_stream(struct lttng_live_session *session, - uint64_t ctf_trace_id, - uint64_t stream_id, - const char *trace_name) -{ - struct lttng_live_component *lttng_live = - session->lttng_live_msg_iter->lttng_live_comp; - struct lttng_live_metadata *metadata = NULL; - struct lttng_live_trace *trace; - const char *match; - - metadata = g_new0(struct lttng_live_metadata, 1); - if (!metadata) { - return -1; - } - metadata->stream_id = stream_id; - - match = strstr(trace_name, session->session_name->str); - if (!match) { - goto error; - } - - metadata->decoder = ctf_metadata_decoder_create( - lttng_live->self_comp, NULL); - if (!metadata->decoder) { - goto error; - } - trace = lttng_live_borrow_trace(session, ctf_trace_id); - if (!trace) { - goto error; - } - metadata->trace = trace; - trace->metadata = metadata; - return 0; - -error: - ctf_metadata_decoder_destroy(metadata->decoder); - g_free(metadata); - return -1; -} - -BT_HIDDEN -void lttng_live_metadata_fini(struct lttng_live_trace *trace) -{ - struct lttng_live_metadata *metadata = trace->metadata; - - if (!metadata) { - return; - } - ctf_metadata_decoder_destroy(metadata->decoder); - trace->metadata = NULL; - g_free(metadata); -} diff --git a/plugins/ctf/lttng-live/metadata.h b/plugins/ctf/lttng-live/metadata.h deleted file mode 100644 index 2a81c884..00000000 --- a/plugins/ctf/lttng-live/metadata.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef LTTNG_LIVE_METADATA_H -#define LTTNG_LIVE_METADATA_H - -/* - * Copyright 2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include "lttng-live.h" - -int lttng_live_metadata_create_stream(struct lttng_live_session *session, - uint64_t ctf_trace_id, uint64_t stream_id, - const char *trace_name); - -enum lttng_live_iterator_status lttng_live_metadata_update( - struct lttng_live_trace *trace); - -void lttng_live_metadata_fini(struct lttng_live_trace *trace); - -#endif /* LTTNG_LIVE_METADATA_H */ diff --git a/plugins/ctf/lttng-live/viewer-connection.c b/plugins/ctf/lttng-live/viewer-connection.c deleted file mode 100644 index e11fe7eb..00000000 --- a/plugins/ctf/lttng-live/viewer-connection.c +++ /dev/null @@ -1,1524 +0,0 @@ -/* - * Copyright 2019 - Francis Deslauriers - * Copyright 2016 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-VIEWER" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "lttng-live.h" -#include "viewer-connection.h" -#include "lttng-viewer-abi.h" -#include "data-stream.h" -#include "metadata.h" - -static -ssize_t lttng_live_recv(struct live_viewer_connection *viewer_connection, - void *buf, size_t len) -{ - ssize_t ret; - size_t copied = 0, to_copy = len; - struct lttng_live_msg_iter *lttng_live_msg_iter = - viewer_connection->lttng_live_msg_iter; - BT_SOCKET sock = viewer_connection->control_sock; - - do { - ret = bt_socket_recv(sock, buf + copied, to_copy, 0); - if (ret > 0) { - BT_ASSERT(ret <= to_copy); - copied += ret; - to_copy -= ret; - } - if (ret == BT_SOCKET_ERROR && bt_socket_interrupted()) { - if (!viewer_connection->in_query && - lttng_live_graph_is_canceled(lttng_live_msg_iter->lttng_live_comp)) { - break; - } else { - continue; - } - } - } while (ret > 0 && to_copy > 0); - if (ret > 0) - ret = copied; - /* ret = 0 means orderly shutdown, ret == BT_SOCKET_ERROR is error. */ - return ret; -} - -static -ssize_t lttng_live_send(struct live_viewer_connection *viewer_connection, - const void *buf, size_t len) -{ - struct lttng_live_msg_iter *lttng_live_msg_iter = - viewer_connection->lttng_live_msg_iter; - BT_SOCKET sock = viewer_connection->control_sock; - ssize_t ret; - - for (;;) { - ret = bt_socket_send_nosigpipe(sock, buf, len); - if (ret == BT_SOCKET_ERROR && bt_socket_interrupted()) { - if (!viewer_connection->in_query && - lttng_live_graph_is_canceled(lttng_live_msg_iter->lttng_live_comp)) { - break; - } else { - continue; - } - } else { - break; - } - } - return ret; -} - -static -int parse_url(struct live_viewer_connection *viewer_connection) -{ - char error_buf[256] = { 0 }; - struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; - int ret = -1; - const char *path = viewer_connection->url->str; - - if (!path) { - goto end; - } - - lttng_live_url_parts = bt_common_parse_lttng_live_url(path, - error_buf, sizeof(error_buf)); - if (!lttng_live_url_parts.proto) { - BT_LOGW("Invalid LTTng live URL format: %s", error_buf); - goto end; - } - - viewer_connection->relay_hostname = - lttng_live_url_parts.hostname; - lttng_live_url_parts.hostname = NULL; - - if (lttng_live_url_parts.port >= 0) { - viewer_connection->port = lttng_live_url_parts.port; - } else { - viewer_connection->port = LTTNG_DEFAULT_NETWORK_VIEWER_PORT; - } - - viewer_connection->target_hostname = - lttng_live_url_parts.target_hostname; - lttng_live_url_parts.target_hostname = NULL; - - if (lttng_live_url_parts.session_name) { - viewer_connection->session_name = - lttng_live_url_parts.session_name; - lttng_live_url_parts.session_name = NULL; - } - - BT_LOGD("Connecting to hostname : %s, port : %d, " - "target hostname : %s, session name : %s, " - "proto : %s", - viewer_connection->relay_hostname->str, - viewer_connection->port, - viewer_connection->target_hostname == NULL ? - "" : viewer_connection->target_hostname->str, - viewer_connection->session_name == NULL ? - "" : viewer_connection->session_name->str, - lttng_live_url_parts.proto->str); - ret = 0; - -end: - bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); - return ret; -} - -static -int lttng_live_handshake(struct live_viewer_connection *viewer_connection) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_connect connect; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect); - char cmd_buf[cmd_buf_len]; - int ret; - ssize_t ret_len; - - cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT); - cmd.data_size = htobe64((uint64_t) sizeof(connect)); - cmd.cmd_version = htobe32(0); - - connect.viewer_session_id = -1ULL; /* will be set on recv */ - connect.major = htobe32(LTTNG_LIVE_MAJOR); - connect.minor = htobe32(LTTNG_LIVE_MINOR); - connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &connect, sizeof(connect)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending version: %s", bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - - ret_len = lttng_live_recv(viewer_connection, &connect, sizeof(connect)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving version: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(connect)); - - BT_LOGD("Received viewer session ID : %" PRIu64, - (uint64_t) be64toh(connect.viewer_session_id)); - BT_LOGD("Relayd version : %u.%u", be32toh(connect.major), - be32toh(connect.minor)); - - if (LTTNG_LIVE_MAJOR != be32toh(connect.major)) { - BT_LOGE("Incompatible lttng-relayd protocol"); - goto error; - } - /* Use the smallest protocol version implemented. */ - if (LTTNG_LIVE_MINOR > be32toh(connect.minor)) { - viewer_connection->minor = be32toh(connect.minor); - } else { - viewer_connection->minor = LTTNG_LIVE_MINOR; - } - viewer_connection->major = LTTNG_LIVE_MAJOR; - ret = 0; - return ret; - -error: - BT_LOGE("Unable to establish connection"); - return -1; -} - -static -int lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection) -{ - struct hostent *host; - struct sockaddr_in server_addr; - int ret; - - if (parse_url(viewer_connection)) { - goto error; - } - - host = gethostbyname(viewer_connection->relay_hostname->str); - if (!host) { - BT_LOGE("Cannot lookup hostname %s", - viewer_connection->relay_hostname->str); - goto error; - } - - if ((viewer_connection->control_sock = socket(AF_INET, SOCK_STREAM, 0)) == BT_INVALID_SOCKET) { - BT_LOGE("Socket creation failed: %s", bt_socket_errormsg()); - goto error; - } - - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(viewer_connection->port); - server_addr.sin_addr = *((struct in_addr *) host->h_addr); - memset(&(server_addr.sin_zero), 0, 8); - - if (connect(viewer_connection->control_sock, (struct sockaddr *) &server_addr, - sizeof(struct sockaddr)) == BT_SOCKET_ERROR) { - BT_LOGE("Connection failed: %s", bt_socket_errormsg()); - goto error; - } - if (lttng_live_handshake(viewer_connection)) { - goto error; - } - - ret = 0; - - return ret; - -error: - if (viewer_connection->control_sock != BT_INVALID_SOCKET) { - if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) { - BT_LOGE("Close: %s", bt_socket_errormsg()); - } - } - viewer_connection->control_sock = BT_INVALID_SOCKET; - return -1; -} - -static -void lttng_live_disconnect_viewer( - struct live_viewer_connection *viewer_connection) -{ - if (viewer_connection->control_sock == BT_INVALID_SOCKET) { - return; - } - if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) { - BT_LOGE("Close: %s", bt_socket_errormsg()); - viewer_connection->control_sock = BT_INVALID_SOCKET; - } -} - -static -void connection_release(bt_object *obj) -{ - struct live_viewer_connection *conn = - container_of(obj, struct live_viewer_connection, obj); - - live_viewer_connection_destroy(conn); -} - -static -int list_update_session(bt_value *results, - const struct lttng_viewer_session *session, - bool *_found) -{ - int ret = 0; - bt_value *map = NULL; - bt_value *hostname = NULL; - bt_value *session_name = NULL; - bt_value *btval = NULL; - int i, len; - bool found = false; - - len = bt_value_array_get_size(results); - if (len < 0) { - BT_LOGE_STR("Error getting size of array."); - ret = -1; - goto end; - } - for (i = 0; i < len; i++) { - const char *hostname_str = NULL; - const char *session_name_str = NULL; - - map = bt_value_array_borrow_element_by_index(results, (size_t) i); - if (!map) { - BT_LOGE_STR("Error borrowing map."); - ret = -1; - goto end; - } - hostname = bt_value_map_borrow_entry_value(map, "target-hostname"); - if (!hostname) { - BT_LOGE_STR("Error borrowing \"target-hostname\" entry."); - ret = -1; - goto end; - } - session_name = bt_value_map_borrow_entry_value(map, "session-name"); - if (!session_name) { - BT_LOGE_STR("Error borrowing \"session-name\" entry."); - ret = -1; - goto end; - } - hostname_str = bt_value_string_get(hostname); - session_name_str = bt_value_string_get(session_name); - - if (!strcmp(session->hostname, hostname_str) - && !strcmp(session->session_name, - session_name_str)) { - int64_t val; - uint32_t streams = be32toh(session->streams); - uint32_t clients = be32toh(session->clients); - - found = true; - - btval = bt_value_map_borrow_entry_value(map, "stream-count"); - if (!btval) { - BT_LOGE_STR("Error borrowing \"stream-count\" entry."); - ret = -1; - goto end; - } - val = bt_value_signed_integer_get(btval); - /* sum */ - val += streams; - bt_value_signed_integer_set(btval, val); - - btval = bt_value_map_borrow_entry_value(map, "client-count"); - if (!btval) { - BT_LOGE_STR("Error borrowing \"client-count\" entry."); - ret = -1; - goto end; - } - val = bt_value_signed_integer_get(btval); - /* max */ - val = max_t(int64_t, clients, val); - bt_value_signed_integer_set(btval, val); - } - - if (found) { - break; - } - } -end: - *_found = found; - return ret; -} - -static -int list_append_session(bt_value *results, - GString *base_url, - const struct lttng_viewer_session *session) -{ - int ret = 0; - bt_value_status ret_status; - bt_value *map = NULL; - GString *url = NULL; - bool found = false; - - /* - * If the session already exists, add the stream count to it, - * and do max of client counts. - */ - ret = list_update_session(results, session, &found); - if (ret || found) { - goto end; - } - - map = bt_value_map_create(); - if (!map) { - BT_LOGE_STR("Error creating map value."); - ret = -1; - goto end; - } - - if (base_url->len < 1) { - BT_LOGE_STR("Error: base_url length smaller than 1."); - ret = -1; - goto end; - } - /* - * key = "url", - * value = , - */ - url = g_string_new(base_url->str); - g_string_append(url, "/host/"); - g_string_append(url, session->hostname); - g_string_append_c(url, '/'); - g_string_append(url, session->session_name); - - ret_status = bt_value_map_insert_string_entry(map, "url", url->str); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"url\" entry."); - ret = -1; - goto end; - } - - /* - * key = "target-hostname", - * value = , - */ - ret_status = bt_value_map_insert_string_entry(map, "target-hostname", - session->hostname); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"target-hostname\" entry."); - ret = -1; - goto end; - } - - /* - * key = "session-name", - * value = , - */ - ret_status = bt_value_map_insert_string_entry(map, "session-name", - session->session_name); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"session-name\" entry."); - ret = -1; - goto end; - } - - /* - * key = "timer-us", - * value = , - */ - { - uint32_t live_timer = be32toh(session->live_timer); - - ret_status = bt_value_map_insert_signed_integer_entry( - map, "timer-us", live_timer); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"timer-us\" entry."); - ret = -1; - goto end; - } - } - - /* - * key = "stream-count", - * value = , - */ - { - uint32_t streams = be32toh(session->streams); - - ret_status = bt_value_map_insert_signed_integer_entry(map, - "stream-count", streams); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"stream-count\" entry."); - ret = -1; - goto end; - } - } - - /* - * key = "client-count", - * value = , - */ - { - uint32_t clients = be32toh(session->clients); - - ret_status = bt_value_map_insert_signed_integer_entry(map, - "client-count", clients); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error inserting \"client-count\" entry."); - ret = -1; - goto end; - } - } - - ret_status = bt_value_array_append_element(results, map); - if (ret_status != BT_VALUE_STATUS_OK) { - BT_LOGE_STR("Error appending map to results."); - ret = -1; - } - -end: - if (url) { - g_string_free(url, true); - } - BT_VALUE_PUT_REF_AND_RESET(map); - return ret; -} - -/* - * Data structure returned: - * - * { - * = { - * [n] = { - * = { - * { - * key = "url", - * value = , - * }, - * { - * key = "target-hostname", - * value = , - * }, - * { - * key = "session-name", - * value = , - * }, - * { - * key = "timer-us", - * value = , - * }, - * { - * key = "stream-count", - * value = , - * }, - * { - * key = "client-count", - * value = , - * }, - * }, - * } - * } - */ - -BT_HIDDEN -bt_query_status live_viewer_connection_list_sessions( - struct live_viewer_connection *viewer_connection, - const bt_value **user_result) -{ - bt_query_status status = BT_QUERY_STATUS_OK; - bt_value *result = NULL; - struct lttng_viewer_cmd cmd; - struct lttng_viewer_list_sessions list; - uint32_t i, sessions_count; - ssize_t ret_len; - - if (lttng_live_handshake(viewer_connection)) { - goto error; - } - - result = bt_value_array_create(); - if (!result) { - BT_LOGE("Error creating array"); - status = BT_QUERY_STATUS_NOMEM; - goto error; - } - - cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); - cmd.data_size = htobe64((uint64_t) 0); - cmd.cmd_version = htobe32(0); - - ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); - status = BT_QUERY_STATUS_ERROR; - goto error; - } - BT_ASSERT(ret_len == sizeof(cmd)); - - ret_len = lttng_live_recv(viewer_connection, &list, sizeof(list)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - status = BT_QUERY_STATUS_ERROR; - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving session list: %s", bt_socket_errormsg()); - status = BT_QUERY_STATUS_ERROR; - goto error; - } - BT_ASSERT(ret_len == sizeof(list)); - - sessions_count = be32toh(list.sessions_count); - for (i = 0; i < sessions_count; i++) { - struct lttng_viewer_session lsession; - - ret_len = lttng_live_recv(viewer_connection, &lsession, - sizeof(lsession)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - status = BT_QUERY_STATUS_ERROR; - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving session: %s", bt_socket_errormsg()); - status = BT_QUERY_STATUS_ERROR; - goto error; - } - BT_ASSERT(ret_len == sizeof(lsession)); - lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0'; - lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; - if (list_append_session(result, viewer_connection->url, - &lsession)) { - status = BT_QUERY_STATUS_ERROR; - goto error; - } - } - - *user_result = result; - goto end; -error: - BT_VALUE_PUT_REF_AND_RESET(result); -end: - return status; -} - -static -int lttng_live_query_session_ids(struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_list_sessions list; - struct lttng_viewer_session lsession; - uint32_t i, sessions_count; - ssize_t ret_len; - uint64_t session_id; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - - cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); - cmd.data_size = htobe64((uint64_t) 0); - cmd.cmd_version = htobe32(0); - - ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(cmd)); - - ret_len = lttng_live_recv(viewer_connection, &list, sizeof(list)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving session list: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(list)); - - sessions_count = be32toh(list.sessions_count); - for (i = 0; i < sessions_count; i++) { - ret_len = lttng_live_recv(viewer_connection, - &lsession, sizeof(lsession)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving session: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(lsession)); - lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0'; - lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; - session_id = be64toh(lsession.id); - - BT_LOGD("Adding session %" PRIu64 " hostname: %s session_name: %s", - session_id, lsession.hostname, lsession.session_name); - - if ((strncmp(lsession.session_name, - viewer_connection->session_name->str, - LTTNG_VIEWER_NAME_MAX) == 0) && (strncmp(lsession.hostname, - viewer_connection->target_hostname->str, - LTTNG_VIEWER_HOST_NAME_MAX) == 0)) { - if (lttng_live_add_session(lttng_live_msg_iter, session_id, - lsession.hostname, - lsession.session_name)) { - goto error; - } - } - } - - return 0; - -error: - BT_LOGE("Unable to query session ids"); - return -1; -} - -BT_HIDDEN -int lttng_live_create_viewer_session( - struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_create_session_response resp; - ssize_t ret_len; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - - cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION); - cmd.data_size = htobe64((uint64_t) 0); - cmd.cmd_version = htobe32(0); - - ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(cmd)); - - ret_len = lttng_live_recv(viewer_connection, &resp, sizeof(resp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving create session reply: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(resp)); - - if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) { - BT_LOGE("Error creating viewer session"); - goto error; - } - if (lttng_live_query_session_ids(lttng_live_msg_iter)) { - goto error; - } - - return 0; - -error: - return -1; -} - -static -int receive_streams(struct lttng_live_session *session, - uint32_t stream_count) -{ - ssize_t ret_len; - uint32_t i; - struct lttng_live_msg_iter *lttng_live_msg_iter = - session->lttng_live_msg_iter; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - - BT_LOGD("Getting %" PRIu32 " new streams:", stream_count); - for (i = 0; i < stream_count; i++) { - struct lttng_viewer_stream stream; - struct lttng_live_stream_iterator *live_stream; - uint64_t stream_id; - uint64_t ctf_trace_id; - - ret_len = lttng_live_recv(viewer_connection, &stream, sizeof(stream)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving stream"); - goto error; - } - BT_ASSERT(ret_len == sizeof(stream)); - stream.path_name[LTTNG_VIEWER_PATH_MAX - 1] = '\0'; - stream.channel_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; - stream_id = be64toh(stream.id); - ctf_trace_id = be64toh(stream.ctf_trace_id); - - if (stream.metadata_flag) { - BT_LOGD(" metadata stream %" PRIu64 " : %s/%s", - stream_id, stream.path_name, - stream.channel_name); - if (lttng_live_metadata_create_stream(session, - ctf_trace_id, stream_id, - stream.path_name)) { - BT_LOGE("Error creating metadata stream"); - - goto error; - } - session->lazy_stream_msg_init = true; - } else { - BT_LOGD(" stream %" PRIu64 " : %s/%s", - stream_id, stream.path_name, - stream.channel_name); - live_stream = lttng_live_stream_iterator_create(session, - ctf_trace_id, stream_id); - if (!live_stream) { - BT_LOGE("Error creating streamn"); - goto error; - } - } - } - return 0; - -error: - return -1; -} - -BT_HIDDEN -int lttng_live_attach_session(struct lttng_live_session *session) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_attach_session_request rq; - struct lttng_viewer_attach_session_response rp; - ssize_t ret_len; - struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - uint64_t session_id = session->id; - uint32_t streams_count; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - - cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.session_id = htobe64(session_id); - // TODO: add cmd line parameter to select seek beginning - // rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING); - rq.seek = htobe32(LTTNG_VIEWER_SEEK_LAST); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending attach request: %s", bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving attach response: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(rp)); - - streams_count = be32toh(rp.streams_count); - switch(be32toh(rp.status)) { - case LTTNG_VIEWER_ATTACH_OK: - break; - case LTTNG_VIEWER_ATTACH_UNK: - BT_LOGW("Session id %" PRIu64 " is unknown", session_id); - goto error; - case LTTNG_VIEWER_ATTACH_ALREADY: - BT_LOGW("There is already a viewer attached to this session"); - goto error; - case LTTNG_VIEWER_ATTACH_NOT_LIVE: - BT_LOGW("Not a live session"); - goto error; - case LTTNG_VIEWER_ATTACH_SEEK_ERR: - BT_LOGE("Wrong seek parameter"); - goto error; - default: - BT_LOGE("Unknown attach return code %u", be32toh(rp.status)); - goto error; - } - - /* We receive the initial list of streams. */ - if (receive_streams(session, streams_count)) { - goto error; - } - - session->attached = true; - session->new_streams_needed = false; - - return 0; - -error: - return -1; -} - -BT_HIDDEN -int lttng_live_detach_session(struct lttng_live_session *session) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_detach_session_request rq; - struct lttng_viewer_detach_session_response rp; - ssize_t ret_len; - struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - uint64_t session_id = session->id; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - - if (!session->attached) { - return 0; - } - - cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.session_id = htobe64(session_id); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending detach request: %s", bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving detach response: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(rp)); - - switch(be32toh(rp.status)) { - case LTTNG_VIEWER_DETACH_SESSION_OK: - break; - case LTTNG_VIEWER_DETACH_SESSION_UNK: - BT_LOGW("Session id %" PRIu64 " is unknown", session_id); - goto error; - case LTTNG_VIEWER_DETACH_SESSION_ERR: - BT_LOGW("Error detaching session id %" PRIu64 "", session_id); - goto error; - default: - BT_LOGE("Unknown detach return code %u", be32toh(rp.status)); - goto error; - } - - session->attached = false; - - return 0; - -error: - return -1; -} - -BT_HIDDEN -ssize_t lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, - FILE *fp) -{ - uint64_t len = 0; - int ret; - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_metadata rq; - struct lttng_viewer_metadata_packet rp; - char *data = NULL; - ssize_t ret_len; - struct lttng_live_session *session = trace->session; - struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; - struct lttng_live_metadata *metadata = trace->metadata; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - - rq.stream_id = htobe64(metadata->stream_id); - cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending get_metadata request: %s", bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving get_metadata response: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(rp)); - - switch (be32toh(rp.status)) { - case LTTNG_VIEWER_METADATA_OK: - BT_LOGD("get_metadata : OK"); - break; - case LTTNG_VIEWER_NO_NEW_METADATA: - BT_LOGD("get_metadata : NO NEW"); - ret = 0; - goto end; - case LTTNG_VIEWER_METADATA_ERR: - BT_LOGD("get_metadata : ERR"); - goto error; - default: - BT_LOGD("get_metadata : UNKNOWN"); - goto error; - } - - len = be64toh(rp.len); - BT_LOGD("Writing %" PRIu64" bytes to metadata", len); - if (len <= 0) { - goto error; - } - - data = zmalloc(len); - if (!data) { - BT_LOGE("relay data zmalloc: %s", strerror(errno)); - goto error; - } - ret_len = lttng_live_recv(viewer_connection, data, len); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error_free_data; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving trace packet: %s", bt_socket_errormsg()); - goto error_free_data; - } - BT_ASSERT(ret_len == len); - - do { - ret_len = fwrite(data, 1, len, fp); - } while (ret_len < 0 && errno == EINTR); - if (ret_len < 0) { - BT_LOGE("Writing in the metadata fp"); - goto error_free_data; - } - BT_ASSERT(ret_len == len); - free(data); - ret = len; -end: - return ret; - -error_free_data: - free(data); -error: - return -1; -} - -/* - * Assign the fields from a lttng_viewer_index to a packet_index. - */ -static -void lttng_index_to_packet_index(struct lttng_viewer_index *lindex, - struct packet_index *pindex) -{ - BT_ASSERT(lindex); - BT_ASSERT(pindex); - - pindex->offset = be64toh(lindex->offset); - pindex->packet_size = be64toh(lindex->packet_size); - pindex->content_size = be64toh(lindex->content_size); - pindex->ts_cycles.timestamp_begin = be64toh(lindex->timestamp_begin); - pindex->ts_cycles.timestamp_end = be64toh(lindex->timestamp_end); - pindex->events_discarded = be64toh(lindex->events_discarded); -} - -BT_HIDDEN -enum lttng_live_iterator_status lttng_live_get_next_index( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream, - struct packet_index *index) -{ - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_next_index rq; - ssize_t ret_len; - struct lttng_viewer_index rp; - uint32_t flags, status; - enum lttng_live_iterator_status retstatus = - LTTNG_LIVE_ITERATOR_STATUS_OK; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - struct lttng_live_trace *trace = stream->trace; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - struct lttng_live_component *lttng_live = - lttng_live_msg_iter->lttng_live_comp; - - cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - - memset(&rq, 0, sizeof(rq)); - rq.stream_id = htobe64(stream->viewer_stream_id); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending get_next_index request: %s", - bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving get_next_index response: %s", - bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == sizeof(rp)); - - flags = be32toh(rp.flags); - status = be32toh(rp.status); - - switch (status) { - case LTTNG_VIEWER_INDEX_INACTIVE: - { - uint64_t ctf_stream_class_id; - - BT_LOGD("get_next_index: inactive"); - memset(index, 0, sizeof(struct packet_index)); - index->ts_cycles.timestamp_end = be64toh(rp.timestamp_end); - stream->current_inactivity_ts = index->ts_cycles.timestamp_end; - ctf_stream_class_id = be64toh(rp.stream_id); - if (stream->ctf_stream_class_id != -1ULL) { - BT_ASSERT(stream->ctf_stream_class_id == - ctf_stream_class_id); - } else { - stream->ctf_stream_class_id = ctf_stream_class_id; - } - stream->state = LTTNG_LIVE_STREAM_QUIESCENT; - break; - } - case LTTNG_VIEWER_INDEX_OK: - { - uint64_t ctf_stream_class_id; - - BT_LOGD("get_next_index: OK"); - lttng_index_to_packet_index(&rp, index); - ctf_stream_class_id = be64toh(rp.stream_id); - if (stream->ctf_stream_class_id != -1ULL) { - BT_ASSERT(stream->ctf_stream_class_id == - ctf_stream_class_id); - } else { - stream->ctf_stream_class_id = ctf_stream_class_id; - } - - stream->state = LTTNG_LIVE_STREAM_ACTIVE_DATA; - - if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { - BT_LOGD("get_next_index: new metadata needed"); - trace->new_metadata_needed = true; - } - if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) { - BT_LOGD("get_next_index: new streams needed"); - lttng_live_need_new_streams(lttng_live_msg_iter); - } - break; - } - case LTTNG_VIEWER_INDEX_RETRY: - BT_LOGD("get_next_index: retry"); - memset(index, 0, sizeof(struct packet_index)); - retstatus = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; - goto end; - case LTTNG_VIEWER_INDEX_HUP: - BT_LOGD("get_next_index: stream hung up"); - memset(index, 0, sizeof(struct packet_index)); - index->offset = EOF; - retstatus = LTTNG_LIVE_ITERATOR_STATUS_END; - stream->state = LTTNG_LIVE_STREAM_EOF; - break; - case LTTNG_VIEWER_INDEX_ERR: - BT_LOGE("get_next_index: error"); - memset(index, 0, sizeof(struct packet_index)); - stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; - goto error; - default: - BT_LOGE("get_next_index: unknown value"); - memset(index, 0, sizeof(struct packet_index)); - stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; - goto error; - } -end: - return retstatus; - -error: - if (lttng_live_graph_is_canceled(lttng_live)) { - retstatus = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - } else { - retstatus = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - } - return retstatus; -} - -BT_HIDDEN -enum bt_msg_iter_medium_status lttng_live_get_stream_bytes( - struct lttng_live_msg_iter *lttng_live_msg_iter, - struct lttng_live_stream_iterator *stream, uint8_t *buf, - uint64_t offset, uint64_t req_len, uint64_t *recv_len) -{ - enum bt_msg_iter_medium_status retstatus = BT_MSG_ITER_MEDIUM_STATUS_OK; - struct lttng_viewer_cmd cmd; - struct lttng_viewer_get_packet rq; - struct lttng_viewer_trace_packet rp; - ssize_t ret_len; - uint32_t flags, status; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - struct lttng_live_trace *trace = stream->trace; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - struct lttng_live_component *lttng_live = - lttng_live_msg_iter->lttng_live_comp; - - BT_LOGD("lttng_live_get_stream_bytes: offset=%" PRIu64 ", req_len=%" PRIu64, - offset, req_len); - cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.stream_id = htobe64(stream->viewer_stream_id); - rq.offset = htobe64(offset); - rq.len = htobe32(req_len); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending get_data request: %s", bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving get_data response: %s", bt_socket_errormsg()); - goto error; - } - if (ret_len != sizeof(rp)) { - BT_LOGE("get_data_packet: expected %zu" - ", received %zd", sizeof(rp), - ret_len); - goto error; - } - - flags = be32toh(rp.flags); - status = be32toh(rp.status); - - switch (status) { - case LTTNG_VIEWER_GET_PACKET_OK: - req_len = be32toh(rp.len); - BT_LOGD("get_data_packet: Ok, packet size : %" PRIu64 "", req_len); - break; - case LTTNG_VIEWER_GET_PACKET_RETRY: - /* Unimplemented by relay daemon */ - BT_LOGD("get_data_packet: retry"); - retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; - goto end; - case LTTNG_VIEWER_GET_PACKET_ERR: - if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { - BT_LOGD("get_data_packet: new metadata needed, try again later"); - trace->new_metadata_needed = true; - } - if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) { - BT_LOGD("get_data_packet: new streams needed, try again later"); - lttng_live_need_new_streams(lttng_live_msg_iter); - } - if (flags & (LTTNG_VIEWER_FLAG_NEW_METADATA - | LTTNG_VIEWER_FLAG_NEW_STREAM)) { - retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; - goto end; - } - BT_LOGE("get_data_packet: error"); - goto error; - case LTTNG_VIEWER_GET_PACKET_EOF: - retstatus = BT_MSG_ITER_MEDIUM_STATUS_EOF; - goto end; - default: - BT_LOGE("get_data_packet: unknown"); - goto error; - } - - if (req_len == 0) { - goto error; - } - - ret_len = lttng_live_recv(viewer_connection, buf, req_len); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving trace packet: %s", bt_socket_errormsg()); - goto error; - } - BT_ASSERT(ret_len == req_len); - *recv_len = ret_len; -end: - return retstatus; - -error: - if (lttng_live_graph_is_canceled(lttng_live)) { - retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; - } else { - retstatus = BT_MSG_ITER_MEDIUM_STATUS_ERROR; - } - return retstatus; -} - -/* - * Request new streams for a session. - */ -BT_HIDDEN -enum lttng_live_iterator_status lttng_live_get_new_streams( - struct lttng_live_session *session) -{ - enum lttng_live_iterator_status status = - LTTNG_LIVE_ITERATOR_STATUS_OK; - struct lttng_viewer_cmd cmd; - struct lttng_viewer_new_streams_request rq; - struct lttng_viewer_new_streams_response rp; - ssize_t ret_len; - struct lttng_live_msg_iter *lttng_live_msg_iter = - session->lttng_live_msg_iter; - struct live_viewer_connection *viewer_connection = - lttng_live_msg_iter->viewer_connection; - struct lttng_live_component *lttng_live = - lttng_live_msg_iter->lttng_live_comp; - uint32_t streams_count; - const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); - char cmd_buf[cmd_buf_len]; - - if (!session->new_streams_needed) { - return LTTNG_LIVE_ITERATOR_STATUS_OK; - } - - cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS); - cmd.data_size = htobe64((uint64_t) sizeof(rq)); - cmd.cmd_version = htobe32(0); - - memset(&rq, 0, sizeof(rq)); - rq.session_id = htobe64(session->id); - - /* - * Merge the cmd and connection request to prevent a write-write - * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the - * second write to be performed quickly in presence of Nagle's algorithm. - */ - memcpy(cmd_buf, &cmd, sizeof(cmd)); - memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); - ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error sending get_new_streams request: %s", - bt_socket_errormsg()); - goto error; - } - - BT_ASSERT(ret_len == cmd_buf_len); - ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); - if (ret_len == 0) { - BT_LOGI("Remote side has closed connection"); - goto error; - } - if (ret_len == BT_SOCKET_ERROR) { - BT_LOGE("Error receiving get_new_streams response"); - goto error; - } - BT_ASSERT(ret_len == sizeof(rp)); - - streams_count = be32toh(rp.streams_count); - - switch(be32toh(rp.status)) { - case LTTNG_VIEWER_NEW_STREAMS_OK: - session->new_streams_needed = false; - break; - case LTTNG_VIEWER_NEW_STREAMS_NO_NEW: - session->new_streams_needed = false; - goto end; - case LTTNG_VIEWER_NEW_STREAMS_HUP: - session->new_streams_needed = false; - session->closed = true; - status = LTTNG_LIVE_ITERATOR_STATUS_END; - goto end; - case LTTNG_VIEWER_NEW_STREAMS_ERR: - BT_LOGE("get_new_streams error"); - goto error; - default: - BT_LOGE("Unknown return code %u", be32toh(rp.status)); - goto error; - } - - if (receive_streams(session, streams_count)) { - goto error; - } -end: - return status; - -error: - if (lttng_live_graph_is_canceled(lttng_live)) { - status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; - } else { - status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; - } - return status; -} - -BT_HIDDEN -struct live_viewer_connection *live_viewer_connection_create( - const char *url, bool in_query, - struct lttng_live_msg_iter *lttng_live_msg_iter) -{ - struct live_viewer_connection *viewer_connection; - - viewer_connection = g_new0(struct live_viewer_connection, 1); - - if (bt_socket_init() != 0) { - goto error; - } - - bt_object_init_shared(&viewer_connection->obj, connection_release); - viewer_connection->control_sock = BT_INVALID_SOCKET; - viewer_connection->port = -1; - viewer_connection->in_query = in_query; - viewer_connection->lttng_live_msg_iter = lttng_live_msg_iter; - viewer_connection->url = g_string_new(url); - if (!viewer_connection->url) { - goto error; - } - - BT_LOGD("Establishing connection to url \"%s\"...", url); - if (lttng_live_connect_viewer(viewer_connection)) { - goto error_report; - } - BT_LOGD("Connection to url \"%s\" is established", url); - return viewer_connection; - -error_report: - BT_LOGW("Failure to establish connection to url \"%s\"", url); -error: - g_free(viewer_connection); - return NULL; -} - -BT_HIDDEN -void live_viewer_connection_destroy( - struct live_viewer_connection *viewer_connection) -{ - BT_LOGD("Closing connection to url \"%s\"", viewer_connection->url->str); - lttng_live_disconnect_viewer(viewer_connection); - g_string_free(viewer_connection->url, true); - if (viewer_connection->relay_hostname) { - g_string_free(viewer_connection->relay_hostname, true); - } - if (viewer_connection->target_hostname) { - g_string_free(viewer_connection->target_hostname, true); - } - if (viewer_connection->session_name) { - g_string_free(viewer_connection->session_name, true); - } - g_free(viewer_connection); - - bt_socket_fini(); -} diff --git a/plugins/ctf/lttng-live/viewer-connection.h b/plugins/ctf/lttng-live/viewer-connection.h deleted file mode 100644 index 752d1a91..00000000 --- a/plugins/ctf/lttng-live/viewer-connection.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef LTTNG_LIVE_VIEWER_CONNECTION_H -#define LTTNG_LIVE_VIEWER_CONNECTION_H - -/* - * Copyright 2016 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include -#include - -//TODO: this should not be used by plugins. Should copy code into plugin -//instead. -#include - -#define LTTNG_DEFAULT_NETWORK_VIEWER_PORT 5344 - -#define LTTNG_LIVE_MAJOR 2 -#define LTTNG_LIVE_MINOR 4 - -struct lttng_live_component; - -struct live_viewer_connection { - bt_object obj; - - GString *url; - - GString *relay_hostname; - GString *target_hostname; - GString *session_name; - - BT_SOCKET control_sock; - int port; - - int32_t major; - int32_t minor; - - bool in_query; - struct lttng_live_msg_iter *lttng_live_msg_iter; -}; - -struct packet_index_time { - uint64_t timestamp_begin; - uint64_t timestamp_end; -}; - -struct packet_index { - off_t offset; /* offset of the packet in the file, in bytes */ - int64_t data_offset; /* offset of data within the packet, in bits */ - uint64_t packet_size; /* packet size, in bits */ - uint64_t content_size; /* content size, in bits */ - uint64_t events_discarded; - uint64_t events_discarded_len; /* length of the field, in bits */ - struct packet_index_time ts_cycles; /* timestamp in cycles */ - struct packet_index_time ts_real; /* realtime timestamp */ - /* CTF_INDEX 1.0 limit */ - uint64_t stream_instance_id; /* ID of the channel instance */ - uint64_t packet_seq_num; /* packet sequence number */ -}; - -struct live_viewer_connection * live_viewer_connection_create( - const char *url, bool in_query, - struct lttng_live_msg_iter *lttng_live_msg_iter); - -void live_viewer_connection_destroy( - struct live_viewer_connection *conn); - -bt_query_status live_viewer_connection_list_sessions( - struct live_viewer_connection *viewer_connection, - const bt_value **user_result); - -#endif /* LTTNG_LIVE_VIEWER_CONNECTION_H */ diff --git a/plugins/ctf/plugin.c b/plugins/ctf/plugin.c deleted file mode 100644 index 95974030..00000000 --- a/plugins/ctf/plugin.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * plugin.c - * - * Babeltrace CTF Plug-in Registration Symbols - * - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "fs-src/fs.h" -#include "fs-sink/fs-sink.h" -#include "lttng-live/lttng-live.h" - -#ifndef BT_BUILT_IN_PLUGINS -BT_PLUGIN_MODULE(); -#endif - -/* Initialize plug-in description. */ -BT_PLUGIN(ctf); -BT_PLUGIN_DESCRIPTION("CTF source and sink support"); -BT_PLUGIN_AUTHOR("Julien Desfossez, Mathieu Desnoyers, Jérémie Galarneau, Philippe Proulx"); -BT_PLUGIN_LICENSE("MIT"); - -/* ctf.fs source */ -BT_PLUGIN_SOURCE_COMPONENT_CLASS(fs, ctf_fs_iterator_next); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(fs, - "Read CTF traces from the file system."); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD(fs, ctf_fs_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD(fs, ctf_fs_query); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_finalize); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(fs, - ctf_fs_iterator_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(fs, - ctf_fs_iterator_finalize); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(fs, - ctf_fs_iterator_seek_beginning); - -/* ctf.fs sink */ -BT_PLUGIN_SINK_COMPONENT_CLASS(fs, ctf_fs_sink_consume); -BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(fs, ctf_fs_sink_init); -BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_sink_finalize); -BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(fs, - ctf_fs_sink_graph_is_configured); -BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(fs, "Write CTF traces to the file system."); - -/* ctf.lttng-live source */ -BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, lttng_live, "lttng-live", - lttng_live_msg_iter_next); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION_WITH_ID(auto, lttng_live, - "Connect to an LTTng relay daemon and receive CTF streams."); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD_WITH_ID(auto, lttng_live, - lttng_live_component_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(auto, lttng_live, - lttng_live_query); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(auto, lttng_live, - lttng_live_component_finalize); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD_WITH_ID(auto, - lttng_live, lttng_live_msg_iter_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD_WITH_ID(auto, - lttng_live, lttng_live_msg_iter_finalize); diff --git a/plugins/ctf/print.h b/plugins/ctf/print.h deleted file mode 100644 index eab99d77..00000000 --- a/plugins/ctf/print.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CTF_PRINT_H -#define CTF_PRINT_H - -/* - * Define PRINT_PREFIX, PRINT_DBG_CHECK, and PRINT_ERR_STREAM, then - * include this file. - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#define PERR(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "[error " PRINT_PREFIX "] " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PWARN(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "[warning " PRINT_PREFIX "] " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PDBG(fmt, ...) \ - do { \ - if (PRINT_DBG_CHECK) { \ - fprintf(stderr, \ - "[debug " PRINT_PREFIX "] " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#endif /* CTF_PRINT_H */ diff --git a/plugins/lttng-utils/Makefile.am b/plugins/lttng-utils/Makefile.am deleted file mode 100644 index 1d555a55..00000000 --- a/plugins/lttng-utils/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -SUBDIRS = - -babeltrace_plugin_lttng_utils_la_LIBADD = - -if ENABLE_DEBUG_INFO -SUBDIRS += debug-info -babeltrace_plugin_lttng_utils_la_LIBADD += \ - debug-info/libdebug-info.la -endif - -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -plugindir = "$(PLUGINSDIR)" -plugin_LTLIBRARIES = babeltrace-plugin-lttng-utils.la - -babeltrace_plugin_lttng_utils_la_SOURCES = \ - plugin.c - -babeltrace_plugin_lttng_utils_la_LDFLAGS = \ - $(LT_NO_UNDEFINED) \ - -avoid-version -module \ - $(ELFUTILS_LIBS) - -if !ENABLE_BUILT_IN_PLUGINS -babeltrace_plugin_lttng_utils_la_LIBADD += \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la -endif diff --git a/plugins/lttng-utils/debug-info/Makefile.am b/plugins/lttng-utils/debug-info/Makefile.am deleted file mode 100644 index 0dea0a46..00000000 --- a/plugins/lttng-utils/debug-info/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libdebug-info.la - -libdebug_info_la_LIBADD = \ - $(top_builddir)/fd-cache/libbabeltrace2-fd-cache.la - -libdebug_info_la_SOURCES = \ - bin-info.c \ - bin-info.h \ - crc32.c \ - crc32.h \ - debug-info.c \ - debug-info.h \ - dwarf.c \ - dwarf.h \ - logging.c \ - logging.h \ - trace-ir-data-copy.c \ - trace-ir-data-copy.h \ - trace-ir-mapping.c \ - trace-ir-mapping.h \ - trace-ir-metadata-copy.c \ - trace-ir-metadata-copy.h \ - trace-ir-metadata-field-class-copy.c \ - trace-ir-metadata-field-class-copy.h \ - utils.c \ - utils.h diff --git a/plugins/lttng-utils/debug-info/bin-info.c b/plugins/lttng-utils/debug-info/bin-info.c deleted file mode 100644 index bad796d2..00000000 --- a/plugins/lttng-utils/debug-info/bin-info.c +++ /dev/null @@ -1,1583 +0,0 @@ -/* - * bin-info.c - * - * Babeltrace - Executable and Shared Object Debug Info Reader - * - * Copyright 2015 Antoine Busque - * - * Author: Antoine Busque - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-UTILS-DEBUG-INFO-FLT-BIN-INFO" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "bin-info.h" -#include "crc32.h" -#include "dwarf.h" -#include "utils.h" - -/* - * An address printed in hex is at most 20 bytes (16 for 64-bits + - * leading 0x + optional leading '+' if addr is an offset + null - * character). - */ -#define ADDR_STR_LEN 20 -#define BUILD_ID_NOTE_NAME "GNU" - -BT_HIDDEN -int bin_info_init(void) -{ - int ret = 0; - - if (elf_version(EV_CURRENT) == EV_NONE) { - BT_LOGD("ELF library initialization failed: %s.", - elf_errmsg(-1)); - ret = -1; - } - - return ret; -} - -BT_HIDDEN -struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path, - uint64_t low_addr, uint64_t memsz, bool is_pic, - const char *debug_info_dir, const char *target_prefix) -{ - struct bin_info *bin = NULL; - - BT_ASSERT(fdc); - - if (!path) { - goto error; - } - - bin = g_new0(struct bin_info, 1); - if (!bin) { - goto error; - } - - if (target_prefix) { - bin->elf_path = g_build_filename(target_prefix, path, NULL); - } else { - bin->elf_path = g_strdup(path); - } - - if (!bin->elf_path) { - goto error; - } - - if (debug_info_dir) { - bin->debug_info_dir = g_strdup(debug_info_dir); - if (!bin->debug_info_dir) { - goto error; - } - } - - bin->is_pic = is_pic; - bin->memsz = memsz; - bin->low_addr = low_addr; - bin->high_addr = bin->low_addr + bin->memsz; - bin->build_id = NULL; - bin->build_id_len = 0; - bin->file_build_id_matches = false; - bin->fd_cache = fdc; - - return bin; - -error: - bin_info_destroy(bin); - return NULL; -} - -BT_HIDDEN -void bin_info_destroy(struct bin_info *bin) -{ - if (!bin) { - return; - } - - dwarf_end(bin->dwarf_info); - - g_free(bin->debug_info_dir); - g_free(bin->elf_path); - g_free(bin->dwarf_path); - g_free(bin->build_id); - g_free(bin->dbg_link_filename); - - elf_end(bin->elf_file); - - bt_fd_cache_put_handle(bin->fd_cache, bin->elf_handle); - bt_fd_cache_put_handle(bin->fd_cache, bin->dwarf_handle); - - g_free(bin); -} - -/** - * Initialize the ELF file for a given executable. - * - * @param bin bin_info instance - * @returns 0 on success, negative value on error. - */ -static -int bin_info_set_elf_file(struct bin_info *bin) -{ - struct bt_fd_cache_handle *elf_handle = NULL; - Elf *elf_file = NULL; - - if (!bin) { - goto error; - } - - elf_handle = bt_fd_cache_get_handle(bin->fd_cache, bin->elf_path); - if (!elf_handle) { - BT_LOGD("Failed to open %s", bin->elf_path); - goto error; - } - bin->elf_handle = elf_handle; - - elf_file = elf_begin(bt_fd_cache_handle_get_fd(bin->elf_handle), - ELF_C_READ, NULL); - if (!elf_file) { - BT_LOGE("elf_begin failed: %s", elf_errmsg(-1)); - goto error; - } - - bin->elf_file = elf_file; - - if (elf_kind(elf_file) != ELF_K_ELF) { - BT_LOGE("Error: %s is not an ELF object", bin->elf_path); - goto error; - } - - return 0; - -error: - bt_fd_cache_put_handle(bin->fd_cache, elf_handle); - elf_end(elf_file); - return -1; -} - -/** - * From a note section data struct, check if it is a build id note. - * - * @param note_data Pointer to a note section - * - * @returns 1 on match, 0 if `buf` does not contain a - * valid build id note - */ -static -int is_build_id_note_section(Elf_Data *note_data) -{ - size_t name_offset, desc_offset; - GElf_Nhdr note_header; - int ret = 0; - - /* - * Discard the return value as it contains the size of the note section - * and we don't need it. - */ - (void) gelf_getnote(note_data, 0, ¬e_header, &name_offset, - &desc_offset); - - /* - * Check the note name length. The name_sz field includes the - * terminating null byte. - */ - if (note_header.n_namesz != sizeof(BUILD_ID_NOTE_NAME)) { - goto invalid; - } - - /* Check the note type. */ - if (note_header.n_type != NT_GNU_BUILD_ID) { - goto invalid; - } - - /* Check the note name. */ - if (memcmp(note_data->d_buf + name_offset, BUILD_ID_NOTE_NAME, - note_header.n_namesz) != 0) { - goto invalid; - } - - ret = 1; - -invalid: - return ret; -} - -/** - * From a build id note section data struct, check if the build id it contains - * is identical to the build id passed as parameter. - * - * @param note_data Pointer to the file build id note section. - * @param build_id Pointer to a build id to compare to. - * @param build_id_len length of the build id. - * - * @returns 1 on match, 0 otherwise. - */ -static -int is_build_id_note_section_matching(Elf_Data *note_data, - uint8_t *build_id, size_t build_id_len) -{ - size_t name_offset, desc_offset; - GElf_Nhdr note_header; - - if (build_id_len <= 0) { - goto end; - } - - /* - * Discard the return value as it contains the size of the note section - * and we don't need it. - */ - (void) gelf_getnote(note_data, 0, ¬e_header, &name_offset, - &desc_offset); - - /* - * Compare the binary build id with the supplied build id. - */ - if (memcmp(build_id, note_data->d_buf + desc_offset, - build_id_len) == 0) { - return 1; - } -end: - return 0; -} - -/** - * Checks if the build id stored in `bin` (bin->build_id) is matching the build - * id of the ondisk file (bin->elf_file). - * - * @param bin bin_info instance - * @param build_id build id to compare ot the on disk file - * @param build_id_len length of the build id - * - * @returns 1 on if the build id of stored in `bin` matches - * the build id of the ondisk file. - * 0 on if they are different or an error occured. - */ -static -int is_build_id_matching(struct bin_info *bin) -{ - int ret, is_build_id, is_matching = 0; - Elf_Scn *curr_section = NULL, *next_section = NULL; - GElf_Shdr curr_section_hdr; - - if (!bin->build_id) { - goto error; - } - - /* Set ELF file if it hasn't been accessed yet. */ - if (!bin->elf_file) { - ret = bin_info_set_elf_file(bin); - if (ret) { - /* Failed to set ELF file. */ - goto error; - } - } - - next_section = elf_nextscn(bin->elf_file, curr_section); - if (!next_section) { - goto error; - } - - while (next_section) { - Elf_Data *note_data = NULL; - - curr_section = next_section; - next_section = elf_nextscn(bin->elf_file, curr_section); - - if (!gelf_getshdr(curr_section, &curr_section_hdr)) { - goto error; - } - - if (curr_section_hdr.sh_type != SHT_NOTE) { - continue; - } - - /* - * elf_getdata() translates the data to native byte order. - */ - note_data = elf_getdata(curr_section, NULL); - if (!note_data) { - goto error; - } - - /* Check if the note is of the build-id type. */ - is_build_id = is_build_id_note_section(note_data); - if (!is_build_id) { - continue; - } - - /* - * Compare the build id of the on-disk file and - * the build id recorded in the trace. - */ - is_matching = is_build_id_note_section_matching( - note_data, bin->build_id, bin->build_id_len); - if (!is_matching) { - break; - } - } -error: - return is_matching; -} - -BT_HIDDEN -int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id, - size_t build_id_len) -{ - if (!bin || !build_id) { - goto error; - } - - /* Set the build id. */ - bin->build_id = g_new0(uint8_t, build_id_len); - if (!bin->build_id) { - goto error; - } - - memcpy(bin->build_id, build_id, build_id_len); - bin->build_id_len = build_id_len; - - /* - * Check if the file found on the file system has the same build id - * that what was recorded in the trace. - */ - bin->file_build_id_matches = is_build_id_matching(bin); - if (!bin->file_build_id_matches) { - BT_LOGD_STR("Supplied Build ID does not match Build ID of the " - "binary or library found on the file system."); - goto error; - } - - /* - * Reset the is_elf_only flag in case it had been set - * previously, because we might find separate debug info using - * the new build id information. - */ - bin->is_elf_only = false; - - return 0; - -error: - return -1; -} - -BT_HIDDEN -int bin_info_set_debug_link(struct bin_info *bin, const char *filename, - uint32_t crc) -{ - if (!bin || !filename) { - goto error; - } - - bin->dbg_link_filename = g_strdup(filename); - if (!bin->dbg_link_filename) { - goto error; - } - - bin->dbg_link_crc = crc; - - /* - * Reset the is_elf_only flag in case it had been set - * previously, because we might find separate debug info using - * the new build id information. - */ - bin->is_elf_only = false; - - return 0; - -error: - - return -1; -} - -/** - * Tries to read DWARF info from the location given by path, and - * attach it to the given bin_info instance if it exists. - * - * @param bin bin_info instance for which to set DWARF info - * @param path Presumed location of the DWARF info - * @returns 0 on success, negative value on failure - */ -static -int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path) -{ - int ret = 0; - struct bt_fd_cache_handle *dwarf_handle = NULL; - struct bt_dwarf_cu *cu = NULL; - Dwarf *dwarf_info = NULL; - - if (!bin || !path) { - goto error; - } - - dwarf_handle = bt_fd_cache_get_handle(bin->fd_cache, path); - if (!dwarf_handle) { - goto error; - } - - dwarf_info = dwarf_begin(bt_fd_cache_handle_get_fd(dwarf_handle), - DWARF_C_READ); - if (!dwarf_info) { - goto error; - } - - /* - * Check if the dwarf info has any CU. If not, the - * executable's object file contains no DWARF info. - */ - cu = bt_dwarf_cu_create(dwarf_info); - if (!cu) { - goto error; - } - - ret = bt_dwarf_cu_next(cu); - if (ret) { - goto error; - } - - bin->dwarf_handle = dwarf_handle; - bin->dwarf_path = g_strdup(path); - if (!bin->dwarf_path) { - goto error; - } - bin->dwarf_info = dwarf_info; - free(cu); - - return 0; - -error: - bt_fd_cache_put_handle(bin->fd_cache, dwarf_handle); - dwarf_end(dwarf_info); - g_free(dwarf_info); - free(cu); - - return -1; -} - -/** - * Try to set the dwarf_info for a given bin_info instance via the - * build ID method. - * - * @param bin bin_info instance for which to retrieve the - * DWARF info via build ID - * @returns 0 on success (i.e. dwarf_info set), -1 on failure - */ -static -int bin_info_set_dwarf_info_build_id(struct bin_info *bin) -{ - int i = 0, ret = 0; - char *path = NULL, *build_id_prefix_dir = NULL, *build_id_file = NULL; - const char *dbg_dir = NULL; - size_t build_id_char_len, build_id_suffix_char_len, build_id_file_len; - - if (!bin || !bin->build_id) { - goto error; - } - - dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR; - - /* - * The prefix dir is the first byte of the build id, represented in - * lowercase hex as two characters per byte, +1 for '\0'. - */ - build_id_prefix_dir = g_new0(gchar, BUILD_ID_PREFIX_DIR_LEN + 1); - if (!build_id_prefix_dir) { - goto error; - } - g_snprintf(build_id_prefix_dir, BUILD_ID_PREFIX_DIR_LEN + 1, "%02x", bin->build_id[0]); - - /* - * The build id file is the remaining bytes of the build id, - * represented in lowercase hex, as two characters per byte. - */ - build_id_char_len = (2 * (bin->build_id_len - 1)); - - /* To which the build id suffix is added, +1 for '\0'. */ - build_id_suffix_char_len = strlen(BUILD_ID_SUFFIX) + 1; - - /* - * The resulting filename string is the concatenation of the - * hex build id and the suffix. - */ - build_id_file_len = build_id_char_len + build_id_suffix_char_len; - build_id_file = g_new0(gchar, build_id_file_len); - if (!build_id_file) { - goto error; - } - - /* - * For each byte, starting at offset 1, append two characters - * in lowercase hex. - */ - for (i = 1; i < bin->build_id_len; ++i) { - int path_idx = 2 * (i - 1); - - g_snprintf(&build_id_file[path_idx], 3, "%02x", bin->build_id[i]); - } - /* Append the suffix to the generated string, including the '\0'. */ - g_snprintf(&build_id_file[build_id_char_len], build_id_suffix_char_len, - BUILD_ID_SUFFIX); - - path = g_build_filename(dbg_dir, BUILD_ID_SUBDIR, build_id_prefix_dir, build_id_file, NULL); - if (!path) { - goto error; - } - - ret = bin_info_set_dwarf_info_from_path(bin, path); - if (ret) { - goto error; - } - - goto end; - -error: - ret = -1; -end: - g_free(build_id_prefix_dir); - g_free(build_id_file); - g_free(path); - - return ret; -} - -/** - * Tests whether the file located at path exists and has the expected - * checksum. - * - * This predicate is used when looking up separate debug info via the - * GNU debuglink method. The expected crc can be found .gnu_debuglink - * section in the original ELF file, along with the filename for the - * file containing the debug info. - * - * @param path Full path at which to look for the debug file - * @param crc Expected checksum for the debug file - * @returns 1 if the file exists and has the correct checksum, - * 0 otherwise - */ -static -int is_valid_debug_file(struct bin_info *bin, char *path, uint32_t crc) -{ - int ret = 0; - struct bt_fd_cache_handle *debug_handle = NULL; - uint32_t _crc = 0; - - if (!path) { - goto end; - } - - debug_handle = bt_fd_cache_get_handle(bin->fd_cache, path); - if (!debug_handle) { - goto end; - } - - ret = crc32(bt_fd_cache_handle_get_fd(debug_handle), &_crc); - if (ret) { - ret = 0; - goto end; - } - - ret = (crc == _crc); - -end: - bt_fd_cache_put_handle(bin->fd_cache, debug_handle); - return ret; -} - -/** - * Try to set the dwarf_info for a given bin_info instance via the - * debug-link method. - * - * @param bin bin_info instance for which to retrieve the - * DWARF info via debug link - * @returns 0 on success (i.e. dwarf_info set), -1 on failure - */ -static -int bin_info_set_dwarf_info_debug_link(struct bin_info *bin) -{ - int ret = 0; - const gchar *dbg_dir = NULL; - gchar *bin_dir = NULL, *path = NULL; - - if (!bin || !bin->dbg_link_filename) { - goto error; - } - - dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR; - bin_dir = g_path_get_dirname(bin->elf_path); - - /* First look in the executable's dir */ - path = g_build_filename(bin_dir, bin->dbg_link_filename, NULL); - - if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { - goto found; - } - - /* If not found, look in .debug subdir */ - g_free(path); - path = g_build_filename(bin_dir, DEBUG_SUBDIR, bin->dbg_link_filename, NULL); - - if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { - goto found; - } - - /* Lastly, look under the global debug directory */ - g_free(path); - - path = g_build_filename(dbg_dir, bin_dir, bin->dbg_link_filename, NULL); - if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { - goto found; - } - -error: - ret = -1; -end: - g_free(bin_dir); - g_free(path); - - return ret; - -found: - ret = bin_info_set_dwarf_info_from_path(bin, path); - if (ret) { - goto error; - } - - goto end; -} - -/** - * Initialize the DWARF info for a given executable. - * - * @param bin bin_info instance - * @returns 0 on success, negative value on failure - */ -static -int bin_info_set_dwarf_info(struct bin_info *bin) -{ - int ret = 0; - - if (!bin) { - ret = -1; - goto end; - } - - /* First try to set the DWARF info from the ELF file */ - ret = bin_info_set_dwarf_info_from_path(bin, bin->elf_path); - if (!ret) { - goto end; - } - - /* - * If that fails, try to find separate debug info via build ID - * and debug link. - */ - ret = bin_info_set_dwarf_info_build_id(bin); - if (!ret) { - goto end; - } - - ret = bin_info_set_dwarf_info_debug_link(bin); - if (!ret) { - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -void source_location_destroy(struct source_location *src_loc) -{ - if (!src_loc) { - return; - } - - free(src_loc->filename); - g_free(src_loc); -} - -/** - * Append a string representation of an address offset to an existing - * string. - * - * On success, the out parameter `result` will contain the base string - * followed by the offset string of the form "+0x1234". On failure, - * `result` remains unchanged. - * - * @param base_str The string to which to append an offset string - * @param low_addr The lower virtual memory address, the base from - * which the offset is computed - * @param high_addr The higher virtual memory address - * @param result Out parameter, the base string followed by the - * offset string - * @returns 0 on success, -1 on failure - */ -static -int bin_info_append_offset_str(const char *base_str, uint64_t low_addr, - uint64_t high_addr, char **result) -{ - uint64_t offset; - char *_result = NULL; - - if (!base_str || !result) { - goto error; - } - - offset = high_addr - low_addr; - - _result = g_strdup_printf("%s+%#0" PRIx64, base_str, offset); - if (!_result) { - goto error; - } - *result = _result; - - return 0; - -error: - free(_result); - return -1; -} - -/** - * Try to find the symbol closest to an address within a given ELF - * section. - * - * Only function symbols are taken into account. The symbol's address - * must precede `addr`. A symbol with a closer address might exist - * after `addr` but is irrelevant because it cannot encompass `addr`. - * - * On success, if found, the out parameters `sym` and `shdr` are - * set. On failure or if none are found, they remain unchanged. - * - * @param scn ELF section in which to look for the address - * @param addr Virtual memory address for which to find the - * nearest function symbol - * @param sym Out parameter, the nearest function symbol - * @param shdr Out parameter, the section header for scn - * @returns 0 on success, -1 on failure - */ -static -int bin_info_get_nearest_symbol_from_section(Elf_Scn *scn, uint64_t addr, - GElf_Sym **sym, GElf_Shdr **shdr) -{ - int i; - size_t symbol_count; - Elf_Data *data = NULL; - GElf_Shdr *_shdr = NULL; - GElf_Sym *nearest_sym = NULL; - - if (!scn || !sym || !shdr) { - goto error; - } - - _shdr = g_new0(GElf_Shdr, 1); - if (!_shdr) { - goto error; - } - - _shdr = gelf_getshdr(scn, _shdr); - if (!_shdr) { - goto error; - } - - if (_shdr->sh_type != SHT_SYMTAB) { - /* - * We are only interested in symbol table (symtab) - * sections, skip this one. - */ - goto end; - } - - data = elf_getdata(scn, NULL); - if (!data) { - goto error; - } - - symbol_count = _shdr->sh_size / _shdr->sh_entsize; - - for (i = 0; i < symbol_count; ++i) { - GElf_Sym *cur_sym = NULL; - - cur_sym = g_new0(GElf_Sym, 1); - if (!cur_sym) { - goto error; - } - cur_sym = gelf_getsym(data, i, cur_sym); - if (!cur_sym) { - goto error; - } - if (GELF_ST_TYPE(cur_sym->st_info) != STT_FUNC) { - /* We're only interested in the functions. */ - g_free(cur_sym); - continue; - } - - if (cur_sym->st_value <= addr && - (!nearest_sym || - cur_sym->st_value > nearest_sym->st_value)) { - g_free(nearest_sym); - nearest_sym = cur_sym; - } else { - g_free(cur_sym); - } - } - -end: - if (nearest_sym) { - *sym = nearest_sym; - *shdr = _shdr; - } else { - g_free(_shdr); - } - - return 0; - -error: - g_free(nearest_sym); - g_free(_shdr); - return -1; -} - -/** - * Get the name of the function containing a given address within an - * executable using ELF symbols. - * - * The function name is in fact the name of the nearest ELF symbol, - * followed by the offset in bytes between the address and the symbol - * (in hex), separated by a '+' character. - * - * If found, the out parameter `func_name` is set on success. On failure, - * it remains unchanged. - * - * @param bin bin_info instance for the executable containing - * the address - * @param addr Virtual memory address for which to find the - * function name - * @param func_name Out parameter, the function name - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_elf_function_name(struct bin_info *bin, uint64_t addr, - char **func_name) -{ - /* - * TODO (possible optimisation): if an ELF has no symtab - * section, it has been stripped. Therefore, it would be wise - * to store a flag indicating the stripped status after the - * first iteration to prevent subsequent ones. - */ - int ret = 0; - Elf_Scn *scn = NULL; - GElf_Sym *sym = NULL; - GElf_Shdr *shdr = NULL; - char *sym_name = NULL; - - /* Set ELF file if it hasn't been accessed yet. */ - if (!bin->elf_file) { - ret = bin_info_set_elf_file(bin); - if (ret) { - /* Failed to set ELF file. */ - goto error; - } - } - - scn = elf_nextscn(bin->elf_file, scn); - if (!scn) { - goto error; - } - - while (scn && !sym) { - ret = bin_info_get_nearest_symbol_from_section( - scn, addr, &sym, &shdr); - if (ret) { - goto error; - } - - scn = elf_nextscn(bin->elf_file, scn); - } - - if (sym) { - sym_name = elf_strptr(bin->elf_file, shdr->sh_link, - sym->st_name); - if (!sym_name) { - goto error; - } - - ret = bin_info_append_offset_str(sym_name, sym->st_value, addr, - func_name); - if (ret) { - goto error; - } - } - - g_free(shdr); - g_free(sym); - return 0; - -error: - g_free(shdr); - g_free(sym); - return ret; -} - -/** - * Get the name of the function containing a given address within a - * given compile unit (CU). - * - * If found, the out parameter `func_name` is set on success. On - * failure, it remains unchanged. - * - * @param cu bt_dwarf_cu instance which may contain the address - * @param addr Virtual memory address for which to find the - * function name - * @param func_name Out parameter, the function name - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_cu_function_name(struct bt_dwarf_cu *cu, uint64_t addr, - char **func_name) -{ - int ret = 0; - bool found = false; - struct bt_dwarf_die *die = NULL; - - if (!cu || !func_name) { - goto error; - } - - die = bt_dwarf_die_create(cu); - if (!die) { - goto error; - } - - while (bt_dwarf_die_next(die) == 0) { - int tag; - - ret = bt_dwarf_die_get_tag(die, &tag); - if (ret) { - goto error; - } - - if (tag == DW_TAG_subprogram) { - ret = bt_dwarf_die_contains_addr(die, addr, &found); - if (ret) { - goto error; - } - - if (found) { - break; - } - } - } - - if (found) { - uint64_t low_addr = 0; - char *die_name = NULL; - - ret = bt_dwarf_die_get_name(die, &die_name); - if (ret) { - goto error; - } - - ret = dwarf_lowpc(die->dwarf_die, &low_addr); - if (ret) { - free(die_name); - goto error; - } - - ret = bin_info_append_offset_str(die_name, low_addr, addr, - func_name); - free(die_name); - if (ret) { - goto error; - } - } - - bt_dwarf_die_destroy(die); - return 0; - -error: - bt_dwarf_die_destroy(die); - return -1; -} - -/** - * Get the name of the function containing a given address within an - * executable using DWARF debug info. - * - * If found, the out parameter `func_name` is set on success. On - * failure, it remains unchanged. - * - * @param bin bin_info instance for the executable containing - * the address - * @param addr Virtual memory address for which to find the - * function name - * @param func_name Out parameter, the function name - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_dwarf_function_name(struct bin_info *bin, uint64_t addr, - char **func_name) -{ - int ret = 0; - char *_func_name = NULL; - struct bt_dwarf_cu *cu = NULL; - - if (!bin || !func_name) { - goto error; - } - - cu = bt_dwarf_cu_create(bin->dwarf_info); - if (!cu) { - goto error; - } - - while (bt_dwarf_cu_next(cu) == 0) { - ret = bin_info_lookup_cu_function_name(cu, addr, &_func_name); - if (ret) { - goto error; - } - - if (_func_name) { - break; - } - } - - if (_func_name) { - *func_name = _func_name; - } else { - goto error; - } - - bt_dwarf_cu_destroy(cu); - return 0; - -error: - bt_dwarf_cu_destroy(cu); - return -1; -} - -BT_HIDDEN -int bin_info_lookup_function_name(struct bin_info *bin, - uint64_t addr, char **func_name) -{ - int ret = 0; - char *_func_name = NULL; - - if (!bin || !func_name) { - goto error; - } - - /* - * If the bin_info has a build id but it does not match the build id - * that was found on the file system, return an error. - */ - if (bin->build_id && !bin->file_build_id_matches) { - goto error; - } - - /* Set DWARF info if it hasn't been accessed yet. */ - if (!bin->dwarf_info && !bin->is_elf_only) { - ret = bin_info_set_dwarf_info(bin); - if (ret) { - BT_LOGD_STR("Failed to set bin dwarf info, falling " - "back to ELF lookup."); - /* Failed to set DWARF info, fallback to ELF. */ - bin->is_elf_only = true; - } - } - - if (!bin_info_has_address(bin, addr)) { - goto error; - } - - /* - * Addresses in ELF and DWARF are relative to base address for - * PIC, so make the address argument relative too if needed. - */ - if (bin->is_pic) { - addr -= bin->low_addr; - } - - if (bin->is_elf_only) { - ret = bin_info_lookup_elf_function_name(bin, addr, - &_func_name); - if (ret) { - BT_LOGD("Failed to lookup function name (ELF): " - "ret=%d", ret); - } - } else { - ret = bin_info_lookup_dwarf_function_name(bin, addr, - &_func_name); - if (ret) { - BT_LOGD("Failed to lookup function name (DWARF): " - "ret=%d", ret); - } - } - - *func_name = _func_name; - return 0; - -error: - return -1; -} - -BT_HIDDEN -int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc) -{ - gchar *_bin_loc = NULL; - - if (!bin || !bin_loc) { - goto error; - } - - /* - * If the bin_info has a build id but it does not match the build id - * that was found on the file system, return an error. - */ - if (bin->build_id && !bin->file_build_id_matches) { - goto error; - } - - if (bin->is_pic) { - addr -= bin->low_addr; - _bin_loc = g_strdup_printf("+%#0" PRIx64, addr); - } else { - _bin_loc = g_strdup_printf("@%#0" PRIx64, addr); - } - - if (!_bin_loc) { - goto error; - } - - *bin_loc = _bin_loc; - return 0; - -error: - return -1; -} - -/** - * Predicate used to determine whether the children of a given DIE - * contain a specific address. - * - * More specifically, the parameter `die` is expected to be a - * subprogram (function) DIE, and this predicate tells whether any - * subroutines are inlined within this function and would contain - * `addr`. - * - * On success, the out parameter `contains` is set with the boolean - * value indicating whether the DIE's range covers `addr`. On failure, - * it remains unchanged. - * - * Do note that this function advances the position of `die`. If the - * address is found within one of its children, `die` will be pointing - * to that child upon returning from the function, allowing to extract - * the information deemed necessary. - * - * @param die The parent DIE in whose children the address will be - * looked for - * @param addr The address for which to look for in the DIEs - * @param contains Out parameter, true if addr is contained, - * false if not - * @returns Returns 0 on success, -1 on failure - */ -static -int bin_info_child_die_has_address(struct bt_dwarf_die *die, uint64_t addr, bool *contains) -{ - int ret = 0; - bool _contains = false; - - if (!die) { - goto error; - } - - ret = bt_dwarf_die_child(die); - if (ret) { - goto error; - } - - do { - ret = bt_dwarf_die_contains_addr(die, addr, &_contains); - if (ret) { - goto error; - } - - if (_contains) { - /* - * The address is within the range of the current DIE - * or its children. - */ - int tag; - - ret = bt_dwarf_die_get_tag(die, &tag); - if (ret) { - goto error; - } - - if (tag == DW_TAG_inlined_subroutine) { - /* Found the tracepoint. */ - goto end; - } - - if (bt_dwarf_die_has_children(die)) { - /* - * Look for the address in the children DIEs. - */ - ret = bt_dwarf_die_child(die); - if (ret) { - goto error; - } - } - } - } while (bt_dwarf_die_next(die) == 0); - -end: - *contains = _contains; - return 0; - -error: - return -1; -} - -/** - * Lookup the source location for a given address within a CU, making - * the assumption that it is contained within an inline routine in a - * function. - * - * @param cu bt_dwarf_cu instance in which to look for the address - * @param addr The address for which to look for - * @param src_loc Out parameter, the source location (filename and - * line number) for the address - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_cu_src_loc_inl(struct bt_dwarf_cu *cu, uint64_t addr, - struct source_location **src_loc) -{ - int ret = 0; - bool found = false; - struct bt_dwarf_die *die = NULL; - struct source_location *_src_loc = NULL; - - if (!cu || !src_loc) { - goto error; - } - - die = bt_dwarf_die_create(cu); - if (!die) { - goto error; - } - - while (bt_dwarf_die_next(die) == 0) { - int tag; - - ret = bt_dwarf_die_get_tag(die, &tag); - if (ret) { - goto error; - } - - if (tag == DW_TAG_subprogram) { - bool contains = false; - - ret = bt_dwarf_die_contains_addr(die, addr, &contains); - if (ret) { - goto error; - } - - if (contains) { - /* - * Try to find an inlined subroutine - * child of this DIE containing addr. - */ - ret = bin_info_child_die_has_address(die, addr, - &found); - if(ret) { - goto error; - } - - goto end; - } - } - } - -end: - if (found) { - char *filename = NULL; - uint64_t line_no; - - _src_loc = g_new0(struct source_location, 1); - if (!_src_loc) { - goto error; - } - - ret = bt_dwarf_die_get_call_file(die, &filename); - if (ret) { - goto error; - } - ret = bt_dwarf_die_get_call_line(die, &line_no); - if (ret) { - free(filename); - goto error; - } - - _src_loc->filename = filename; - _src_loc->line_no = line_no; - *src_loc = _src_loc; - } - - bt_dwarf_die_destroy(die); - return 0; - -error: - source_location_destroy(_src_loc); - bt_dwarf_die_destroy(die); - return -1; -} - -/** - * Lookup the source location for a given address within a CU, - * assuming that it is contained within an inlined function. - * - * A source location can be found regardless of inlining status for - * this method, but in the case of an inlined function, the returned - * source location will point not to the callsite but rather to the - * definition site of the inline function. - * - * @param cu bt_dwarf_cu instance in which to look for the address - * @param addr The address for which to look for - * @param src_loc Out parameter, the source location (filename and - * line number) for the address. Set only if the address - * is found and resolved successfully - * - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr, - struct source_location **src_loc) -{ - struct source_location *_src_loc = NULL; - struct bt_dwarf_die *die = NULL; - const char *filename = NULL; - Dwarf_Line *line = NULL; - Dwarf_Addr line_addr; - int ret = 0, line_no; - - if (!cu || !src_loc) { - goto error; - } - - die = bt_dwarf_die_create(cu); - if (!die) { - goto error; - } - - line = dwarf_getsrc_die(die->dwarf_die, addr); - if (!line) { - /* This is not an error. The caller needs to keep looking. */ - goto end; - } - - ret = dwarf_lineaddr(line, &line_addr); - if (ret) { - goto error; - } - - filename = dwarf_linesrc(line, NULL, NULL); - if (!filename) { - goto error; - } - - if (addr == line_addr) { - _src_loc = g_new0(struct source_location, 1); - if (!_src_loc) { - goto error; - } - - ret = dwarf_lineno(line, &line_no); - if (ret) { - goto error; - } - - _src_loc->line_no = line_no; - _src_loc->filename = g_strdup(filename); - } - - if (_src_loc) { - *src_loc = _src_loc; - } - - goto end; - -error: - source_location_destroy(_src_loc); - ret = -1; -end: - bt_dwarf_die_destroy(die); - return ret; -} - -/** - * Get the source location (file name and line number) for a given - * address within a compile unit (CU). - * - * On success, the out parameter `src_loc` is set if found. On - * failure, it remains unchanged. - * - * @param cu bt_dwarf_cu instance for the compile unit which - * may contain the address - * @param addr Virtual memory address for which to find the - * source location - * @param src_loc Out parameter, the source location - * @returns 0 on success, -1 on failure - */ -static -int bin_info_lookup_cu_src_loc(struct bt_dwarf_cu *cu, uint64_t addr, - struct source_location **src_loc) -{ - int ret = 0; - struct source_location *_src_loc = NULL; - - if (!cu || !src_loc) { - goto error; - } - - ret = bin_info_lookup_cu_src_loc_inl(cu, addr, &_src_loc); - if (ret) { - goto error; - } - - if (_src_loc) { - goto end; - } - - ret = bin_info_lookup_cu_src_loc_no_inl(cu, addr, &_src_loc); - if (ret) { - goto error; - } - - if (_src_loc) { - goto end; - } - -end: - if (_src_loc) { - *src_loc = _src_loc; - } - - return 0; - -error: - source_location_destroy(_src_loc); - return -1; -} - -BT_HIDDEN -int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr, - struct source_location **src_loc) -{ - struct bt_dwarf_cu *cu = NULL; - struct source_location *_src_loc = NULL; - - if (!bin || !src_loc) { - goto error; - } - - /* - * If the bin_info has a build id but it does not match the build id - * that was found on the file system, return an error. - */ - if (bin->build_id && !bin->file_build_id_matches) { - goto error; - } - - /* Set DWARF info if it hasn't been accessed yet. */ - if (!bin->dwarf_info && !bin->is_elf_only) { - if (bin_info_set_dwarf_info(bin)) { - /* Failed to set DWARF info. */ - bin->is_elf_only = true; - } - } - - if (bin->is_elf_only) { - /* We cannot lookup source location without DWARF info. */ - goto error; - } - - if (!bin_info_has_address(bin, addr)) { - goto error; - } - - /* - * Addresses in ELF and DWARF are relative to base address for - * PIC, so make the address argument relative too if needed. - */ - if (bin->is_pic) { - addr -= bin->low_addr; - } - - cu = bt_dwarf_cu_create(bin->dwarf_info); - if (!cu) { - goto error; - } - - while (bt_dwarf_cu_next(cu) == 0) { - int ret; - - ret = bin_info_lookup_cu_src_loc(cu, addr, &_src_loc); - if (ret) { - goto error; - } - - if (_src_loc) { - break; - } - } - - bt_dwarf_cu_destroy(cu); - if (_src_loc) { - *src_loc = _src_loc; - } - - return 0; - -error: - source_location_destroy(_src_loc); - bt_dwarf_cu_destroy(cu); - return -1; -} diff --git a/plugins/lttng-utils/debug-info/bin-info.h b/plugins/lttng-utils/debug-info/bin-info.h deleted file mode 100644 index c193a06d..00000000 --- a/plugins/lttng-utils/debug-info/bin-info.h +++ /dev/null @@ -1,240 +0,0 @@ -#ifndef _BABELTRACE_BIN_INFO_H -#define _BABELTRACE_BIN_INFO_H - -/* - * Babeltrace - Executable and Shared Object Debug Info Reader - * - * Copyright 2015 Antoine Busque - * - * Author: Antoine Busque - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -#define DEFAULT_DEBUG_DIR "/usr/lib/debug" -#define DEBUG_SUBDIR ".debug" -#define BUILD_ID_SUBDIR ".build-id" -#define BUILD_ID_SUFFIX ".debug" -#define BUILD_ID_PREFIX_DIR_LEN 2 - -struct bin_info { - /* Base virtual memory address. */ - uint64_t low_addr; - /* Upper bound of exec address space. */ - uint64_t high_addr; - /* Size of exec address space. */ - uint64_t memsz; - /* Paths to ELF and DWARF files. */ - gchar *elf_path; - gchar *dwarf_path; - /* libelf and libdw objects representing the files. */ - Elf *elf_file; - Dwarf *dwarf_info; - /* Optional build ID info. */ - uint8_t *build_id; - size_t build_id_len; - - /* Optional debug link info. */ - gchar *dbg_link_filename; - uint32_t dbg_link_crc; - /* fd cache handles to ELF and DWARF files. */ - struct bt_fd_cache_handle *elf_handle; - struct bt_fd_cache_handle *dwarf_handle; - /* Configuration. */ - gchar *debug_info_dir; - /* Denotes whether the executable is position independent code. */ - bool is_pic:1; - /* Denotes whether the build id in the trace matches to one on disk. */ - bool file_build_id_matches:1; - /* - * Denotes whether the executable only has ELF symbols and no - * DWARF info. - */ - bool is_elf_only:1; - /* Weak ref. Owned by the iterator. */ - struct bt_fd_cache *fd_cache; -}; - -struct source_location { - uint64_t line_no; - gchar *filename; -}; - -/** - * Initializes the bin_info framework. Call this before calling - * anything else. - * - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_init(void); - -/** - * Instantiate a structure representing an ELF executable, possibly - * with DWARF info, located at the given path. - * - * @param path Path to the ELF file - * @param low_addr Base address of the executable - * @param memsz In-memory size of the executable - * @param is_pic Whether the executable is position independent - * code (PIC) - * @param debug_info_dir Directory containing debug info or NULL. - * @param target_prefix Path to the root file system of the target - * or NULL. - * @returns Pointer to the new bin_info on success, - * NULL on failure. - */ -BT_HIDDEN -struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path, - uint64_t low_addr, uint64_t memsz, bool is_pic, - const char *debug_info_dir, const char *target_prefix); - -/** - * Destroy the given bin_info instance - * - * @param bin bin_info instance to destroy - */ -BT_HIDDEN -void bin_info_destroy(struct bin_info *bin); - -/** - * Sets the build ID information for a given bin_info instance. - * - * @param bin The bin_info instance for which to set - * the build ID - * @param build_id Array of bytes containing the actual ID - * @param build_id_len Length in bytes of the build_id - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id, - size_t build_id_len); - -/** - * Sets the debug link information for a given bin_info instance. - * - * @param bin The bin_info instance for which to set - * the debug link - * @param filename Name of the separate debug info file - * @param crc Checksum for the debug info file - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_set_debug_link(struct bin_info *bin, const char *filename, - uint32_t crc); - -/** - * Returns whether or not the given bin info \p bin contains the - * address \p addr. - * - * @param bin bin_info instance - * @param addr Address to lookup - * @returns 1 if \p bin contains \p addr, 0 if it does not, - * -1 on failure - */ -static inline -int bin_info_has_address(struct bin_info *bin, uint64_t addr) -{ - if (!bin) { - return -1; - } - - return addr >= bin->low_addr && addr < bin->high_addr; -} - -/** - * Get the name of the function containing a given address within an - * executable. - * - * If no DWARF info is available, the function falls back to ELF - * symbols and the "function name" is in fact the name of the closest - * symbol, followed by the offset between the symbol and the address. - * - * On success, if found, the out parameter `func_name` is set. The ownership - * of `func_name` is passed to the caller. On failure, `func_name` remains - * unchanged. - * - * @param bin bin_info instance for the executable containing - * the address - * @param addr Virtual memory address for which to find the - * function name - * @param func_name Out parameter, the function name. - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_lookup_function_name(struct bin_info *bin, uint64_t addr, - char **func_name); - -/** - * Get the source location (file name and line number) for a given - * address within an executable. - * - * If no DWARF info is available, the source location cannot be found - * and the function will return unsuccessfully. - * - * On success, if found, the out parameter `src_loc` is set. The ownership - * of `src_loc` is passed to the caller. On failure, `src_loc` remains - * unchanged. - * - * @param bin bin_info instance for the executable containing - * the address - * @param addr Virtual memory address for which to find the - * source location - * @param src_loc Out parameter, the source location - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr, - struct source_location **src_loc); -/** - * Get a string representing the location within the binary of a given - * address. - * - * In the case of a PIC binary, the location is relative (+0x1234). - * For a non-PIC binary, the location is absolute (@0x1234) - * - * On success, the out parameter `bin_loc` is set. The ownership is - * passed to the caller. On failure, `bin_loc` remains unchanged. - * - * @param bin bin_info instance for the executable containing - * the address - * @param addr Virtual memory address for which to find the - * binary location - * @param bin_loc Out parameter, the binary location - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc); - -/** - * Destroy the given source_location instance - * - * @param src_loc source_location instance to destroy - */ -BT_HIDDEN -void source_location_destroy(struct source_location *src_loc); - -#endif /* _BABELTRACE_BIN_INFO_H */ diff --git a/plugins/lttng-utils/debug-info/crc32.c b/plugins/lttng-utils/debug-info/crc32.c deleted file mode 100644 index e68c0434..00000000 --- a/plugins/lttng-utils/debug-info/crc32.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "crc32.h" - -#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) - -/* generated using the AUTODIN II polynomial - * x^32 + x^26 + x^23 + x^22 + x^16 + - * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - */ -static const uint32_t crctab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -int crc32(int fd, uint32_t *crc) -{ - int nr; - uint32_t _crc = ~0; - char buf[BUFSIZ], *p; - - if (fd < 0 || !crc) { - goto error; - } - - while ((nr = read(fd, buf, sizeof(buf))) > 0) { - for (p = buf; nr--; ++p) { - CRC(_crc, *p); - } - } - - if (nr < 0) { - goto error; - } - - *crc = ~_crc; - return 0; - -error: - return -1; -} diff --git a/plugins/lttng-utils/debug-info/crc32.h b/plugins/lttng-utils/debug-info/crc32.h deleted file mode 100644 index 3cb825e4..00000000 --- a/plugins/lttng-utils/debug-info/crc32.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _BABELTRACE_CRC32_H -#define _BABELTRACE_CRC32_H - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include - -/** - * Compute a 32-bit cyclic redundancy checksum for a given file. - * - * On success, the out parameter crc is set with the computed checksum - * value, - * - * @param fd File descriptor for the file for which to compute the CRC - * @param crc Out parameter, the computed checksum - * @returns 0 on success, -1 on failure. - */ -BT_HIDDEN -int crc32(int fd, uint32_t *crc); - -#endif /* _BABELTRACE_CRC32_H */ diff --git a/plugins/lttng-utils/debug-info/debug-info.c b/plugins/lttng-utils/debug-info/debug-info.c deleted file mode 100644 index 1e7a2381..00000000 --- a/plugins/lttng-utils/debug-info/debug-info.c +++ /dev/null @@ -1,2061 +0,0 @@ -/* - * Babeltrace - Debug Information State Tracker - * - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015 Philippe Proulx - * Copyright (c) 2015 Antoine Busque - * Copyright (c) 2016 Jérémie Galarneau - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-UTILS-DEBUG-INFO-FLT" -#include "logging.h" - -#include -#include - -#include -#include -#include - -#include "bin-info.h" -#include "debug-info.h" -#include "trace-ir-data-copy.h" -#include "trace-ir-mapping.h" -#include "trace-ir-metadata-copy.h" -#include "utils.h" - -#define DEFAULT_DEBUG_INFO_FIELD_NAME "debug_info" -#define LTTNG_UST_STATEDUMP_PREFIX "lttng_ust" -#define VPID_FIELD_NAME "vpid" -#define IP_FIELD_NAME "ip" -#define BADDR_FIELD_NAME "baddr" -#define CRC32_FIELD_NAME "crc" -#define BUILD_ID_FIELD_NAME "build_id" -#define FILENAME_FIELD_NAME "filename" -#define IS_PIC_FIELD_NAME "is_pic" -#define MEMSZ_FIELD_NAME "memsz" -#define PATH_FIELD_NAME "path" - -struct debug_info_component { - gchar *arg_debug_dir; - gchar *arg_debug_info_field_name; - gchar *arg_target_prefix; - bt_bool arg_full_path; -}; - -struct debug_info_msg_iter { - struct debug_info_component *debug_info_component; - bt_self_message_iterator *input_iterator; - bt_self_component *self_comp; - bt_self_component_port_input_message_iterator *msg_iter; - - struct trace_ir_maps *ir_maps; - /* in_trace -> debug_info_mapping. */ - GHashTable *debug_info_map; - - struct bt_fd_cache fd_cache; -}; - -struct debug_info_source { - /* Strings are owned by debug_info_source. */ - gchar *func; - /* - * Store the line number as a string so that the allocation and - * conversion to string is only done once. - */ - gchar *line_no; - gchar *src_path; - /* short_src_path points inside src_path, no need to free. */ - const gchar *short_src_path; - gchar *bin_path; - /* short_bin_path points inside bin_path, no need to free. */ - const gchar *short_bin_path; - /* - * Location within the binary. Either absolute (@0x1234) or - * relative (+0x4321). - */ - gchar *bin_loc; -}; - -struct proc_debug_info_sources { - /* - * Hash table: base address (pointer to uint64_t) to bin info; owned by - * proc_debug_info_sources. - */ - GHashTable *baddr_to_bin_info; - - /* - * Hash table: IP (pointer to uint64_t) to (struct debug_info_source *); - * owned by proc_debug_info_sources. - */ - GHashTable *ip_to_debug_info_src; -}; - -struct debug_info { - struct debug_info_component *comp; - const bt_trace *input_trace; - uint64_t destruction_listener_id; - - /* - * Hash table of VPIDs (pointer to int64_t) to - * (struct proc_debug_info_sources*); owned by debug_info. - */ - GHashTable *vpid_to_proc_dbg_info_src; - GQuark q_statedump_bin_info; - GQuark q_statedump_debug_link; - GQuark q_statedump_build_id; - GQuark q_statedump_start; - GQuark q_dl_open; - GQuark q_lib_load; - GQuark q_lib_unload; - struct bt_fd_cache *fd_cache; /* Weak ref. Owned by the iterator. */ -}; - -static -int debug_info_init(struct debug_info *info) -{ - info->q_statedump_bin_info = g_quark_from_string( - "lttng_ust_statedump:bin_info"); - info->q_statedump_debug_link = g_quark_from_string( - "lttng_ust_statedump:debug_link"); - info->q_statedump_build_id = g_quark_from_string( - "lttng_ust_statedump:build_id"); - info->q_statedump_start = g_quark_from_string( - "lttng_ust_statedump:start"); - info->q_dl_open = g_quark_from_string("lttng_ust_dl:dlopen"); - info->q_lib_load = g_quark_from_string("lttng_ust_lib:load"); - info->q_lib_unload = g_quark_from_string("lttng_ust_lib:unload"); - - return bin_info_init(); -} - -static -void debug_info_source_destroy(struct debug_info_source *debug_info_src) -{ - if (!debug_info_src) { - return; - } - - g_free(debug_info_src->func); - g_free(debug_info_src->line_no); - g_free(debug_info_src->src_path); - g_free(debug_info_src->bin_path); - g_free(debug_info_src->bin_loc); - g_free(debug_info_src); -} - -static -struct debug_info_source *debug_info_source_create_from_bin( - struct bin_info *bin, uint64_t ip) -{ - int ret; - struct debug_info_source *debug_info_src = NULL; - struct source_location *src_loc = NULL; - - debug_info_src = g_new0(struct debug_info_source, 1); - - if (!debug_info_src) { - goto end; - } - - /* Lookup function name */ - ret = bin_info_lookup_function_name(bin, ip, &debug_info_src->func); - if (ret) { - goto error; - } - - /* Can't retrieve src_loc from ELF, or could not find binary, skip. */ - if (!bin->is_elf_only || !debug_info_src->func) { - /* Lookup source location */ - ret = bin_info_lookup_source_location(bin, ip, &src_loc); - if (ret) { - BT_LOGD("Failed to lookup source location: ret=%d", ret); - } - } - - if (src_loc) { - debug_info_src->line_no = - g_strdup_printf("%"PRId64, src_loc->line_no); - if (!debug_info_src->line_no) { - BT_LOGD("Error occured when setting line_no field."); - goto error; - } - - if (src_loc->filename) { - debug_info_src->src_path = g_strdup(src_loc->filename); - if (!debug_info_src->src_path) { - goto error; - } - - debug_info_src->short_src_path = get_filename_from_path( - debug_info_src->src_path); - } - source_location_destroy(src_loc); - } - - if (bin->elf_path) { - debug_info_src->bin_path = g_strdup(bin->elf_path); - if (!debug_info_src->bin_path) { - goto error; - } - - debug_info_src->short_bin_path = get_filename_from_path( - debug_info_src->bin_path); - - ret = bin_info_get_bin_loc(bin, ip, &(debug_info_src->bin_loc)); - if (ret) { - goto error; - } - } - -end: - return debug_info_src; - -error: - debug_info_source_destroy(debug_info_src); - return NULL; -} - -static -void proc_debug_info_sources_destroy( - struct proc_debug_info_sources *proc_dbg_info_src) -{ - if (!proc_dbg_info_src) { - return; - } - - if (proc_dbg_info_src->baddr_to_bin_info) { - g_hash_table_destroy(proc_dbg_info_src->baddr_to_bin_info); - } - - if (proc_dbg_info_src->ip_to_debug_info_src) { - g_hash_table_destroy(proc_dbg_info_src->ip_to_debug_info_src); - } - - g_free(proc_dbg_info_src); -} - -static -struct proc_debug_info_sources *proc_debug_info_sources_create(void) -{ - struct proc_debug_info_sources *proc_dbg_info_src = NULL; - - proc_dbg_info_src = g_new0(struct proc_debug_info_sources, 1); - if (!proc_dbg_info_src) { - goto end; - } - - proc_dbg_info_src->baddr_to_bin_info = g_hash_table_new_full( - g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, - (GDestroyNotify) bin_info_destroy); - if (!proc_dbg_info_src->baddr_to_bin_info) { - goto error; - } - - proc_dbg_info_src->ip_to_debug_info_src = g_hash_table_new_full( - g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, - (GDestroyNotify) debug_info_source_destroy); - if (!proc_dbg_info_src->ip_to_debug_info_src) { - goto error; - } - -end: - return proc_dbg_info_src; - -error: - proc_debug_info_sources_destroy(proc_dbg_info_src); - return NULL; -} - -static -struct proc_debug_info_sources *proc_debug_info_sources_ht_get_entry( - GHashTable *ht, int64_t vpid) -{ - gpointer key = g_new0(int64_t, 1); - struct proc_debug_info_sources *proc_dbg_info_src = NULL; - - if (!key) { - goto end; - } - - *((int64_t *) key) = vpid; - - /* Exists? Return it */ - proc_dbg_info_src = g_hash_table_lookup(ht, key); - if (proc_dbg_info_src) { - goto end; - } - - /* Otherwise, create and return it */ - proc_dbg_info_src = proc_debug_info_sources_create(); - if (!proc_dbg_info_src) { - goto end; - } - - g_hash_table_insert(ht, key, proc_dbg_info_src); - /* Ownership passed to ht */ - key = NULL; -end: - g_free(key); - return proc_dbg_info_src; -} - -static inline -const bt_field *event_borrow_payload_field(const bt_event *event, - const char *field_name) -{ - const bt_field *event_payload, *field; - - event_payload = bt_event_borrow_payload_field_const(event); - BT_ASSERT(event_payload); - - field = bt_field_structure_borrow_member_field_by_name_const( - event_payload, field_name); - return field; -} - -static inline -const bt_field *event_borrow_common_context_field(const bt_event *event, - const char *field_name) -{ - const bt_field *event_common_ctx, *field = NULL; - - event_common_ctx = bt_event_borrow_common_context_field_const(event); - if (!event_common_ctx) { - goto end; - } - - field = bt_field_structure_borrow_member_field_by_name_const( - event_common_ctx, field_name); - -end: - return field; -} - -static inline -void event_get_common_context_signed_integer_field_value( - const bt_event *event, const char *field_name, int64_t *value) -{ - *value = bt_field_signed_integer_get_value( - event_borrow_common_context_field(event, field_name)); -} - -static inline -int event_get_payload_build_id_length(const bt_event *event, - const char *field_name, uint64_t *build_id_len) -{ - const bt_field *build_id_field; - const bt_field_class *build_id_field_class; - - build_id_field = event_borrow_payload_field(event, field_name); - build_id_field_class = bt_field_borrow_class_const(build_id_field); - - BT_ASSERT(bt_field_class_get_type(build_id_field_class) == - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); - BT_ASSERT(bt_field_class_get_type( - bt_field_class_array_borrow_element_field_class_const( - build_id_field_class)) == - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); - - *build_id_len = bt_field_array_get_length(build_id_field); - - return 0; -} - -static inline -int event_get_payload_build_id_value(const bt_event *event, - const char *field_name, uint8_t *build_id) -{ - const bt_field *curr_field, *build_id_field; - const bt_field_class *build_id_field_class; - uint64_t i, build_id_len; - int ret; - - ret = 0; - - build_id_field = event_borrow_payload_field(event, field_name); - build_id_field_class = bt_field_borrow_class_const(build_id_field); - - BT_ASSERT(bt_field_class_get_type(build_id_field_class) == - BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); - BT_ASSERT(bt_field_class_get_type( - bt_field_class_array_borrow_element_field_class_const( - build_id_field_class)) == - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); - - build_id_len = bt_field_array_get_length(build_id_field); - - for (i = 0; i < build_id_len; i++) { - curr_field = - bt_field_array_borrow_element_field_by_index_const( - build_id_field, i); - - build_id[i] = bt_field_unsigned_integer_get_value(curr_field); - } - - return ret; -} - -static -void event_get_payload_unsigned_integer_field_value(const bt_event *event, - const char *field_name, uint64_t *value) -{ - *value = bt_field_unsigned_integer_get_value( - event_borrow_payload_field(event, field_name)); -} - -static -void event_get_payload_string_field_value(const bt_event *event, - const char *field_name, const char **value) -{ - *value = bt_field_string_get_value( - event_borrow_payload_field(event, field_name)); -} - -static inline -bool event_has_payload_field(const bt_event *event, - const char *field_name) -{ - return event_borrow_payload_field(event, field_name) != NULL; -} - -static -struct debug_info_source *proc_debug_info_sources_get_entry( - struct proc_debug_info_sources *proc_dbg_info_src, uint64_t ip) -{ - struct debug_info_source *debug_info_src = NULL; - gpointer key = g_new0(uint64_t, 1); - GHashTableIter iter; - gpointer baddr, value; - - if (!key) { - goto end; - } - - *((uint64_t *) key) = ip; - - /* Look in IP to debug infos hash table first. */ - debug_info_src = g_hash_table_lookup( - proc_dbg_info_src->ip_to_debug_info_src, - key); - if (debug_info_src) { - goto end; - } - - /* Check in all bin_infos. */ - g_hash_table_iter_init(&iter, proc_dbg_info_src->baddr_to_bin_info); - - while (g_hash_table_iter_next(&iter, &baddr, &value)) - { - struct bin_info *bin = value; - - if (!bin_info_has_address(value, ip)) { - continue; - } - - /* - * Found; add it to cache. - * - * FIXME: this should be bounded in size (and implement - * a caching policy), and entries should be prunned when - * libraries are unmapped. - */ - debug_info_src = debug_info_source_create_from_bin(bin, ip); - if (debug_info_src) { - g_hash_table_insert( - proc_dbg_info_src->ip_to_debug_info_src, - key, debug_info_src); - /* Ownership passed to ht. */ - key = NULL; - } - break; - } - -end: - free(key); - return debug_info_src; -} - -static -struct debug_info_source *debug_info_query(struct debug_info *debug_info, - int64_t vpid, uint64_t ip) -{ - struct debug_info_source *dbg_info_src = NULL; - struct proc_debug_info_sources *proc_dbg_info_src; - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - goto end; - } - - dbg_info_src = proc_debug_info_sources_get_entry(proc_dbg_info_src, ip); - -end: - return dbg_info_src; -} - -static -struct debug_info *debug_info_create(struct debug_info_component *comp, - const bt_trace *trace, struct bt_fd_cache *fdc) -{ - int ret; - struct debug_info *debug_info; - - BT_ASSERT(comp); - BT_ASSERT(trace); - BT_ASSERT(fdc); - - debug_info = g_new0(struct debug_info, 1); - if (!debug_info) { - goto end; - } - - debug_info->vpid_to_proc_dbg_info_src = g_hash_table_new_full( - g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, - (GDestroyNotify) proc_debug_info_sources_destroy); - if (!debug_info->vpid_to_proc_dbg_info_src) { - goto error; - } - - debug_info->comp = comp; - ret = debug_info_init(debug_info); - if (ret) { - goto error; - } - - debug_info->input_trace = trace; - debug_info->fd_cache = fdc; - -end: - return debug_info; -error: - g_free(debug_info); - return NULL; -} - -static -void debug_info_destroy(struct debug_info *debug_info) -{ - bt_trace_status status; - if (!debug_info) { - goto end; - } - - if (debug_info->vpid_to_proc_dbg_info_src) { - g_hash_table_destroy(debug_info->vpid_to_proc_dbg_info_src); - } - - status = bt_trace_remove_destruction_listener(debug_info->input_trace, - debug_info->destruction_listener_id); - if (status != BT_TRACE_STATUS_OK) { - BT_LOGD("Trace destruction listener removal failed."); - } - - g_free(debug_info); -end: - return; -} - -static -void handle_event_statedump_build_id(struct debug_info *debug_info, - const bt_event *event) -{ - struct proc_debug_info_sources *proc_dbg_info_src; - uint64_t build_id_len, baddr; - uint8_t *build_id = NULL; - struct bin_info *bin; - int64_t vpid; - int ret = 0; - - event_get_common_context_signed_integer_field_value(event, - VPID_FIELD_NAME, &vpid); - event_get_payload_unsigned_integer_field_value(event, - BADDR_FIELD_NAME, &baddr); - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - goto end; - } - - bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, - (gpointer) &baddr); - if (!bin) { - /* - * The build_id event comes after the bin has been - * created. If it isn't found, just ignore this event. - */ - goto end; - } - ret = event_get_payload_build_id_length(event, BUILD_ID_FIELD_NAME, - &build_id_len); - - build_id = g_new0(uint8_t, build_id_len); - if (!build_id) { - goto end; - } - - ret = event_get_payload_build_id_value(event, BUILD_ID_FIELD_NAME, - build_id); - if (ret) { - goto end; - } - - ret = bin_info_set_build_id(bin, build_id, build_id_len); - if (ret) { - goto end; - } - - /* - * Reset the is_elf_only flag in case it had been set - * previously, because we might find separate debug info using - * the new build id information. - */ - bin->is_elf_only = false; - -end: - g_free(build_id); - return; -} - -static -void handle_event_statedump_debug_link(struct debug_info *debug_info, - const bt_event *event) -{ - struct proc_debug_info_sources *proc_dbg_info_src; - struct bin_info *bin = NULL; - int64_t vpid; - uint64_t baddr; - const char *filename = NULL; - uint32_t crc32; - uint64_t crc_field_value; - - event_get_common_context_signed_integer_field_value(event, - VPID_FIELD_NAME, &vpid); - - event_get_payload_unsigned_integer_field_value(event, - BADDR_FIELD_NAME, &baddr); - - event_get_payload_unsigned_integer_field_value(event, - CRC32_FIELD_NAME, &crc_field_value); - - crc32 = (uint32_t) crc_field_value; - - event_get_payload_string_field_value(event, - FILENAME_FIELD_NAME, &filename); - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - goto end; - } - - bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, - (gpointer) &baddr); - if (!bin) { - /* - * The debug_link event comes after the bin has been - * created. If it isn't found, just ignore this event. - */ - goto end; - } - - bin_info_set_debug_link(bin, filename, crc32); - -end: - return; -} - -static -void handle_bin_info_event(struct debug_info *debug_info, - const bt_event *event, bool has_pic_field) -{ - struct proc_debug_info_sources *proc_dbg_info_src; - struct bin_info *bin; - uint64_t baddr, memsz; - int64_t vpid; - const char *path; - gpointer key = NULL; - bool is_pic; - - event_get_payload_unsigned_integer_field_value(event, - MEMSZ_FIELD_NAME, &memsz); - if (memsz == 0) { - /* Ignore VDSO. */ - goto end; - } - - event_get_payload_unsigned_integer_field_value(event, - BADDR_FIELD_NAME, &baddr); - - /* - * This field is not produced by the dlopen event emitted before - * lttng-ust 2.9. - */ - if (!event_has_payload_field(event, PATH_FIELD_NAME)) { - goto end; - } - event_get_payload_string_field_value(event, PATH_FIELD_NAME, &path); - - if (has_pic_field) { - uint64_t is_pic_field_value; - - event_get_payload_unsigned_integer_field_value(event, - IS_PIC_FIELD_NAME, &is_pic_field_value); - is_pic = is_pic_field_value == 1; - } else { - /* - * dlopen has no is_pic field, because the shared - * object is always PIC. - */ - is_pic = true; - } - - event_get_common_context_signed_integer_field_value(event, - VPID_FIELD_NAME, &vpid); - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - goto end; - } - - key = g_new0(uint64_t, 1); - if (!key) { - goto end; - } - - *((uint64_t *) key) = baddr; - - bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, key); - if (bin) { - goto end; - } - - bin = bin_info_create(debug_info->fd_cache, path, baddr, memsz, is_pic, - debug_info->comp->arg_debug_dir, - debug_info->comp->arg_target_prefix); - if (!bin) { - goto end; - } - - g_hash_table_insert(proc_dbg_info_src->baddr_to_bin_info, key, bin); - /* Ownership passed to ht. */ - key = NULL; - -end: - g_free(key); - return; -} - -static inline -void handle_event_statedump_bin_info(struct debug_info *debug_info, - const bt_event *event) -{ - handle_bin_info_event(debug_info, event, true); -} - -static inline -void handle_event_lib_load(struct debug_info *debug_info, - const bt_event *event) -{ - handle_bin_info_event(debug_info, event, false); -} - -static -void handle_event_lib_unload(struct debug_info *debug_info, - const bt_event *event) -{ - gboolean ret; - struct proc_debug_info_sources *proc_dbg_info_src; - uint64_t baddr; - int64_t vpid; - - event_get_payload_unsigned_integer_field_value(event, BADDR_FIELD_NAME, - &baddr); - - event_get_common_context_signed_integer_field_value(event, - VPID_FIELD_NAME, &vpid); - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - /* - * It's an unload event for a library for which no load event - * was previously received. - */ - goto end; - } - - ret = g_hash_table_remove(proc_dbg_info_src->baddr_to_bin_info, - (gpointer) &baddr); - BT_ASSERT(ret); -end: - return; -} - -static -void handle_event_statedump_start(struct debug_info *debug_info, - const bt_event *event) -{ - struct proc_debug_info_sources *proc_dbg_info_src; - int64_t vpid; - - event_get_common_context_signed_integer_field_value( - event, VPID_FIELD_NAME, &vpid); - - proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( - debug_info->vpid_to_proc_dbg_info_src, vpid); - if (!proc_dbg_info_src) { - goto end; - } - - g_hash_table_remove_all(proc_dbg_info_src->baddr_to_bin_info); - g_hash_table_remove_all(proc_dbg_info_src->ip_to_debug_info_src); - -end: - return; -} - -void trace_debug_info_remove_func(const bt_trace *in_trace, void *data) -{ - struct debug_info_msg_iter *debug_it = data; - if (debug_it->debug_info_map) { - gboolean ret; - ret = g_hash_table_remove(debug_it->debug_info_map, - (gpointer) in_trace); - BT_ASSERT(ret); - } -} - -static -void handle_event_statedump(struct debug_info_msg_iter *debug_it, - const bt_event *event) -{ - const bt_event_class *event_class; - const char *event_name; - GQuark q_event_name; - const bt_trace *trace; - struct debug_info *debug_info; - - BT_ASSERT(debug_it); - BT_ASSERT(event); - - event_class = bt_event_borrow_class_const(event); - - event_name = bt_event_class_get_name(event_class); - - trace = bt_stream_borrow_trace_const( - bt_event_borrow_stream_const(event)); - - debug_info = g_hash_table_lookup(debug_it->debug_info_map, trace); - if (!debug_info) { - debug_info = debug_info_create(debug_it->debug_info_component, - trace, &debug_it->fd_cache); - g_hash_table_insert(debug_it->debug_info_map, (gpointer) trace, - debug_info); - bt_trace_add_destruction_listener(trace, - trace_debug_info_remove_func, debug_it, - &debug_info->destruction_listener_id); - } - - q_event_name = g_quark_try_string(event_name); - - if (q_event_name == debug_info->q_statedump_bin_info) { - /* State dump */ - handle_event_statedump_bin_info(debug_info, event); - } else if (q_event_name == debug_info->q_dl_open || - q_event_name == debug_info->q_lib_load) { - /* - * dl_open and lib_load events are both checked for since - * only dl_open was produced as of lttng-ust 2.8. - * - * lib_load, which is produced from lttng-ust 2.9+, is a lot - * more reliable since it will be emitted when other functions - * of the dlopen family are called (e.g. dlmopen) and when - * library are transitively loaded. - */ - handle_event_lib_load(debug_info, event); - } else if (q_event_name == debug_info->q_statedump_start) { - /* Start state dump */ - handle_event_statedump_start(debug_info, event); - } else if (q_event_name == debug_info->q_statedump_debug_link) { - /* Debug link info */ - handle_event_statedump_debug_link(debug_info, event); - } else if (q_event_name == debug_info->q_statedump_build_id) { - /* Build ID info */ - handle_event_statedump_build_id(debug_info, event); - } else if (q_event_name == debug_info-> q_lib_unload) { - handle_event_lib_unload(debug_info, event); - } - - return; -} - -static -void destroy_debug_info_comp(struct debug_info_component *debug_info) -{ - if (!debug_info) { - return; - } - - g_free(debug_info->arg_debug_dir); - g_free(debug_info->arg_debug_info_field_name); - g_free(debug_info->arg_target_prefix); - g_free(debug_info); -} - -static -void fill_debug_info_bin_field(struct debug_info_source *dbg_info_src, - bool full_path, bt_field *curr_field) -{ - bt_field_status status; - - BT_ASSERT(bt_field_get_class_type(curr_field) == - BT_FIELD_CLASS_TYPE_STRING); - - if (dbg_info_src) { - if (full_path) { - status = bt_field_string_set_value(curr_field, - dbg_info_src->bin_path); - } else { - status = bt_field_string_set_value(curr_field, - dbg_info_src->short_bin_path); - } - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set path component of \"bin\" " - "curr_field field's value: str-fc-addr=%p", - curr_field); - } - - status = bt_field_string_append(curr_field, dbg_info_src->bin_loc); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set bin location component of \"bin\" " - "curr_field field's value: str-fc-addr=%p", - curr_field); - } - } else { - status = bt_field_string_set_value(curr_field, ""); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"bin\" curr_field field's value: " - "str-fc-addr=%p", curr_field); - } - } -} - -static -void fill_debug_info_func_field(struct debug_info_source *dbg_info_src, - bt_field *curr_field) -{ - bt_field_status status; - - BT_ASSERT(bt_field_get_class_type(curr_field) == - BT_FIELD_CLASS_TYPE_STRING); - if (dbg_info_src && dbg_info_src->func) { - status = bt_field_string_set_value(curr_field, - dbg_info_src->func); - } else { - status = bt_field_string_set_value(curr_field, ""); - } - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"func\" curr_field field's value: " - "str-fc-addr=%p", curr_field); - } -} - -static -void fill_debug_info_src_field(struct debug_info_source *dbg_info_src, - bool full_path, bt_field *curr_field) -{ - bt_field_status status; - - BT_ASSERT(bt_field_get_class_type(curr_field) == - BT_FIELD_CLASS_TYPE_STRING); - - if (dbg_info_src && dbg_info_src->src_path) { - if (full_path) { - status = bt_field_string_set_value(curr_field, - dbg_info_src->src_path); - } else { - status = bt_field_string_set_value(curr_field, - dbg_info_src->short_src_path); - } - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set path component of \"src\" " - "curr_field field's value: str-fc-addr=%p", - curr_field); - } - - status = bt_field_string_append(curr_field, ":"); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set colon component of \"src\" " - "curr_field field's value: str-fc-addr=%p", - curr_field); - } - - status = bt_field_string_append(curr_field, dbg_info_src->line_no); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set line number component of \"src\" " - "curr_field field's value: str-fc-addr=%p", - curr_field); - } - } else { - status = bt_field_string_set_value(curr_field, ""); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"src\" curr_field field's value: " - "str-fc-addr=%p", curr_field); - } - } -} - -void fill_debug_info_field_empty(bt_field *debug_info_field) -{ - bt_field_status status; - bt_field *bin_field, *func_field, *src_field; - - BT_ASSERT(bt_field_get_class_type(debug_info_field) == - BT_FIELD_CLASS_TYPE_STRUCTURE); - - bin_field = bt_field_structure_borrow_member_field_by_name( - debug_info_field, "bin"); - func_field = bt_field_structure_borrow_member_field_by_name( - debug_info_field, "func"); - src_field = bt_field_structure_borrow_member_field_by_name( - debug_info_field, "src"); - - BT_ASSERT(bt_field_get_class_type(bin_field) == - BT_FIELD_CLASS_TYPE_STRING); - BT_ASSERT(bt_field_get_class_type(func_field) == - BT_FIELD_CLASS_TYPE_STRING); - BT_ASSERT(bt_field_get_class_type(src_field) == - BT_FIELD_CLASS_TYPE_STRING); - - status = bt_field_string_set_value(bin_field, ""); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"bin\" bin_field field's value: " - "str-fc-addr=%p", bin_field); - } - - status = bt_field_string_set_value(func_field, ""); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"func\" func_field field's value: " - "str-fc-addr=%p", func_field); - } - - status = bt_field_string_set_value(src_field, ""); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set \"src\" src_field field's value: " - "str-fc-addr=%p", src_field); - } -} -static -void fill_debug_info_field(struct debug_info *debug_info, int64_t vpid, - uint64_t ip, bt_field *debug_info_field) -{ - struct debug_info_source *dbg_info_src; - const bt_field_class *debug_info_fc; - - BT_ASSERT(bt_field_get_class_type(debug_info_field) == - BT_FIELD_CLASS_TYPE_STRUCTURE); - - debug_info_fc = bt_field_borrow_class_const(debug_info_field); - - BT_ASSERT(bt_field_class_structure_get_member_count(debug_info_fc) == 3); - - dbg_info_src = debug_info_query(debug_info, vpid, ip); - - fill_debug_info_bin_field(dbg_info_src, debug_info->comp->arg_full_path, - bt_field_structure_borrow_member_field_by_name( - debug_info_field, "bin")); - fill_debug_info_func_field(dbg_info_src, - bt_field_structure_borrow_member_field_by_name( - debug_info_field, "func")); - fill_debug_info_src_field(dbg_info_src, debug_info->comp->arg_full_path, - bt_field_structure_borrow_member_field_by_name( - debug_info_field, "src")); -} - -static -void fill_debug_info_event_if_needed(struct debug_info_msg_iter *debug_it, - const bt_event *in_event, bt_event *out_event) -{ - bt_field *out_common_ctx_field, *out_debug_info_field; - const bt_field *vpid_field, *ip_field, *in_common_ctx_field; - const bt_field_class *in_common_ctx_fc; - struct debug_info *debug_info; - uint64_t vpid; - int64_t ip; - gchar *debug_info_field_name = - debug_it->debug_info_component->arg_debug_info_field_name; - - in_common_ctx_field = bt_event_borrow_common_context_field_const( - in_event); - if (!in_common_ctx_field) { - /* - * There is no event common context so no need to add debug - * info field. - */ - goto end; - } - - in_common_ctx_fc = bt_field_borrow_class_const(in_common_ctx_field); - if (!is_event_common_ctx_dbg_info_compatible(in_common_ctx_fc, - debug_it->ir_maps->debug_info_field_class_name)) { - /* - * The input event common context does not have the necessary - * fields to resolve debug information. - */ - goto end; - } - - /* Borrow the debug-info field. */ - out_common_ctx_field = bt_event_borrow_common_context_field(out_event); - if (!out_common_ctx_field) { - goto end; - } - - out_debug_info_field = bt_field_structure_borrow_member_field_by_name( - out_common_ctx_field, debug_info_field_name); - - vpid_field = bt_field_structure_borrow_member_field_by_name_const( - out_common_ctx_field, VPID_FIELD_NAME); - ip_field = bt_field_structure_borrow_member_field_by_name_const( - out_common_ctx_field, IP_FIELD_NAME); - - vpid = bt_field_signed_integer_get_value(vpid_field); - ip = bt_field_unsigned_integer_get_value(ip_field); - - /* - * Borrow the debug_info structure needed for the source - * resolving. - */ - debug_info = g_hash_table_lookup(debug_it->debug_info_map, - bt_stream_borrow_trace_const( - bt_event_borrow_stream_const(in_event))); - - if (debug_info) { - /* - * Perform the debug-info resolving and set the event fields - * accordingly. - */ - fill_debug_info_field(debug_info, vpid, ip, out_debug_info_field); - } else { - BT_LOGD("No debug information for this trace. Setting debug " - "info fields to empty strings."); - fill_debug_info_field_empty(out_debug_info_field); - } -end: - return; -} - -static -void update_event_statedump_if_needed(struct debug_info_msg_iter *debug_it, - const bt_event *in_event) -{ - const bt_field *event_common_ctx; - const bt_field_class *event_common_ctx_fc; - const bt_event_class *in_event_class = bt_event_borrow_class_const(in_event); - - /* - * If the event is an lttng_ust_statedump event AND has the right event - * common context fields update the debug-info view for this process. - */ - event_common_ctx = bt_event_borrow_common_context_field_const(in_event); - if (!event_common_ctx) { - goto end; - } - - event_common_ctx_fc = bt_field_borrow_class_const(event_common_ctx); - if (is_event_common_ctx_dbg_info_compatible(event_common_ctx_fc, - debug_it->ir_maps->debug_info_field_class_name)) { - /* Checkout if it might be a one of lttng ust statedump events. */ - const char *in_event_name = bt_event_class_get_name(in_event_class); - if (strncmp(in_event_name, LTTNG_UST_STATEDUMP_PREFIX, - strlen(LTTNG_UST_STATEDUMP_PREFIX)) == 0) { - /* Handle statedump events. */ - handle_event_statedump(debug_it, in_event); - } - } -end: - return; -} - -static -bt_message *handle_event_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_clock_snapshot *cs; - const bt_clock_class *default_cc; - const bt_packet *in_packet; - bt_event_class *out_event_class; - bt_packet *out_packet; - bt_event *out_event; - - bt_message *out_message = NULL; - - /* Borrow the input event and its event class. */ - const bt_event *in_event = - bt_message_event_borrow_event_const(in_message); - const bt_event_class *in_event_class = - bt_event_borrow_class_const(in_event); - - update_event_statedump_if_needed(debug_it, in_event); - - out_event_class = trace_ir_mapping_borrow_mapped_event_class( - debug_it->ir_maps, in_event_class); - if (!out_event_class) { - out_event_class = trace_ir_mapping_create_new_mapped_event_class( - debug_it->ir_maps, in_event_class); - } - BT_ASSERT(out_event_class); - - /* Borrow the input and output packets. */ - in_packet = bt_event_borrow_packet_const(in_event); - out_packet = trace_ir_mapping_borrow_mapped_packet(debug_it->ir_maps, - in_packet); - - default_cc = bt_stream_class_borrow_default_clock_class_const( - bt_event_class_borrow_stream_class_const(in_event_class)); - if (default_cc) { - /* Borrow event clock snapshot. */ - cs = bt_message_event_borrow_default_clock_snapshot_const( - in_message); - - /* Create an output event message. */ - out_message = bt_message_event_create_with_default_clock_snapshot( - debug_it->input_iterator, - out_event_class, out_packet, - bt_clock_snapshot_get_value(cs)); - } else { - out_message = bt_message_event_create(debug_it->input_iterator, - out_event_class, out_packet); - } - - if (!out_message) { - BT_LOGE("Error creating output event message."); - goto error; - } - - out_event = bt_message_event_borrow_event(out_message); - - /* Copy the original fields to the output event. */ - copy_event_content(in_event, out_event); - - /* - * Try to set the debug-info fields based on debug information that is - * gathered so far. - */ - fill_debug_info_event_if_needed(debug_it, in_event, out_event); - -error: - return out_message; -} - -static -bt_message *handle_stream_begin_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_stream *in_stream; - bt_message *out_message; - bt_stream *out_stream; - - in_stream = bt_message_stream_beginning_borrow_stream_const(in_message); - BT_ASSERT(in_stream); - - /* Create a duplicated output stream. */ - out_stream = trace_ir_mapping_create_new_mapped_stream( - debug_it->ir_maps, in_stream); - if (!out_stream) { - out_message = NULL; - goto error; - } - - /* Create an output stream beginning message. */ - out_message = bt_message_stream_beginning_create( - debug_it->input_iterator, out_stream); - if (!out_message) { - BT_LOGE("Error creating output stream beginning message: " - "out-s-addr=%p", out_stream); - } -error: - return out_message; -} - -static -bt_message *handle_stream_end_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_stream *in_stream; - bt_message *out_message = NULL; - bt_stream *out_stream; - - in_stream = bt_message_stream_end_borrow_stream_const(in_message); - BT_ASSERT(in_stream); - - out_stream = trace_ir_mapping_borrow_mapped_stream( - debug_it->ir_maps, in_stream); - BT_ASSERT(out_stream); - - /* Create an output stream end message. */ - out_message = bt_message_stream_end_create(debug_it->input_iterator, - out_stream); - if (!out_message) { - BT_LOGE("Error creating output stream end message: out-s-addr=%p", - out_stream); - } - - /* Remove stream from trace mapping hashtable. */ - trace_ir_mapping_remove_mapped_stream(debug_it->ir_maps, in_stream); - - return out_message; -} - -static -bt_message *handle_packet_begin_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - bool has_default_clock_snapshot; - const bt_clock_snapshot *cs; - bt_message *out_message = NULL; - bt_packet *out_packet; - - const bt_packet *in_packet = - bt_message_packet_beginning_borrow_packet_const(in_message); - BT_ASSERT(in_packet); - - /* This packet should not be already mapped. */ - BT_ASSERT(!trace_ir_mapping_borrow_mapped_packet( - debug_it->ir_maps, in_packet)); - - out_packet = trace_ir_mapping_create_new_mapped_packet(debug_it->ir_maps, - in_packet); - - BT_ASSERT(out_packet); - - has_default_clock_snapshot = - bt_stream_class_packets_have_beginning_default_clock_snapshot( - bt_stream_borrow_class_const( - bt_packet_borrow_stream_const(in_packet))); - if (has_default_clock_snapshot) { - /* Borrow clock snapshot. */ - cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const( - in_message); - - /* Create an output packet beginning message. */ - out_message = bt_message_packet_beginning_create_with_default_clock_snapshot( - debug_it->input_iterator, out_packet, - bt_clock_snapshot_get_value(cs)); - } else { - out_message = bt_message_packet_beginning_create( - debug_it->input_iterator, out_packet); - } - if (!out_message) { - BT_LOGE("Error creating output packet beginning message: " - "out-p-addr=%p", out_packet); - } - - return out_message; -} - -static -bt_message *handle_packet_end_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - bool has_default_clock_snapshot; - const bt_clock_snapshot *cs; - const bt_packet *in_packet; - bt_message *out_message = NULL; - bt_packet *out_packet; - - in_packet = bt_message_packet_end_borrow_packet_const(in_message); - BT_ASSERT(in_packet); - - out_packet = trace_ir_mapping_borrow_mapped_packet(debug_it->ir_maps, in_packet); - BT_ASSERT(out_packet); - - has_default_clock_snapshot = - bt_stream_class_packets_have_end_default_clock_snapshot( - bt_stream_borrow_class_const( - bt_packet_borrow_stream_const(in_packet))); - if (has_default_clock_snapshot) { - /* Borrow clock snapshot. */ - cs = bt_message_packet_end_borrow_default_clock_snapshot_const( - in_message); - - /* Create an outpute packet end message. */ - out_message = bt_message_packet_end_create_with_default_clock_snapshot( - debug_it->input_iterator, out_packet, - bt_clock_snapshot_get_value(cs)); - } else { - out_message = bt_message_packet_end_create( - debug_it->input_iterator, out_packet); - } - - if (!out_message) { - BT_LOGE("Error creating output packet end message: " - "out-p-addr=%p", out_packet); - } - - /* Remove packet from data mapping hashtable. */ - trace_ir_mapping_remove_mapped_packet(debug_it->ir_maps, in_packet); - - return out_message; -} - -static -bt_message *handle_msg_iterator_inactivity(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - /* - * This message type can be forwarded directly because it does - * not refer to any objects in the trace class. - */ - bt_message_get_ref(in_message); - return (bt_message*) in_message; -} - -static -bt_message *handle_stream_act_begin_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_clock_snapshot *cs; - const bt_clock_class *default_cc; - bt_message *out_message = NULL; - bt_stream *out_stream; - uint64_t cs_value; - bt_message_stream_activity_clock_snapshot_state cs_state; - - const bt_stream *in_stream = - bt_message_stream_activity_beginning_borrow_stream_const( - in_message); - BT_ASSERT(in_stream); - - out_stream = trace_ir_mapping_borrow_mapped_stream(debug_it->ir_maps, - in_stream); - BT_ASSERT(out_stream); - - out_message = bt_message_stream_activity_beginning_create( - debug_it->input_iterator, out_stream); - if (!out_message) { - BT_LOGE("Error creating output stream activity beginning " - "message: out-s-addr=%p", out_stream); - goto error; - } - - default_cc = bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(in_stream)); - if (default_cc) { - /* Borrow clock snapshot. */ - cs_state = - bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - in_message, &cs); - - if (cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { - cs_value = bt_clock_snapshot_get_value(cs); - bt_message_stream_activity_beginning_set_default_clock_snapshot( - out_message, cs_value); - } else { - bt_message_stream_activity_beginning_set_default_clock_snapshot_state( - out_message, cs_state); - } - } - -error: - return out_message; -} - -static -bt_message *handle_stream_act_end_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_clock_snapshot *cs; - const bt_clock_class *default_cc; - const bt_stream *in_stream; - bt_message *out_message; - bt_stream *out_stream; - uint64_t cs_value; - bt_message_stream_activity_clock_snapshot_state cs_state; - - in_stream = bt_message_stream_activity_end_borrow_stream_const( - in_message); - BT_ASSERT(in_stream); - - out_stream = trace_ir_mapping_borrow_mapped_stream(debug_it->ir_maps, - in_stream); - BT_ASSERT(out_stream); - - out_message = bt_message_stream_activity_end_create( - debug_it->input_iterator, out_stream); - if (!out_message) { - BT_LOGE("Error creating output stream activity end message: " - "out-s-addr=%p", out_stream); - goto error; - } - - default_cc = bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(in_stream)); - - if (default_cc) { - cs_state = - bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - in_message, &cs); - - if (cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN ) { - cs_value = bt_clock_snapshot_get_value(cs); - bt_message_stream_activity_end_set_default_clock_snapshot( - out_message, cs_value); - } else { - bt_message_stream_activity_end_set_default_clock_snapshot_state( - out_message, cs_state); - } - } - -error: - return out_message; -} - -static -bt_message *handle_discarded_events_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_clock_snapshot *begin_cs, *end_cs; - const bt_stream *in_stream; - bool has_default_clock_snapshots; - uint64_t discarded_events, begin_cs_value, end_cs_value; - bt_property_availability prop_avail; - bt_message *out_message = NULL; - bt_stream *out_stream; - - in_stream = bt_message_discarded_events_borrow_stream_const( - in_message); - BT_ASSERT(in_stream); - - out_stream = trace_ir_mapping_borrow_mapped_stream( - debug_it->ir_maps, in_stream); - BT_ASSERT(out_stream); - - has_default_clock_snapshots = - bt_stream_class_discarded_events_have_default_clock_snapshots( - bt_stream_borrow_class_const(in_stream)); - if (has_default_clock_snapshots) { - begin_cs = - bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - in_message); - end_cs = - bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - in_message); - - begin_cs_value = bt_clock_snapshot_get_value(begin_cs); - end_cs_value = bt_clock_snapshot_get_value(end_cs); - - out_message = - bt_message_discarded_events_create_with_default_clock_snapshots( - debug_it->input_iterator, out_stream, - begin_cs_value, end_cs_value); - } else { - out_message = bt_message_discarded_events_create( - debug_it->input_iterator, out_stream); - } - if (!out_message) { - BT_LOGE("Error creating output discarded events message: " - "out-s-addr=%p", out_stream); - goto error; - } - - prop_avail = bt_message_discarded_events_get_count(in_message, - &discarded_events); - - if (prop_avail == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { - bt_message_discarded_events_set_count(out_message, - discarded_events); - } - -error: - return out_message; -} - -static -bt_message *handle_discarded_packets_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - const bt_clock_snapshot *begin_cs, *end_cs; - bool has_default_clock_snapshots; - const bt_stream *in_stream; - uint64_t discarded_packets, begin_cs_value, end_cs_value; - bt_property_availability prop_avail; - bt_message *out_message = NULL; - bt_stream *out_stream; - - in_stream = bt_message_discarded_packets_borrow_stream_const( - in_message); - BT_ASSERT(in_stream); - - out_stream = trace_ir_mapping_borrow_mapped_stream( - debug_it->ir_maps, in_stream); - BT_ASSERT(out_stream); - - has_default_clock_snapshots = - bt_stream_class_discarded_packets_have_default_clock_snapshots( - bt_stream_borrow_class_const(in_stream)); - if (has_default_clock_snapshots) { - begin_cs = - bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - in_message); - - end_cs = - bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - in_message); - - begin_cs_value = bt_clock_snapshot_get_value(begin_cs); - end_cs_value = bt_clock_snapshot_get_value(end_cs); - - out_message = bt_message_discarded_packets_create_with_default_clock_snapshots( - debug_it->input_iterator, out_stream, - begin_cs_value, end_cs_value); - } else { - out_message = bt_message_discarded_packets_create( - debug_it->input_iterator, out_stream); - } - if (!out_message) { - BT_LOGE("Error creating output discarded packet message: " - "out-s-addr=%p", out_stream); - goto error; - } - - prop_avail = bt_message_discarded_packets_get_count(in_message, - &discarded_packets); - if (prop_avail == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { - bt_message_discarded_packets_set_count(out_message, - discarded_packets); - } - -error: - return out_message; -} - -static -const bt_message *handle_message(struct debug_info_msg_iter *debug_it, - const bt_message *in_message) -{ - bt_message *out_message = NULL; - - switch (bt_message_get_type(in_message)) { - case BT_MESSAGE_TYPE_EVENT: - out_message = handle_event_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - out_message = handle_packet_begin_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_PACKET_END: - out_message = handle_packet_end_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - out_message = handle_stream_begin_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_STREAM_END: - out_message = handle_stream_end_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - out_message = handle_msg_iterator_inactivity(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - out_message = handle_stream_act_begin_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - out_message = handle_stream_act_end_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - out_message = handle_discarded_events_message(debug_it, - in_message); - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - out_message = handle_discarded_packets_message(debug_it, - in_message); - break; - default: - abort(); - break; - } - - return out_message; -} - -static -int init_from_params(struct debug_info_component *debug_info_component, - const bt_value *params) -{ - const bt_value *value = NULL; - int ret = 0; - - BT_ASSERT(params); - - value = bt_value_map_borrow_entry_value_const(params, - "debug-info-field-name"); - if (value) { - debug_info_component->arg_debug_info_field_name = - g_strdup(bt_value_string_get(value)); - } else { - debug_info_component->arg_debug_info_field_name = - g_strdup(DEFAULT_DEBUG_INFO_FIELD_NAME); - } - - value = bt_value_map_borrow_entry_value_const(params, "debug-info-dir"); - if (value) { - debug_info_component->arg_debug_dir = - g_strdup(bt_value_string_get(value)); - } else { - debug_info_component->arg_debug_dir = NULL; - } - - - value = bt_value_map_borrow_entry_value_const(params, "target-prefix"); - if (value) { - debug_info_component->arg_target_prefix = - g_strdup(bt_value_string_get(value)); - } else { - debug_info_component->arg_target_prefix = NULL; - } - - value = bt_value_map_borrow_entry_value_const(params, "full-path"); - if (value) { - debug_info_component->arg_full_path = bt_value_bool_get(value); - } else { - debug_info_component->arg_full_path = BT_FALSE; - } - - return ret; -} - -BT_HIDDEN -bt_self_component_status debug_info_comp_init( - bt_self_component_filter *self_comp, - const bt_value *params, UNUSED_VAR void *init_method_data) -{ - int ret; - struct debug_info_component *debug_info_comp; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - - BT_LOGD("Initializing debug_info component: " - "comp-addr=%p, params-addr=%p", self_comp, params); - - debug_info_comp = g_new0(struct debug_info_component, 1); - if (!debug_info_comp) { - BT_LOGE_STR("Failed to allocate one debug_info component."); - goto error; - } - - bt_self_component_set_data( - bt_self_component_filter_as_self_component(self_comp), - debug_info_comp); - - status = bt_self_component_filter_add_input_port(self_comp, "in", - NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - status = bt_self_component_filter_add_output_port(self_comp, "out", - NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - ret = init_from_params(debug_info_comp, params); - if (ret) { - BT_LOGE("Cannot configure debug_info component: " - "debug_info-comp-addr=%p, params-addr=%p", - debug_info_comp, params); - goto error; - } - - goto end; - -error: - destroy_debug_info_comp(debug_info_comp); - bt_self_component_set_data( - bt_self_component_filter_as_self_component(self_comp), - NULL); - - if (status == BT_SELF_COMPONENT_STATUS_OK) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - } -end: - return status; -} - -BT_HIDDEN -void debug_info_comp_finalize(bt_self_component_filter *self_comp) -{ - struct debug_info_component *debug_info = - bt_self_component_get_data( - bt_self_component_filter_as_self_component( - self_comp)); - BT_LOGD("Finalizing debug_info self_component: comp-addr=%p", - self_comp); - - destroy_debug_info_comp(debug_info); -} - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - const bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_component_port_input_message_iterator *upstream_iterator = NULL; - bt_message_iterator_status upstream_iterator_ret_status; - struct debug_info_msg_iter *debug_info_msg_iter; - struct debug_info_component *debug_info = NULL; - bt_self_message_iterator_status status; - bt_self_component *self_comp = NULL; - bt_message_array_const input_msgs; - const bt_message *out_message; - uint64_t curr_msg_idx, i; - - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - self_comp = bt_self_message_iterator_borrow_component(self_msg_iter); - BT_ASSERT(self_comp); - - debug_info = bt_self_component_get_data(self_comp); - BT_ASSERT(debug_info); - - debug_info_msg_iter = bt_self_message_iterator_get_data(self_msg_iter); - BT_ASSERT(debug_info_msg_iter); - - upstream_iterator = debug_info_msg_iter->msg_iter; - BT_ASSERT(upstream_iterator); - - upstream_iterator_ret_status = - bt_self_component_port_input_message_iterator_next( - upstream_iterator, &input_msgs, count); - if (upstream_iterator_ret_status != BT_MESSAGE_ITERATOR_STATUS_OK) { - /* - * No messages were returned. Not necessarily an error. Convert - * the upstream message iterator status to a self status. - */ - status = bt_common_message_iterator_status_to_self( - upstream_iterator_ret_status); - goto end; - } - - /* - * There should never be more received messages than the capacity we - * provided. - */ - BT_ASSERT(*count <= capacity); - - for (curr_msg_idx = 0; curr_msg_idx < *count; curr_msg_idx++) { - out_message = handle_message(debug_info_msg_iter, - input_msgs[curr_msg_idx]); - if (!out_message) { - goto handle_msg_error; - } - - msgs[curr_msg_idx] = out_message; - /* - * Drop our reference of the input message as we are done with - * it and created a output copy. - */ - bt_message_put_ref(input_msgs[curr_msg_idx]); - } - - goto end; - -handle_msg_error: - /* - * Drop references of all the output messages created before the - * failure. - */ - for (i = 0; i < curr_msg_idx; i++) { - bt_message_put_ref(msgs[i]); - } - - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; -end: - return status; -} - -static -void debug_info_msg_iter_destroy(struct debug_info_msg_iter *debug_info_msg_iter) -{ - if (!debug_info_msg_iter) { - goto end; - } - - if (debug_info_msg_iter->msg_iter) { - bt_self_component_port_input_message_iterator_put_ref( - debug_info_msg_iter->msg_iter); - } - - if (debug_info_msg_iter->ir_maps) { - trace_ir_maps_destroy(debug_info_msg_iter->ir_maps); - } - - if (debug_info_msg_iter->debug_info_map) { - g_hash_table_destroy(debug_info_msg_iter->debug_info_map); - } - - bt_fd_cache_fini(&debug_info_msg_iter->fd_cache); - g_free(debug_info_msg_iter); - -end: - return; -} - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *self_port) -{ - bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - struct bt_self_component_port_input *input_port = NULL; - bt_self_component_port_input_message_iterator *upstream_iterator = NULL; - struct debug_info_msg_iter *debug_info_msg_iter = NULL; - gchar *debug_info_field_name; - int ret; - - /* Borrow the upstream input port. */ - input_port = bt_self_component_filter_borrow_input_port_by_name( - self_comp, "in"); - if (!input_port) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto error; - } - - debug_info_msg_iter = g_new0(struct debug_info_msg_iter, 1); - if (!debug_info_msg_iter) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - /* Create an iterator on the upstream component. */ - upstream_iterator = bt_self_component_port_input_message_iterator_create( - input_port); - if (!upstream_iterator) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( - debug_info_msg_iter->msg_iter, upstream_iterator); - - /* Create hashtable that will contain debug info mapping. */ - debug_info_msg_iter->debug_info_map = g_hash_table_new_full( - g_direct_hash, g_direct_equal, (GDestroyNotify) NULL, - (GDestroyNotify) debug_info_destroy); - if (!debug_info_msg_iter->debug_info_map) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - debug_info_msg_iter->self_comp = - bt_self_component_filter_as_self_component(self_comp); - - debug_info_msg_iter->debug_info_component = bt_self_component_get_data( - bt_self_component_filter_as_self_component( - self_comp)); - - debug_info_field_name = - debug_info_msg_iter->debug_info_component->arg_debug_info_field_name; - - debug_info_msg_iter->ir_maps = trace_ir_maps_create( - bt_self_component_filter_as_self_component(self_comp), - debug_info_field_name); - if (!debug_info_msg_iter->ir_maps) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - ret = bt_fd_cache_init(&debug_info_msg_iter->fd_cache); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto error; - } - - bt_self_message_iterator_set_data(self_msg_iter, debug_info_msg_iter); - debug_info_msg_iter->input_iterator = self_msg_iter; - - goto end; - -error: - debug_info_msg_iter_destroy(debug_info_msg_iter); -end: - return status; -} - -BT_HIDDEN -bt_bool debug_info_msg_iter_can_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct debug_info_msg_iter *debug_info_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - BT_ASSERT(debug_info_msg_iter); - - return bt_self_component_port_input_message_iterator_can_seek_beginning( - debug_info_msg_iter->msg_iter); -} - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct debug_info_msg_iter *debug_info_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(debug_info_msg_iter); - - /* Ask the upstream component to seek to the beginning. */ - status = bt_self_component_port_input_message_iterator_seek_beginning( - debug_info_msg_iter->msg_iter); - if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - /* Clear this iterator data. */ - trace_ir_maps_clear(debug_info_msg_iter->ir_maps); - g_hash_table_remove_all(debug_info_msg_iter->debug_info_map); -end: - return bt_common_message_iterator_status_to_self(status); -} - -BT_HIDDEN -void debug_info_msg_iter_finalize(bt_self_message_iterator *it) -{ - struct debug_info_msg_iter *debug_info_msg_iter; - - debug_info_msg_iter = bt_self_message_iterator_get_data(it); - BT_ASSERT(debug_info_msg_iter); - - debug_info_msg_iter_destroy(debug_info_msg_iter); -} diff --git a/plugins/lttng-utils/debug-info/debug-info.h b/plugins/lttng-utils/debug-info/debug-info.h deleted file mode 100644 index f97d32d2..00000000 --- a/plugins/lttng-utils/debug-info/debug-info.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_H - -/* - * Babeltrace - Debug information Plugin - * - * Copyright (c) 2015-2019 EfficiOS Inc. - * Copyright (c) 2015 Antoine Busque - * Copyright (c) 2019 Francis Deslauriers francis.deslauriers@efficios.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -#define VPID_FIELD_NAME "vpid" -#define IP_FIELD_NAME "ip" - -BT_HIDDEN -bt_self_component_status debug_info_comp_init( - bt_self_component_filter *self_comp, - const bt_value *params, void *init_method_data); - -BT_HIDDEN -void debug_info_comp_finalize(bt_self_component_filter *self_comp); - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *self_port); - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - const bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -BT_HIDDEN -bt_bool debug_info_msg_iter_can_seek_beginning( - bt_self_message_iterator *message_iterator); - -BT_HIDDEN -bt_self_message_iterator_status debug_info_msg_iter_seek_beginning( - bt_self_message_iterator *message_iterator); - -BT_HIDDEN -void debug_info_msg_iter_finalize(bt_self_message_iterator *it); - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_H */ diff --git a/plugins/lttng-utils/debug-info/dwarf.c b/plugins/lttng-utils/debug-info/dwarf.c deleted file mode 100644 index 534d2883..00000000 --- a/plugins/lttng-utils/debug-info/dwarf.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * dwarf.c - * - * Babeltrace - DWARF Information Reader - * - * Copyright 2015 Antoine Busque - * - * Author: Antoine Busque - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "dwarf.h" - -BT_HIDDEN -struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info) -{ - struct bt_dwarf_cu *cu; - - if (!dwarf_info) { - goto error; - } - - cu = g_new0(struct bt_dwarf_cu, 1); - if (!cu) { - goto error; - } - cu->dwarf_info = dwarf_info; - return cu; - -error: - return NULL; -} - -BT_HIDDEN -void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu) -{ - g_free(cu); -} - -BT_HIDDEN -int bt_dwarf_cu_next(struct bt_dwarf_cu *cu) -{ - int ret; - Dwarf_Off next_offset; - size_t cu_header_size; - - if (!cu) { - ret = -1; - goto end; - } - - ret = dwarf_nextcu(cu->dwarf_info, cu->next_offset, &next_offset, - &cu_header_size, NULL, NULL, NULL); - if (ret) { - /* ret is -1 on error, 1 if no next CU. */ - goto end; - } - - cu->offset = cu->next_offset; - cu->next_offset = next_offset; - cu->header_size = cu_header_size; - -end: - return ret; -} - -BT_HIDDEN -struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu) -{ - Dwarf_Die *dwarf_die = NULL; - struct bt_dwarf_die *die = NULL; - - if (!cu) { - goto error; - } - - dwarf_die = g_new0(Dwarf_Die, 1); - if (!dwarf_die) { - goto error; - } - - dwarf_die = dwarf_offdie(cu->dwarf_info, cu->offset + cu->header_size, - dwarf_die); - if (!dwarf_die) { - goto error; - } - - die = g_new0(struct bt_dwarf_die, 1); - if (!die) { - goto error; - } - - die->cu = cu; - die->dwarf_die = dwarf_die; - die->depth = 0; - - return die; - -error: - g_free(dwarf_die); - g_free(die); - return NULL; -} - -BT_HIDDEN -void bt_dwarf_die_destroy(struct bt_dwarf_die *die) -{ - if (!die) { - return; - } - - g_free(die->dwarf_die); - g_free(die); -} - -BT_HIDDEN -int bt_dwarf_die_has_children(struct bt_dwarf_die *die) -{ - return dwarf_haschildren(die->dwarf_die); -} - -BT_HIDDEN -int bt_dwarf_die_child(struct bt_dwarf_die *die) -{ - int ret; - Dwarf_Die *child_die = NULL; - - if (!die) { - ret = -1; - goto error; - } - - child_die = g_new0(Dwarf_Die, 1); - if (!child_die) { - ret = -1; - goto error; - } - - ret = dwarf_child(die->dwarf_die, child_die); - if (ret) { - /* ret is -1 on error, 1 if no child DIE. */ - goto error; - } - - g_free(die->dwarf_die); - die->dwarf_die = child_die; - die->depth++; - return 0; - -error: - g_free(child_die); - return ret; -} - -BT_HIDDEN -int bt_dwarf_die_next(struct bt_dwarf_die *die) -{ - int ret; - Dwarf_Die *next_die = NULL; - - if (!die) { - ret = -1; - goto error; - } - - next_die = g_new0(Dwarf_Die, 1); - if (!next_die) { - ret = -1; - goto error; - } - - if (die->depth == 0) { - ret = dwarf_child(die->dwarf_die, next_die); - if (ret) { - /* ret is -1 on error, 1 if no child DIE. */ - goto error; - } - - die->depth = 1; - } else { - ret = dwarf_siblingof(die->dwarf_die, next_die); - if (ret) { - /* ret is -1 on error, 1 if we reached end of - * DIEs at this depth. */ - goto error; - } - } - - g_free(die->dwarf_die); - die->dwarf_die = next_die; - return 0; - -error: - g_free(next_die); - return ret; -} - -BT_HIDDEN -int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag) -{ - int _tag; - - if (!die || !tag) { - goto error; - } - - _tag = dwarf_tag(die->dwarf_die); - if (_tag == DW_TAG_invalid) { - goto error; - } - - *tag = _tag; - return 0; - -error: - return -1; -} - -BT_HIDDEN -int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name) -{ - const char *_name; - - if (!die || !name) { - goto error; - } - - _name = dwarf_diename(die->dwarf_die); - if (!_name) { - goto error; - } - - *name = g_strdup(_name); - if (!*name) { - goto error; - } - - return 0; - -error: - return -1; -} - -BT_HIDDEN -int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename) -{ - int ret; - Dwarf_Sword file_no; - const char *_filename = NULL; - Dwarf_Files *src_files = NULL; - Dwarf_Attribute *file_attr = NULL; - struct bt_dwarf_die *cu_die = NULL; - - if (!die || !filename) { - goto error; - } - - file_attr = g_new0(Dwarf_Attribute, 1); - if (!file_attr) { - goto error; - } - - file_attr = dwarf_attr(die->dwarf_die, DW_AT_call_file, file_attr); - if (!file_attr) { - goto error; - } - - ret = dwarf_formsdata(file_attr, &file_no); - if (ret) { - goto error; - } - - cu_die = bt_dwarf_die_create(die->cu); - if (!cu_die) { - goto error; - } - - ret = dwarf_getsrcfiles(cu_die->dwarf_die, &src_files, NULL); - if (ret) { - goto error; - } - - _filename = dwarf_filesrc(src_files, file_no, NULL, NULL); - if (!_filename) { - goto error; - } - - *filename = g_strdup(_filename); - - bt_dwarf_die_destroy(cu_die); - g_free(file_attr); - - return 0; - -error: - bt_dwarf_die_destroy(cu_die); - g_free(file_attr); - - return -1; -} - -BT_HIDDEN -int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, - uint64_t *line_no) -{ - int ret = 0; - Dwarf_Attribute *line_attr = NULL; - uint64_t _line_no; - - if (!die || !line_no) { - goto error; - } - - line_attr = g_new0(Dwarf_Attribute, 1); - if (!line_attr) { - goto error; - } - - line_attr = dwarf_attr(die->dwarf_die, DW_AT_call_line, line_attr); - if (!line_attr) { - goto error; - } - - ret = dwarf_formudata(line_attr, &_line_no); - if (ret) { - goto error; - } - - *line_no = _line_no; - g_free(line_attr); - - return 0; - -error: - g_free(line_attr); - - return -1; -} - -BT_HIDDEN -int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, - bool *contains) -{ - int ret; - - ret = dwarf_haspc(die->dwarf_die, addr); - if (ret == -1) { - goto error; - } - - *contains = (ret == 1); - - return 0; - -error: - return -1; -} diff --git a/plugins/lttng-utils/debug-info/dwarf.h b/plugins/lttng-utils/debug-info/dwarf.h deleted file mode 100644 index 72bb8d2b..00000000 --- a/plugins/lttng-utils/debug-info/dwarf.h +++ /dev/null @@ -1,235 +0,0 @@ -#ifndef _BABELTRACE_DWARF_H -#define _BABELTRACE_DWARF_H - -/* - * Babeltrace - DWARF Information Reader - * - * Copyright 2015 Antoine Busque - * - * Author: Antoine Busque - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include - -/* - * bt_dwarf is a wrapper over libdw providing a nicer, higher-level - * interface, to access basic debug information. - */ - -/* - * This structure corresponds to a single compilation unit (CU) for a - * given set of debug information (Dwarf type). - */ -struct bt_dwarf_cu { - Dwarf *dwarf_info; - /* Offset in bytes in the DWARF file to current CU header. */ - Dwarf_Off offset; - /* Offset in bytes in the DWARF file to next CU header. */ - Dwarf_Off next_offset; - /* Size in bytes of CU header */ - size_t header_size; -}; - -/* - * This structure represents a single debug information entry (DIE), - * within a compilation unit (CU). - */ -struct bt_dwarf_die { - struct bt_dwarf_cu *cu; - Dwarf_Die *dwarf_die; - /* - * A depth of 0 represents a root DIE, located in the DWARF - * layout on the same level as its corresponding CU entry. Its - * children DIEs will have a depth of 1, and so forth. - */ - unsigned int depth; -}; - -/** - * Instantiate a structure to access compile units (CU) from a given - * `dwarf_info`. - * - * @param dwarf_info Dwarf instance - * @returns Pointer to the new bt_dwarf_cu on success, - * NULL on failure. - */ -BT_HIDDEN -struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info); - -/** - * Destroy the given bt_dwarf_cu instance. - * - * @param cu bt_dwarf_cu instance - */ -BT_HIDDEN -void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu); - -/** - * Advance the compile unit `cu` to the next one. - * - * On success, `cu`'s offset is set to that of the current compile - * unit in the executable. On failure, `cu` remains unchanged. - * - * @param cu bt_dwarf_cu instance - * @returns 0 on success, 1 if no next CU is available, - * -1 on failure - */ -BT_HIDDEN -int bt_dwarf_cu_next(struct bt_dwarf_cu *cu); - -/** - * Instantiate a structure to access debug information entries (DIE) - * for the given compile unit `cu`. - * - * @param cu bt_dwarf_cu instance - * @returns Pointer to the new bt_dwarf_die on success, - * NULL on failure. - */ -BT_HIDDEN -struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu); - -/** - * Destroy the given bt_dwarf_die instance. - * - * @param die bt_dwarf_die instance - */ -BT_HIDDEN -void bt_dwarf_die_destroy(struct bt_dwarf_die *die); - -/** - * Indicates if the debug information entry `die` has children DIEs. - * - * @param die bt_dwarf_die instance - * @returns 0 if the die no child, 1 otherwise - */ -BT_HIDDEN -int bt_dwarf_die_has_children(struct bt_dwarf_die *die); - -/** - * Advance the debug information entry `die` to its first child, if - * any. - * - * @param die bt_dwarf_die instance - * @returns 0 on success, 1 if no child DIE is available, - * -1 on failure - */ -BT_HIDDEN -int bt_dwarf_die_child(struct bt_dwarf_die *die); - -/** - * Advance the debug information entry `die` to the next one. - * - * The next DIE is considered to be its sibling on the same level. The - * only exception is when the depth of the given DIE is 0, i.e. a - * newly created bt_dwarf_die, in which case next returns the first - * DIE at depth 1. - * - * The reason for staying at a depth of 1 is that this is where all - * the function DIEs (those with a tag value of DW_TAG_subprogram) are - * located, from which more specific child DIEs can then be accessed - * if needed via bt_dwarf_die_child. - * - * @param die bt_dwarf_die instance - * @returns 0 on success, 1 if no other siblings are available, -1 on - * failure - */ -BT_HIDDEN -int bt_dwarf_die_next(struct bt_dwarf_die *die); - -/** - * Get a DIE's tag. - * - * On success, the `tag` out parameter is set to the `die`'s tag's - * value. It remains unchanged on failure. - * - * @param die bt_dwarf_die instance - * @param tag Out parameter, the DIE's tag value - * @returns 0 on success, -1 on failure. - */ -BT_HIDDEN -int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag); - -/** - * Get a DIE's name. - * - * On success, the `name` out parameter is set to the DIE's name. It - * remains unchanged on failure. - * - * @param die bt_dwarf_die instance - * @param name Out parameter, the DIE's name - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name); - -/** - * Get the full path to the DIE's callsite file. - * - * Only applies to DW_TAG_inlined_subroutine entries. The out - * parameter `filename` is set on success, unchanged on failure. - * - * @param die bt_dwarf_die instance - * @param filename Out parameter, the filename for the subroutine's - * callsite - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename); - -/** - * Get line number for the DIE's callsite. - * - * Only applies to DW_TAG_inlined_subroutine entries. The out - * parameter `line_no` is set on success, unchanged on failure. - * - * @param die bt_dwarf_die instance - * @param line_no Out parameter, the line number for the - * subroutine's callsite - * @returns 0 on success, -1 on failure - */ -BT_HIDDEN -int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, - uint64_t *line_no); - -/** - * Verifies whether a given DIE contains the virtual memory address - * `addr`. - * - * On success, the out parameter `contains` is set with the boolean - * value indicating whether the DIE's range covers `addr`. On failure, - * it remains unchanged. - * - * @param die bt_dwarf_die instance - * @param addr The memory address to verify - * @param contains Out parameter, true if addr is contained, - * false if not - * @returns 0 on succes, -1 on failure - */ -BT_HIDDEN -int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, - bool *contains); - -#endif /* _BABELTRACE_DWARF_H */ diff --git a/plugins/lttng-utils/debug-info/logging.c b/plugins/lttng-utils/debug-info/logging.c deleted file mode 100644 index d7befe94..00000000 --- a/plugins/lttng-utils/debug-info/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_lttng_utils_debug_info_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_lttng_utils_debug_info_log_level, - "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL"); diff --git a/plugins/lttng-utils/debug-info/logging.h b/plugins/lttng-utils/debug-info/logging.h deleted file mode 100644 index 154ca0ff..00000000 --- a/plugins/lttng-utils/debug-info/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H -#define PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_lttng_utils_debug_info_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_lttng_utils_debug_info_log_level); - -#endif /* PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H */ diff --git a/plugins/lttng-utils/debug-info/trace-ir-data-copy.c b/plugins/lttng-utils/debug-info/trace-ir-data-copy.c deleted file mode 100644 index 8eb2eddf..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-data-copy.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Babeltrace - Trace IR data object copy - * - * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-DATA-COPY" -#include "logging.h" - -#include -#include - -#include - -#include "trace-ir-data-copy.h" - -BT_HIDDEN -void copy_trace_content(const bt_trace *in_trace, bt_trace *out_trace) -{ - bt_trace_status status; - const char *trace_name; - - BT_LOGD("Copying content of trace: in-t-addr=%p, out-t-addr=%p", - in_trace, out_trace); - - trace_name = bt_trace_get_name(in_trace); - /* Copy the trace name. */ - if (trace_name) { - status = bt_trace_set_name(out_trace, trace_name); - if (status != BT_TRACE_STATUS_OK) { - BT_LOGE("Cannot set trace's name: trace-addr=%p, name=\"%s\"", - out_trace, trace_name); - goto end; - } - } - - BT_LOGD("Copied content of trace: in-t-addr=%p, out-t-addr=%p", - in_trace, out_trace); -end: - return; -} - -BT_HIDDEN -void copy_stream_content(const bt_stream *in_stream, bt_stream *out_stream) -{ - const char *stream_name; - bt_stream_status status; - - BT_LOGD("Copying content of stream: in-s-addr=%p, out-s-addr=%p", - in_stream, out_stream); - - stream_name = bt_stream_get_name(in_stream); - if (stream_name) { - status = bt_stream_set_name(out_stream, stream_name); - if (status != BT_STREAM_STATUS_OK) { - BT_LOGE("Cannot set stream's name: stream-addr=%p, " - "name=%s", out_stream, stream_name); - goto end; - } - } - - BT_LOGD("Copied content of stream: in-s-addr=%p, out-s-addr=%p", - in_stream, out_stream); -end: - return; -} - -BT_HIDDEN -void copy_packet_content(const bt_packet *in_packet, bt_packet *out_packet) -{ - const bt_field *in_context_field; - bt_field *out_context_field; - - BT_LOGD("Copying content of packet: in-p-addr=%p, out-p-addr=%p", - in_packet, out_packet); - - /* Copy context field. */ - in_context_field = bt_packet_borrow_context_field_const(in_packet); - if (in_context_field) { - out_context_field = bt_packet_borrow_context_field(out_packet); - BT_ASSERT(out_context_field); - copy_field_content(in_context_field, out_context_field); - } - - BT_LOGD("Copied content of packet: in-p-addr=%p, out-p-addr=%p", - in_packet, out_packet); - return; -} - -BT_HIDDEN -void copy_event_content(const bt_event *in_event, bt_event *out_event) -{ - const bt_field *in_common_ctx_field, *in_specific_ctx_field, - *in_payload_field; - bt_field *out_common_ctx_field, *out_specific_ctx_field, - *out_payload_field; - - BT_LOGD("Copying content of event: in-e-addr=%p, out-e-addr=%p", - in_event, out_event); - in_common_ctx_field = - bt_event_borrow_common_context_field_const(in_event); - if (in_common_ctx_field) { - out_common_ctx_field = - bt_event_borrow_common_context_field(out_event); - BT_ASSERT(out_common_ctx_field); - copy_field_content(in_common_ctx_field, - out_common_ctx_field); - } - - in_specific_ctx_field = - bt_event_borrow_specific_context_field_const(in_event); - if (in_specific_ctx_field) { - out_specific_ctx_field = - bt_event_borrow_specific_context_field(out_event); - BT_ASSERT(out_specific_ctx_field); - copy_field_content(in_specific_ctx_field, - out_specific_ctx_field); - } - - in_payload_field = bt_event_borrow_payload_field_const(in_event); - if (in_payload_field) { - out_payload_field = bt_event_borrow_payload_field(out_event); - BT_ASSERT(out_payload_field); - copy_field_content(in_payload_field, - out_payload_field); - } - - BT_LOGD("Copied content of event: in-e-addr=%p, out-e-addr=%p", - in_event, out_event); -} - -BT_HIDDEN -void copy_field_content(const bt_field *in_field, bt_field *out_field) -{ - bt_field_class_type in_fc_type, out_fc_type; - - in_fc_type = bt_field_get_class_type(in_field); - out_fc_type = bt_field_get_class_type(out_field); - BT_ASSERT(in_fc_type == out_fc_type); - - BT_LOGD("Copying content of field: in-f-addr=%p, out-f-addr=%p", - in_field, out_field); - switch (in_fc_type) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - bt_field_unsigned_integer_set_value(out_field, - bt_field_unsigned_integer_get_value(in_field)); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - bt_field_signed_integer_set_value(out_field, - bt_field_signed_integer_get_value(in_field)); - break; - case BT_FIELD_CLASS_TYPE_REAL: - bt_field_real_set_value(out_field, - bt_field_real_get_value(in_field)); - break; - case BT_FIELD_CLASS_TYPE_STRING: - { - const char *str = bt_field_string_get_value(in_field); - bt_field_status status = bt_field_string_set_value(out_field, str); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set string field's value: " - "str-field-addr=%p, str=%s" PRId64, - out_field, str); - } - break; - } - case BT_FIELD_CLASS_TYPE_STRUCTURE: - { - uint64_t i, nb_member_struct; - const bt_field *in_member_field; - bt_field *out_member_field; - const bt_field_class *in_field_class; - const char *in_member_name; - - in_field_class = bt_field_borrow_class_const(in_field); - nb_member_struct = bt_field_class_structure_get_member_count( - in_field_class); - - /* - * Iterate over the fields by names in the input field to avoid - * problem if the struct fields are not in the same order after - * the debug-info was added. - */ - for (i = 0; i < nb_member_struct; i++) { - const bt_field_class_structure_member *member = - bt_field_class_structure_borrow_member_by_index_const( - in_field_class, i); - - in_member_name = - bt_field_class_structure_member_get_name( - member); - in_member_field = - bt_field_structure_borrow_member_field_by_name_const( - in_field, in_member_name); - out_member_field = - bt_field_structure_borrow_member_field_by_name( - out_field, in_member_name); - - copy_field_content(in_member_field, - out_member_field); - } - break; - } - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - /* fall through */ - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - { - const bt_field *in_element_field; - bt_field *out_element_field; - uint64_t i, array_len; - bt_field_status status; - - array_len = bt_field_array_get_length(in_field); - - if (in_fc_type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY) { - status = bt_field_dynamic_array_set_length(out_field, - array_len); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot set dynamic array field's " - "length field: field-addr=%p, " - "length=%" PRIu64, out_field, array_len); - } - } - - for (i = 0; i < array_len; i++) { - in_element_field = - bt_field_array_borrow_element_field_by_index_const( - in_field, i); - out_element_field = - bt_field_array_borrow_element_field_by_index( - out_field, i); - copy_field_content(in_element_field, out_element_field); - } - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - { - bt_field_status status; - uint64_t in_selected_option_idx; - const bt_field *in_option_field; - bt_field *out_option_field; - - in_selected_option_idx = - bt_field_variant_get_selected_option_field_index( - in_field); - status = bt_field_variant_select_option_field(out_field, - in_selected_option_idx); - if (status != BT_FIELD_STATUS_OK) { - BT_LOGE("Cannot select variant field's option field: " - "var-field-addr=%p, opt-index=%" PRId64, - out_field, in_selected_option_idx); - } - - in_option_field = bt_field_variant_borrow_selected_option_field_const(in_field); - out_option_field = bt_field_variant_borrow_selected_option_field(out_field); - - copy_field_content(in_option_field, out_option_field); - - break; - } - default: - abort(); - } - BT_LOGD("Copied content of field: in-f-addr=%p, out-f-addr=%p", - in_field, out_field); -} diff --git a/plugins/lttng-utils/debug-info/trace-ir-data-copy.h b/plugins/lttng-utils/debug-info/trace-ir-data-copy.h deleted file mode 100644 index b9259769..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-data-copy.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H - -/* - * Babeltrace - Trace IR data object copy - * - * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "trace-ir-mapping.h" - -BT_HIDDEN -void copy_trace_content(const bt_trace *in_trace, bt_trace *out_trace); -BT_HIDDEN -void copy_stream_content(const bt_stream *in_stream, bt_stream *out_stream); -BT_HIDDEN -void copy_packet_content(const bt_packet *in_packet, bt_packet *out_packet); -BT_HIDDEN -void copy_event_content(const bt_event *in_event, bt_event *out_event); -BT_HIDDEN -void copy_field_content(const bt_field *in_field, bt_field *out_field); - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H */ diff --git a/plugins/lttng-utils/debug-info/trace-ir-mapping.c b/plugins/lttng-utils/debug-info/trace-ir-mapping.c deleted file mode 100644 index 61bfb016..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-mapping.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Babeltrace - Mapping of IR metadata and data object between input and output - * trace - * - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-MAPPING" -#include "logging.h" - -#include - -#include -#include -/* For bt_property_availability */ -#include - -#include "debug-info.h" -#include "trace-ir-data-copy.h" -#include "trace-ir-mapping.h" -#include "trace-ir-metadata-copy.h" - -static -bt_trace_class *create_new_mapped_trace_class(struct trace_ir_maps *ir_maps, - const bt_trace_class *in_trace_class) -{ - int ret; - bt_trace_class *out_trace_class; - - BT_LOGD("Creating new mapped trace class: in-tc-addr=%p", in_trace_class); - - BT_ASSERT(ir_maps); - BT_ASSERT(in_trace_class); - - /* Create the ouput trace class. */ - out_trace_class = bt_trace_class_create(ir_maps->self_comp); - if (!out_trace_class) { - BT_LOGE_STR("Error create output trace class"); - goto end; - } - - /* If not, create a new one and add it to the mapping. */ - ret = copy_trace_class_content(in_trace_class, out_trace_class); - if (ret) { - BT_LOGE_STR("Error copy content to output trace class"); - out_trace_class = NULL; - goto end; - } - - BT_LOGD("Created new mapped trace class: in-tc-addr=%p, out-tc-addr=%p", - in_trace_class, out_trace_class); - -end: - return out_trace_class; -} - -static -bt_trace *create_new_mapped_trace(struct trace_ir_maps *ir_maps, - const bt_trace *in_trace) -{ - bt_trace *out_trace; - const bt_trace_class *in_trace_class; - struct trace_ir_metadata_maps *metadata_maps; - - BT_LOGD("Creating new mapped trace: in-t-addr=%p", in_trace); - BT_ASSERT(ir_maps); - BT_ASSERT(in_trace); - - in_trace_class = bt_trace_borrow_class_const(in_trace); - metadata_maps = borrow_metadata_maps_from_input_trace_class(ir_maps, - in_trace_class); - - if (!metadata_maps->output_trace_class) { - metadata_maps->output_trace_class = - create_new_mapped_trace_class(ir_maps, in_trace_class); - if (!metadata_maps->output_trace_class) { - out_trace = NULL; - goto end; - } - } - - out_trace = bt_trace_create(metadata_maps->output_trace_class); - if (!out_trace) { - BT_LOGE_STR("Error create output trace"); - goto end; - } - - /* If not, create a new one and add it to the mapping. */ - copy_trace_content(in_trace, out_trace); - - BT_LOGD("Created new mapped trace: in-t-addr=%p, out-t-addr=%p", - in_trace, out_trace); -end: - return out_trace; -} - -static -bt_stream_class *borrow_mapped_stream_class(struct trace_ir_metadata_maps *md_maps, - const bt_stream_class *in_stream_class) -{ - BT_ASSERT(md_maps); - BT_ASSERT(in_stream_class); - - return g_hash_table_lookup(md_maps->stream_class_map, - (gpointer) in_stream_class); -} - -static -bt_stream_class *create_new_mapped_stream_class(struct trace_ir_maps *ir_maps, - const bt_stream_class *in_stream_class) -{ - int ret; - bt_stream_class *out_stream_class; - struct trace_ir_metadata_maps *md_maps; - - BT_LOGD("Creating new mapped stream class: in-sc-addr=%p", - in_stream_class); - - md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, - in_stream_class); - - BT_ASSERT(md_maps); - BT_ASSERT(in_stream_class); - BT_ASSERT(!borrow_mapped_stream_class(md_maps, in_stream_class)); - - /* Create an out_stream_class. */ - out_stream_class = bt_stream_class_create_with_id( - md_maps->output_trace_class, - bt_stream_class_get_id(in_stream_class)); - if (!out_stream_class) { - BT_LOGE_STR("Error create output stream class"); - goto end; - } - - /* If not, create a new one and add it to the mapping. */ - ret = copy_stream_class_content(ir_maps, in_stream_class, - out_stream_class); - if (ret) { - BT_LOGE_STR("Error copy content to output stream class"); - out_stream_class = NULL; - goto end; - } - - g_hash_table_insert(md_maps->stream_class_map, - (gpointer) in_stream_class, out_stream_class); - - BT_LOGD("Created new mapped stream class: in-sc-addr=%p, out-sc-addr=%p", - in_stream_class, out_stream_class); - -end: - return out_stream_class; -} - -static -bt_stream *borrow_mapped_stream(struct trace_ir_data_maps *d_maps, - const bt_stream *in_stream) -{ - BT_ASSERT(d_maps); - BT_ASSERT(in_stream); - - return g_hash_table_lookup(d_maps->stream_map, (gpointer) in_stream); -} - -BT_HIDDEN -bt_stream *trace_ir_mapping_create_new_mapped_stream( - struct trace_ir_maps *ir_maps, - const bt_stream *in_stream) -{ - struct trace_ir_data_maps *d_maps; - struct trace_ir_metadata_maps *md_maps; - const bt_stream_class *in_stream_class; - const bt_trace *in_trace; - bt_stream_class *out_stream_class; - bt_stream *out_stream = NULL; - - BT_LOGD("Creating new mapped stream: in-s-addr=%p", in_stream); - - BT_ASSERT(ir_maps); - BT_ASSERT(in_stream); - - in_trace = bt_stream_borrow_trace_const(in_stream); - - d_maps = borrow_data_maps_from_input_trace(ir_maps, in_trace); - if (!d_maps->output_trace) { - d_maps->output_trace = create_new_mapped_trace(ir_maps, in_trace); - if (!d_maps->output_trace) { - goto end; - } - } - - BT_ASSERT(d_maps->output_trace); - BT_ASSERT(!borrow_mapped_stream(d_maps, in_stream)); - - in_stream_class = bt_stream_borrow_class_const(in_stream); - md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, in_stream_class); - out_stream_class = borrow_mapped_stream_class(md_maps, in_stream_class); - if (!out_stream_class) { - out_stream_class = create_new_mapped_stream_class(ir_maps, - in_stream_class); - if (!out_stream_class) { - goto end; - } - } - BT_ASSERT(out_stream_class); - - out_stream = bt_stream_create_with_id(out_stream_class, - d_maps->output_trace, bt_stream_get_id(in_stream)); - if (!out_stream) { - BT_LOGE_STR("Error creating output stream"); - goto end; - } - /* - * Release our ref since the trace object will be managing the life - * time of the stream objects. - */ - - copy_stream_content(in_stream, out_stream); - - g_hash_table_insert(d_maps->stream_map, (gpointer) in_stream, - out_stream); - - BT_LOGD("Created new mapped stream: in-s-addr=%p, out-s-addr=%p", - in_stream, out_stream); - -end: - return out_stream; -} - -BT_HIDDEN -bt_stream *trace_ir_mapping_borrow_mapped_stream(struct trace_ir_maps *ir_maps, - const bt_stream *in_stream) -{ - BT_ASSERT(ir_maps); - BT_ASSERT(in_stream); - struct trace_ir_data_maps *d_maps; - - d_maps = borrow_data_maps_from_input_stream(ir_maps, in_stream); - /* Return the mapped stream. */ - return borrow_mapped_stream(d_maps, in_stream); -} - -static inline -bt_event_class *borrow_mapped_event_class(struct trace_ir_metadata_maps *md_maps, - const bt_event_class *in_event_class) -{ - return g_hash_table_lookup(md_maps->event_class_map, - (gpointer) in_event_class); -} - -BT_HIDDEN -bt_event_class *trace_ir_mapping_create_new_mapped_event_class( - struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class) -{ - bt_event_class *out_event_class; - const bt_trace_class *in_trace_class; - const bt_stream_class *in_stream_class; - bt_stream_class *out_stream_class; - struct trace_ir_metadata_maps *md_maps; - int ret; - - BT_LOGD("Creating new mapped event class: in-ec-addr=%p", - in_event_class); - - BT_ASSERT(ir_maps); - BT_ASSERT(in_event_class); - - in_trace_class = bt_stream_class_borrow_trace_class_const( - bt_event_class_borrow_stream_class_const( - in_event_class)); - - md_maps = borrow_metadata_maps_from_input_trace_class(ir_maps, in_trace_class); - - BT_ASSERT(!borrow_mapped_event_class(md_maps, in_event_class)); - - in_stream_class = - bt_event_class_borrow_stream_class_const(in_event_class); - BT_ASSERT(in_stream_class); - - /* Get the right output stream class to add the new event class to. */ - out_stream_class = borrow_mapped_stream_class(md_maps, in_stream_class); - BT_ASSERT(out_stream_class); - - /* Create an output event class. */ - out_event_class = bt_event_class_create_with_id(out_stream_class, - bt_event_class_get_id(in_event_class)); - if (!out_event_class) { - BT_LOGE_STR("Error creating output event class"); - goto end; - } - - /* If not, create a new one and add it to the mapping. */ - ret = copy_event_class_content(ir_maps, in_event_class, - out_event_class); - if (ret) { - BT_LOGE_STR("Error copy content to output event class"); - out_event_class = NULL; - goto end; - } - - g_hash_table_insert(md_maps->event_class_map, - (gpointer) in_event_class, out_event_class); - - BT_LOGD("Created new mapped event class: in-ec-addr=%p, out-ec-addr=%p", - in_event_class, out_event_class); - -end: - return out_event_class; -} - -BT_HIDDEN -bt_event_class *trace_ir_mapping_borrow_mapped_event_class( - struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class) -{ - struct trace_ir_metadata_maps *md_maps; - - BT_ASSERT(ir_maps); - BT_ASSERT(in_event_class); - - md_maps = borrow_metadata_maps_from_input_event_class(ir_maps, in_event_class); - - /* Return the mapped event_class. */ - return borrow_mapped_event_class(md_maps, in_event_class); -} - -static inline -bt_packet *borrow_mapped_packet(struct trace_ir_data_maps *d_maps, - const bt_packet *in_packet) -{ - BT_ASSERT(d_maps); - BT_ASSERT(in_packet); - - return g_hash_table_lookup(d_maps->packet_map, - (gpointer) in_packet); -} - -BT_HIDDEN -bt_packet *trace_ir_mapping_create_new_mapped_packet( - struct trace_ir_maps *ir_maps, - const bt_packet *in_packet) -{ - struct trace_ir_data_maps *d_maps; - const bt_trace *in_trace; - const bt_stream *in_stream; - bt_packet *out_packet; - bt_stream *out_stream; - - BT_LOGD("Creating new mapped packet: in-p-addr=%p", in_packet); - - in_stream = bt_packet_borrow_stream_const(in_packet); - in_trace = bt_stream_borrow_trace_const(in_stream); - d_maps = borrow_data_maps_from_input_trace(ir_maps, in_trace); - - /* There should never be a mapped packet. */ - BT_ASSERT(!borrow_mapped_packet(d_maps, in_packet)); - - BT_ASSERT(in_stream); - - /* Get output stream corresponding to this input stream. */ - out_stream = borrow_mapped_stream(d_maps, in_stream); - BT_ASSERT(out_stream); - - /* Create the output packet. */ - out_packet = bt_packet_create(out_stream); - if (!out_packet) { - BT_LOGE_STR("Error create output packet"); - goto end; - } - - /* - * Release our ref since the stream object will be managing the life - * time of the packet objects. - */ - copy_packet_content(in_packet, out_packet); - - g_hash_table_insert(d_maps->packet_map, - (gpointer) in_packet, out_packet); - - BT_LOGD("Created new mapped packet: in-p-addr=%p, out-p-addr=%p", - in_packet, out_packet); - -end: - return out_packet; -} - -BT_HIDDEN -bt_packet *trace_ir_mapping_borrow_mapped_packet(struct trace_ir_maps *ir_maps, - const bt_packet *in_packet) -{ - struct trace_ir_data_maps *d_maps; - BT_ASSERT(ir_maps); - BT_ASSERT(in_packet); - - d_maps = borrow_data_maps_from_input_packet(ir_maps, in_packet); - - return borrow_mapped_packet(d_maps, in_packet); -} - -BT_HIDDEN -void trace_ir_mapping_remove_mapped_packet(struct trace_ir_maps *ir_maps, - const bt_packet *in_packet) -{ - gboolean ret; - - struct trace_ir_data_maps *d_maps; - BT_ASSERT(ir_maps); - BT_ASSERT(in_packet); - - d_maps = borrow_data_maps_from_input_packet(ir_maps, in_packet); - - ret = g_hash_table_remove(d_maps->packet_map, in_packet); - - BT_ASSERT(ret); -} - -BT_HIDDEN -void trace_ir_mapping_remove_mapped_stream(struct trace_ir_maps *ir_maps, - const bt_stream *in_stream) -{ - gboolean ret; - struct trace_ir_data_maps *d_maps; - - BT_ASSERT(ir_maps); - BT_ASSERT(in_stream); - - d_maps = borrow_data_maps_from_input_stream(ir_maps, in_stream); - - ret = g_hash_table_remove(d_maps->stream_map, in_stream); - - BT_ASSERT(ret); -} - -static -void trace_ir_metadata_maps_remove_func(const bt_trace_class *in_trace_class, - void *data) -{ - struct trace_ir_maps *maps = (struct trace_ir_maps *) data; - if (maps->metadata_maps) { - gboolean ret; - ret = g_hash_table_remove(maps->metadata_maps, - (gpointer) in_trace_class); - BT_ASSERT(ret); - } -} - -static -void trace_ir_data_maps_remove_func(const bt_trace *in_trace, void *data) -{ - struct trace_ir_maps *maps = (struct trace_ir_maps *) data; - if (maps->data_maps) { - gboolean ret; - ret = g_hash_table_remove(maps->data_maps, (gpointer) in_trace); - BT_ASSERT(ret); - } -} - -struct trace_ir_data_maps *trace_ir_data_maps_create(struct trace_ir_maps *ir_maps, - const bt_trace *in_trace) -{ - struct trace_ir_data_maps *d_maps = - g_new0(struct trace_ir_data_maps, 1); - if (!d_maps) { - BT_LOGE_STR("Error allocating trace_ir_maps"); - goto error; - } - - d_maps->input_trace = in_trace; - - /* Create the hashtables used to map data objects. */ - d_maps->stream_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL,(GDestroyNotify) bt_stream_put_ref); - d_maps->packet_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL,(GDestroyNotify) bt_packet_put_ref); - - bt_trace_add_destruction_listener(in_trace, trace_ir_data_maps_remove_func, - ir_maps, &d_maps->destruction_listener_id); -error: - return d_maps; -} - -struct trace_ir_metadata_maps *trace_ir_metadata_maps_create( - struct trace_ir_maps *ir_maps, - const bt_trace_class *in_trace_class) -{ - struct trace_ir_metadata_maps *md_maps = - g_new0(struct trace_ir_metadata_maps, 1); - if (!md_maps) { - BT_LOGE_STR("Error allocating trace_ir_maps"); - goto error; - } - - md_maps->input_trace_class = in_trace_class; - /* - * Create the field class resolving context. This is needed to keep - * track of the field class already copied in order to do the field - * path resolution correctly. - */ - md_maps->fc_resolving_ctx = - g_new0(struct field_class_resolving_context, 1); - if (!md_maps->fc_resolving_ctx) { - BT_LOGE_STR("Error allocating field_class_resolving_context"); - goto error; - } - - /* Create the hashtables used to map metadata objects. */ - md_maps->stream_class_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_stream_class_put_ref); - md_maps->event_class_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_event_class_put_ref); - md_maps->field_class_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_field_class_put_ref); - md_maps->clock_class_map = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_clock_class_put_ref); - - bt_trace_class_add_destruction_listener(in_trace_class, - trace_ir_metadata_maps_remove_func, - ir_maps, &md_maps->destruction_listener_id); -error: - return md_maps; -} - -BT_HIDDEN -void trace_ir_data_maps_destroy(struct trace_ir_data_maps *maps) -{ - bt_trace_status status; - if (!maps) { - return; - } - - if (maps->packet_map) { - g_hash_table_destroy(maps->packet_map); - } - - if (maps->stream_map) { - g_hash_table_destroy(maps->stream_map); - } - - if (maps->output_trace) { - bt_trace_put_ref(maps->output_trace); - } - - status = bt_trace_remove_destruction_listener(maps->input_trace, - maps->destruction_listener_id); - if (status != BT_TRACE_STATUS_OK) { - BT_LOGD("Trace destruction listener removal failed."); - } - - g_free(maps); -} - -BT_HIDDEN -void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *maps) -{ - bt_trace_class_status status; - if (!maps) { - return; - } - - if (maps->stream_class_map) { - g_hash_table_destroy(maps->stream_class_map); - } - - if (maps->event_class_map) { - g_hash_table_destroy(maps->event_class_map); - } - - if (maps->field_class_map) { - g_hash_table_destroy(maps->field_class_map); - } - - if (maps->clock_class_map) { - g_hash_table_destroy(maps->clock_class_map); - } - - if (maps->fc_resolving_ctx) { - g_free(maps->fc_resolving_ctx); - } - - if (maps->output_trace_class) { - bt_trace_class_put_ref(maps->output_trace_class); - } - - status = bt_trace_class_remove_destruction_listener(maps->input_trace_class, - maps->destruction_listener_id); - if (status != BT_TRACE_CLASS_STATUS_OK) { - BT_LOGD("Trace destruction listener removal failed."); - } - - g_free(maps); -} - -void trace_ir_maps_clear(struct trace_ir_maps *maps) -{ - if (maps->data_maps) { - g_hash_table_remove_all(maps->data_maps); - } - - if (maps->metadata_maps) { - g_hash_table_remove_all(maps->metadata_maps); - } -} - -BT_HIDDEN -void trace_ir_maps_destroy(struct trace_ir_maps *maps) -{ - if (!maps) { - return; - } - - if (maps->debug_info_field_class_name) { - g_free(maps->debug_info_field_class_name); - } - - if (maps->data_maps) { - g_hash_table_destroy(maps->data_maps); - maps->data_maps = NULL; - } - - if (maps->metadata_maps) { - g_hash_table_destroy(maps->metadata_maps); - maps->metadata_maps = NULL; - } - - g_free(maps); -} - -BT_HIDDEN -struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp, - const char *debug_info_field_name) -{ - struct trace_ir_maps *trace_ir_maps = - g_new0(struct trace_ir_maps, 1); - if (!trace_ir_maps) { - BT_LOGE_STR("Error allocating trace_ir_maps"); - goto error; - } - - /* Copy debug info field name received from the user. */ - trace_ir_maps->debug_info_field_class_name = - g_strdup(debug_info_field_name); - if (!trace_ir_maps->debug_info_field_class_name) { - BT_LOGE_STR("Cannot copy debug info field name"); - goto error; - } - - trace_ir_maps->self_comp = self_comp; - - trace_ir_maps->data_maps = g_hash_table_new_full(g_direct_hash, - g_direct_equal, (GDestroyNotify) NULL, - (GDestroyNotify) trace_ir_data_maps_destroy); - - trace_ir_maps->metadata_maps = g_hash_table_new_full(g_direct_hash, - g_direct_equal, (GDestroyNotify) NULL, - (GDestroyNotify) trace_ir_metadata_maps_destroy); - - goto end; -error: - trace_ir_maps_destroy(trace_ir_maps); - trace_ir_maps = NULL; -end: - return trace_ir_maps; -} diff --git a/plugins/lttng-utils/debug-info/trace-ir-mapping.h b/plugins/lttng-utils/debug-info/trace-ir-mapping.h deleted file mode 100644 index 0f29e23e..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-mapping.h +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H -/* - * Copyright 2019 Francis Deslauriers francis.deslauriers@efficios.com> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include -#include - -#include "debug-info.h" - -/* Used to resolve field paths for dynamic arrays and variant field classes. */ -struct field_class_resolving_context { - /* Weak reference. Owned by input stream class. */ - const bt_field_class *packet_context; - /* Weak reference. Owned by input stream class. */ - const bt_field_class *event_common_context; - /* Weak reference. Owned by input event class. */ - const bt_field_class *event_specific_context; - /* Weak reference. Owned by input event class. */ - const bt_field_class *event_payload; -}; - -struct trace_ir_metadata_maps { - const bt_trace_class *input_trace_class; - bt_trace_class *output_trace_class; - - /* - * Map between input stream class and its corresponding output stream - * class. - * input stream class: weak reference. Owned by an upstream - * component. - * output stream class: owned by this structure. - */ - GHashTable *stream_class_map; - - /* - * Map between input event class and its corresponding output event - * class. - * input event class: weak reference. Owned by an upstream component. - * output event class: owned by this structure. - */ - GHashTable *event_class_map; - - /* - * Map between input field class and its corresponding output field - * class. - * input field class: weak reference. Owned by an upstream component. - * output field class: owned by this structure. - */ - GHashTable *field_class_map; - - /* - * Map between input clock class and its corresponding output clock - * class. - * input clock class: weak reference. Owned by an upstream component. - * output clock class: owned by this structure. - */ - GHashTable *clock_class_map; - - struct field_class_resolving_context *fc_resolving_ctx; - - uint64_t destruction_listener_id; -}; - -struct trace_ir_data_maps { - const bt_trace *input_trace; - bt_trace *output_trace; - - /* - * Map between input stream its corresponding output stream. - * input stream: weak reference. Owned by an upstream component. - * output stream: owned by this structure. - */ - GHashTable *stream_map; - - /* - * Map between input packet its corresponding output packet. - * input packet: weak reference. Owned by an upstream packet component. - * output packet: owned by this structure. - */ - GHashTable *packet_map; - - uint64_t destruction_listener_id; -}; - -struct trace_ir_maps { - /* - * input trace -> trace_ir_data_maps. - * input trace: weak reference. Owned by an upstream component. - * trace_ir_data_maps: Owned by this structure. - */ - GHashTable *data_maps; - - /* - * input trace class -> trace_ir_metadata_maps. - * input trace class: weak reference. Owned by an upstream component. - * trace_ir_metadata_maps: Owned by this structure. - */ - GHashTable *metadata_maps; - - char *debug_info_field_class_name; - - bt_self_component *self_comp; -}; - -BT_HIDDEN -struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp, - const char *debug_info_field_name); - -BT_HIDDEN -void trace_ir_maps_clear(struct trace_ir_maps *maps); - -BT_HIDDEN -void trace_ir_maps_destroy(struct trace_ir_maps *maps); - -BT_HIDDEN -struct trace_ir_data_maps *trace_ir_data_maps_create( - struct trace_ir_maps *ir_maps, - const bt_trace *in_trace); - -BT_HIDDEN -void trace_ir_data_maps_destroy(struct trace_ir_data_maps *d_maps); - -BT_HIDDEN -struct trace_ir_metadata_maps *trace_ir_metadata_maps_create( - struct trace_ir_maps *ir_maps, - const bt_trace_class *in_trace_class); - -BT_HIDDEN -void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *md_maps); - -BT_HIDDEN -bt_stream *trace_ir_mapping_create_new_mapped_stream( - struct trace_ir_maps *ir_maps, - const bt_stream *in_stream); - -BT_HIDDEN -bt_stream *trace_ir_mapping_borrow_mapped_stream( - struct trace_ir_maps *ir_maps, - const bt_stream *in_stream); - -BT_HIDDEN -void trace_ir_mapping_remove_mapped_stream( - struct trace_ir_maps *ir_maps, - const bt_stream *in_stream); - -BT_HIDDEN -bt_event_class *trace_ir_mapping_create_new_mapped_event_class( - struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class); - -BT_HIDDEN -bt_event_class *trace_ir_mapping_borrow_mapped_event_class( - struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class); - -BT_HIDDEN -bt_packet *trace_ir_mapping_create_new_mapped_packet( - struct trace_ir_maps *ir_maps, - const bt_packet *in_packet); - -BT_HIDDEN -bt_packet *trace_ir_mapping_borrow_mapped_packet( - struct trace_ir_maps *ir_maps, - const bt_packet *in_packet); - -BT_HIDDEN -void trace_ir_mapping_remove_mapped_packet( - struct trace_ir_maps *ir_maps, - const bt_packet *in_packet); - -static inline -struct trace_ir_data_maps *borrow_data_maps_from_input_trace( - struct trace_ir_maps *ir_maps, const bt_trace *in_trace) -{ - BT_ASSERT(ir_maps); - BT_ASSERT(in_trace); - - struct trace_ir_data_maps *d_maps = - g_hash_table_lookup(ir_maps->data_maps, (gpointer) in_trace); - if (!d_maps) { - d_maps = trace_ir_data_maps_create(ir_maps, in_trace); - g_hash_table_insert(ir_maps->data_maps, (gpointer) in_trace, d_maps); - } - - return d_maps; -} - -static inline -struct trace_ir_data_maps *borrow_data_maps_from_input_stream( - struct trace_ir_maps *ir_maps, const bt_stream *in_stream) -{ - BT_ASSERT(ir_maps); - BT_ASSERT(in_stream); - - return borrow_data_maps_from_input_trace(ir_maps, - bt_stream_borrow_trace_const(in_stream)); -} - -static inline -struct trace_ir_data_maps *borrow_data_maps_from_input_packet( - struct trace_ir_maps *ir_maps, const bt_packet *in_packet) -{ - BT_ASSERT(ir_maps); - BT_ASSERT(in_packet); - - return borrow_data_maps_from_input_stream(ir_maps, - bt_packet_borrow_stream_const(in_packet)); -} - -static inline -struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_trace_class( - struct trace_ir_maps *ir_maps, - const bt_trace_class *in_trace_class) -{ - BT_ASSERT(ir_maps); - BT_ASSERT(in_trace_class); - - struct trace_ir_metadata_maps *md_maps = - g_hash_table_lookup(ir_maps->metadata_maps, - (gpointer) in_trace_class); - if (!md_maps) { - md_maps = trace_ir_metadata_maps_create(ir_maps, in_trace_class); - g_hash_table_insert(ir_maps->metadata_maps, - (gpointer) in_trace_class, md_maps); - } - - return md_maps; -} - -static inline -struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_stream_class( - struct trace_ir_maps *ir_maps, - const bt_stream_class *in_stream_class) { - - BT_ASSERT(in_stream_class); - - return borrow_metadata_maps_from_input_trace_class(ir_maps, - bt_stream_class_borrow_trace_class_const(in_stream_class)); -} - -static inline -struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_event_class( - struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class) { - - BT_ASSERT(in_event_class); - - return borrow_metadata_maps_from_input_stream_class(ir_maps, - bt_event_class_borrow_stream_class_const(in_event_class)); -} - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H */ diff --git a/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c b/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c deleted file mode 100644 index 55bb953a..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Babeltrace - Trace IR metadata object copy - * - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-METADATA-COPY" -#include "logging.h" - -#include -#include - -#include - -#include "trace-ir-metadata-copy.h" -#include "trace-ir-metadata-field-class-copy.h" -#include "utils.h" - -BT_HIDDEN -int copy_trace_class_content(const bt_trace_class *in_trace_class, - bt_trace_class *out_trace_class) -{ - int ret = 0; - uint64_t i, env_field_count; - const char *in_trace_class_name; - - BT_LOGD("Copying content of trace class: in-tc-addr=%p, out-tc-addr=%p", - in_trace_class, out_trace_class); - - /* Use the same stream class ids as in the origin trace class. */ - bt_trace_class_set_assigns_automatic_stream_class_id(out_trace_class, - BT_FALSE); - - in_trace_class_name = bt_trace_class_get_name(in_trace_class); - if (in_trace_class_name) { - bt_trace_class_set_name(out_trace_class, in_trace_class_name); - } - - /* - * Do not copy the trace class UUID as it may be modified and should no - * longer have the same UUID. - */ - - /* - * Go over all the entries in the environment section of the trace class - * and copy the content to the new trace class. - */ - env_field_count = bt_trace_class_get_environment_entry_count(in_trace_class); - for (i = 0; i < env_field_count; i++) { - const char *value_name; - const bt_value *value = NULL; - bt_trace_class_status trace_class_status; - - bt_trace_class_borrow_environment_entry_by_index_const( - in_trace_class, i, &value_name, &value); - - BT_LOGD("Copying trace class environnement entry: " - "index=%" PRId64 ", value-addr=%p, value-name=%s", - i, value, value_name); - - BT_ASSERT(value_name); - BT_ASSERT(value); - - if (bt_value_is_signed_integer(value)) { - trace_class_status = - bt_trace_class_set_environment_entry_integer( - out_trace_class, value_name, - bt_value_signed_integer_get( - value)); - } else if (bt_value_is_string(value)) { - trace_class_status = - bt_trace_class_set_environment_entry_string( - out_trace_class, value_name, - bt_value_string_get(value)); - } else { - abort(); - } - - if (trace_class_status != BT_TRACE_CLASS_STATUS_OK) { - ret = -1; - goto error; - } - } - - BT_LOGD("Copied content of trace class: in-tc-addr=%p, out-tc-addr=%p", - in_trace_class, out_trace_class); -error: - return ret; -} - -static -int copy_clock_class_content(const bt_clock_class *in_clock_class, - bt_clock_class *out_clock_class) -{ - bt_clock_class_status status; - const char *clock_class_name, *clock_class_description; - int64_t seconds; - uint64_t cycles; - bt_uuid in_uuid; - int ret = 0; - - BT_LOGD("Copying content of clock class: in-cc-addr=%p, out-cc-addr=%p", - in_clock_class, out_clock_class); - - clock_class_name = bt_clock_class_get_name(in_clock_class); - - if (clock_class_name) { - status = bt_clock_class_set_name(out_clock_class, clock_class_name); - if (status != BT_CLOCK_CLASS_STATUS_OK) { - BT_LOGE("Error setting clock class' name cc-addr=%p, name=%p", - out_clock_class, clock_class_name); - out_clock_class = NULL; - ret = -1; - goto error; - } - } - - clock_class_description = bt_clock_class_get_description(in_clock_class); - - if (clock_class_description) { - status = bt_clock_class_set_description(out_clock_class, - clock_class_description); - if (status != BT_CLOCK_CLASS_STATUS_OK) { - BT_LOGE("Error setting clock class' description cc-addr=%p, " - "name=%p", out_clock_class, clock_class_description); - out_clock_class = NULL; - ret = -1; - goto error; - } - } - - in_uuid = bt_clock_class_get_uuid(in_clock_class); - if (in_uuid) { - bt_clock_class_set_uuid(out_clock_class, in_uuid); - } - - bt_clock_class_set_frequency(out_clock_class, - bt_clock_class_get_frequency(in_clock_class)); - bt_clock_class_set_precision(out_clock_class, - bt_clock_class_get_precision(in_clock_class)); - bt_clock_class_get_offset(in_clock_class, &seconds, &cycles); - bt_clock_class_set_offset(out_clock_class, seconds, cycles); - bt_clock_class_set_origin_is_unix_epoch(out_clock_class, - bt_clock_class_origin_is_unix_epoch(in_clock_class)); - - BT_LOGD("Copied content of clock class: in-cc-addr=%p, out-cc-addr=%p", - in_clock_class, out_clock_class); - -error: - return ret; -} - -static -bt_clock_class *borrow_mapped_clock_class( - struct trace_ir_metadata_maps *md_maps, - const bt_clock_class *in_clock_class) -{ - BT_ASSERT(md_maps); - BT_ASSERT(in_clock_class); - - return g_hash_table_lookup(md_maps->clock_class_map, - (gpointer) in_clock_class); -} - -static -bt_clock_class *create_new_mapped_clock_class( - bt_self_component *self_comp, - struct trace_ir_metadata_maps *md_maps, - const bt_clock_class *in_clock_class) -{ - bt_clock_class *out_clock_class; - int ret; - - BT_LOGD("Creating new mapped clock class: in-cc-addr=%p", - in_clock_class); - - BT_ASSERT(md_maps); - BT_ASSERT(in_clock_class); - - BT_ASSERT(!borrow_mapped_clock_class(md_maps, in_clock_class)); - - out_clock_class = bt_clock_class_create(self_comp); - if (!out_clock_class) { - BT_LOGE_STR("Cannot create clock class"); - goto end; - } - /* If not, create a new one and add it to the mapping. */ - ret = copy_clock_class_content(in_clock_class, out_clock_class); - if (ret) { - BT_LOGE_STR("Cannot copy clock class"); - goto end; - } - - g_hash_table_insert(md_maps->clock_class_map, - (gpointer) in_clock_class, out_clock_class); - - BT_LOGD("Created new mapped clock class: in-cc-addr=%p, out-cc-addr=%p", - in_clock_class, out_clock_class); -end: - return out_clock_class; -} - -BT_HIDDEN -int copy_stream_class_content(struct trace_ir_maps *ir_maps, - const bt_stream_class *in_stream_class, - bt_stream_class *out_stream_class) -{ - struct trace_ir_metadata_maps *md_maps; - const bt_clock_class *in_clock_class; - bt_clock_class *out_clock_class; - const bt_field_class *in_packet_context_fc, *in_common_context_fc; - bt_field_class *out_packet_context_fc, *out_common_context_fc; - bt_stream_class_status status; - const char *in_name; - int ret = 0; - - BT_LOGD("Copying content of stream class: in-sc-addr=%p, out-sc-addr=%p", - in_stream_class, out_stream_class); - - md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, in_stream_class); - in_clock_class = bt_stream_class_borrow_default_clock_class_const( - in_stream_class); - - if (in_clock_class) { - /* Copy the clock class. */ - out_clock_class = - borrow_mapped_clock_class(md_maps, in_clock_class); - if (!out_clock_class) { - out_clock_class = create_new_mapped_clock_class( - ir_maps->self_comp, md_maps, - in_clock_class); - } - bt_stream_class_set_default_clock_class(out_stream_class, - out_clock_class); - - } - - bt_stream_class_set_packets_have_beginning_default_clock_snapshot( - out_stream_class, - bt_stream_class_packets_have_beginning_default_clock_snapshot( - in_stream_class)); - bt_stream_class_set_packets_have_end_default_clock_snapshot( - out_stream_class, - bt_stream_class_packets_have_end_default_clock_snapshot( - in_stream_class)); - bt_stream_class_set_supports_discarded_events( - out_stream_class, - bt_stream_class_supports_discarded_events(in_stream_class), - bt_stream_class_discarded_events_have_default_clock_snapshots( - in_stream_class)); - bt_stream_class_set_supports_discarded_packets( - out_stream_class, - bt_stream_class_supports_discarded_packets(in_stream_class), - bt_stream_class_discarded_packets_have_default_clock_snapshots( - in_stream_class)); - - in_name = bt_stream_class_get_name(in_stream_class); - if (in_name) { - status = bt_stream_class_set_name(out_stream_class, in_name); - if (status != BT_STREAM_CLASS_STATUS_OK) { - BT_LOGE("Error set stream class name: out-sc-addr=%p, " - "name=%s", out_stream_class, in_name); - ret = -1; - goto error; - } - } - - bt_stream_class_set_assigns_automatic_stream_id(out_stream_class, - BT_FALSE); - bt_stream_class_set_assigns_automatic_event_class_id(out_stream_class, - BT_FALSE); - - /* - * Add the input packet context field class to the context to - * resolution in the further steps. - */ - in_packet_context_fc = - bt_stream_class_borrow_packet_context_field_class_const( - in_stream_class); - md_maps->fc_resolving_ctx->packet_context = - in_packet_context_fc; - - if (in_packet_context_fc) { - /* Copy packet context. */ - out_packet_context_fc = create_field_class_copy( - md_maps, in_packet_context_fc); - - ret = copy_field_class_content(md_maps, - in_packet_context_fc, out_packet_context_fc); - if (ret) { - ret = -1; - goto error; - } - - status = bt_stream_class_set_packet_context_field_class( - out_stream_class, out_packet_context_fc); - if (status != BT_STREAM_CLASS_STATUS_OK) { - BT_LOGE("Error setting stream class' packet context " - "field class: sc-addr=%p, packet-fc-addr=%p", - out_stream_class, out_packet_context_fc); - ret = -1; - goto error; - } - } - - /* - * Add the input common context field class to the context to - * resolution in the further steps. - */ - in_common_context_fc = - bt_stream_class_borrow_event_common_context_field_class_const( - in_stream_class); - md_maps->fc_resolving_ctx->event_common_context = - in_common_context_fc; - - if (in_common_context_fc) { - /* Copy common context. */ - /* TODO: I find it a bit awkward to have this special function - * here to add the debug-info field class. I would like to - * abstract that.*/ - out_common_context_fc = create_field_class_copy( - md_maps, in_common_context_fc); - - ret = copy_event_common_context_field_class_content( - md_maps, ir_maps->debug_info_field_class_name, - in_common_context_fc, out_common_context_fc); - if (ret) { - goto error; - } - - status = bt_stream_class_set_event_common_context_field_class( - out_stream_class, out_common_context_fc); - if (status != BT_STREAM_CLASS_STATUS_OK) { - BT_LOGE("Error setting stream class' packet context " - "field class: sc-addr=%p, packet-fc-addr=%p", - out_stream_class, out_common_context_fc); - ret = -1; - goto error; - } - } - - /* Set packet snapshot boolean fields. */ - BT_LOGD("Copied content of stream class: in-sc-addr=%p, out-sc-addr=%p", - in_stream_class, out_stream_class); -error: - return ret; -} - -BT_HIDDEN -int copy_event_class_content(struct trace_ir_maps *ir_maps, - const bt_event_class *in_event_class, - bt_event_class *out_event_class) -{ - struct trace_ir_metadata_maps *md_maps; - const char *in_event_class_name, *in_emf_uri; - bt_property_availability prop_avail; - bt_event_class_log_level log_level; - bt_event_class_status status; - bt_field_class *out_specific_context_fc, *out_payload_fc; - const bt_field_class *in_event_specific_context, *in_event_payload; - int ret = 0; - - BT_LOGD("Copying content of event class: in-ec-addr=%p, out-ec-addr=%p", - in_event_class, out_event_class); - - /* Copy event class name. */ - in_event_class_name = bt_event_class_get_name(in_event_class); - if (in_event_class_name) { - status = bt_event_class_set_name(out_event_class, in_event_class_name); - if (status != BT_EVENT_CLASS_STATUS_OK) { - BT_LOGE("Error setting event class' name: ec-addr=%p, " - "name=%s", out_event_class, in_event_class_name); - ret = -1; - goto error; - } - } - - /* Copy event class loglevel. */ - prop_avail = bt_event_class_get_log_level(in_event_class, &log_level); - if (prop_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { - bt_event_class_set_log_level(out_event_class, - log_level); - } - - /* Copy event class emf uri. */ - in_emf_uri = bt_event_class_get_emf_uri(in_event_class); - if (in_emf_uri) { - status = bt_event_class_set_emf_uri(out_event_class, in_emf_uri); - if (status != BT_EVENT_CLASS_STATUS_OK) { - BT_LOGE("Error setting event class' emf uri: ec-addr=%p, " - "emf uri=%s", out_event_class, in_emf_uri); - ret = -1; - goto error; - } - } - - md_maps = borrow_metadata_maps_from_input_event_class(ir_maps, in_event_class); - /* - * Add the input event class' specific ctx to te - * context. - */ - in_event_specific_context = - bt_event_class_borrow_specific_context_field_class_const( - in_event_class); - - md_maps->fc_resolving_ctx->event_specific_context = - in_event_specific_context; - - if (in_event_specific_context) { - /* Copy the specific context of this event class. */ - out_specific_context_fc = create_field_class_copy(md_maps, - in_event_specific_context); - - ret = copy_field_class_content(md_maps, - in_event_specific_context, out_specific_context_fc); - if (ret) { - goto error; - } - /* - * Add the output specific context to the output event - * class. - */ - status = bt_event_class_set_specific_context_field_class( - out_event_class, out_specific_context_fc); - if (status != BT_EVENT_CLASS_STATUS_OK) { - BT_LOGE("Error setting event class' specific context " - "field class: ec-addr=%p, ctx-fc-addr=%p", - out_event_class, out_specific_context_fc); - ret = -1; - goto error; - } - } - - /* - * Add the input event class' payload field class to - * the context. - */ - in_event_payload = bt_event_class_borrow_payload_field_class_const( - in_event_class); - - md_maps->fc_resolving_ctx->event_payload = in_event_payload; - - if (in_event_payload) { - /* Copy the payload of this event class. */ - out_payload_fc = create_field_class_copy(md_maps, - in_event_payload); - ret = copy_field_class_content(md_maps, - in_event_payload, out_payload_fc); - if (ret) { - goto error; - } - - /* Add the output payload to the output event class. */ - status = bt_event_class_set_payload_field_class( - out_event_class, out_payload_fc); - if (status != BT_EVENT_CLASS_STATUS_OK) { - BT_LOGE("Error setting event class' payload " - "field class: ec-addr=%p, payload-fc-addr=%p", - out_event_class, out_payload_fc); - ret = -1; - goto error; - } - } - - BT_LOGD("Copied content of event class: in-ec-addr=%p, out-ec-addr=%p", - in_event_class, out_event_class); -error: - return ret; -} - -BT_HIDDEN -int copy_event_common_context_field_class_content( - struct trace_ir_metadata_maps *md_maps, - const char *debug_info_fc_name, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - bt_field_class_status status; - bt_field_class *debug_field_class = NULL, *bin_field_class = NULL, - *func_field_class = NULL, *src_field_class = NULL; - int ret = 0; - - BT_LOGD("Copying content of event common context field class: " - "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class); - - /* Copy the content of the input common context. */ - ret = copy_field_class_content(md_maps, in_field_class, out_field_class); - if (ret) { - goto error; - } - - /* - * If this event common context has the necessary fields to compute the - * debug information append the debug-info field class to the event - * common context. - */ - if (is_event_common_ctx_dbg_info_compatible(in_field_class, debug_info_fc_name)) { - /* - * The struct field and 3 sub-fields are not stored in the - * field class map because they don't have input equivalent. - * We need to put our reference each of these field classes - * once they are added to their respective containing field - * classes. - */ - debug_field_class = bt_field_class_structure_create( - md_maps->output_trace_class); - if (!debug_field_class) { - BT_LOGE_STR("Failed to create debug_info structure."); - ret = -1; - goto error; - } - - bin_field_class = bt_field_class_string_create( - md_maps->output_trace_class); - if (!bin_field_class) { - BT_LOGE_STR("Failed to create string for field=bin."); - ret = -1; - goto error; - } - - func_field_class = bt_field_class_string_create( - md_maps->output_trace_class); - if (!func_field_class) { - BT_LOGE_STR("Failed to create string for field=func."); - ret = -1; - goto error; - } - - src_field_class = bt_field_class_string_create( - md_maps->output_trace_class); - if (!src_field_class) { - BT_LOGE_STR("Failed to create string for field=src."); - ret = -1; - goto error; - } - - status = bt_field_class_structure_append_member( - debug_field_class, "bin", bin_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add a field to debug_info " - "struct: field=bin."); - ret = -1; - goto error; - } - BT_FIELD_CLASS_PUT_REF_AND_RESET(bin_field_class); - - status = bt_field_class_structure_append_member( - debug_field_class, "func", func_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add a field to debug_info " - "struct: field=func."); - ret = -1; - goto error; - } - BT_FIELD_CLASS_PUT_REF_AND_RESET(func_field_class); - - status = bt_field_class_structure_append_member( - debug_field_class, "src", src_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add a field to debug_info " - "struct: field=src."); - ret = -1; - goto error; - } - BT_FIELD_CLASS_PUT_REF_AND_RESET(src_field_class); - - /*Add the filled debug-info field class to the common context. */ - status = bt_field_class_structure_append_member(out_field_class, - debug_info_fc_name, - debug_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add debug_info field to " - "event common context."); - ret = -1; - goto error; - } - BT_FIELD_CLASS_PUT_REF_AND_RESET(debug_field_class); - } - BT_LOGD("Copied content of event common context field class: " - "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class); - goto end; - -error: - if (debug_field_class) { - bt_field_class_put_ref(debug_field_class); - } - if (bin_field_class) { - bt_field_class_put_ref(bin_field_class); - } - if (func_field_class) { - bt_field_class_put_ref(func_field_class); - } - if (src_field_class) { - bt_field_class_put_ref(src_field_class); - } -end: - return ret; -} - -BT_HIDDEN -bt_field_class *create_field_class_copy(struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class) -{ - return create_field_class_copy_internal(md_maps, in_field_class); -} - -BT_HIDDEN -int copy_field_class_content(struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - return copy_field_class_content_internal(md_maps, in_field_class, - out_field_class); -} diff --git a/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h b/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h deleted file mode 100644 index 9330546d..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H - -/* - * Babeltrace - Trace IR metadata object copy - * - * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "trace-ir-mapping.h" - -BT_HIDDEN -int copy_trace_class_content(const bt_trace_class *in_trace_class, - bt_trace_class *out_trace_class); - -BT_HIDDEN -int copy_stream_class_content(struct trace_ir_maps *trace_ir_maps, - const bt_stream_class *in_stream_class, - bt_stream_class *out_stream_class); - -BT_HIDDEN -int copy_event_class_content(struct trace_ir_maps *trace_ir_maps, - const bt_event_class *in_event_class, - bt_event_class *out_event_class); - -BT_HIDDEN -int copy_field_class_content(struct trace_ir_metadata_maps *trace_ir_metadata_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class); - -BT_HIDDEN -int copy_event_common_context_field_class_content( - struct trace_ir_metadata_maps *trace_ir_metadata_maps, - const char *debug_info_field_class_name, - const bt_field_class *in_field_class, - bt_field_class *out_field_class); - -BT_HIDDEN -bt_field_class *create_field_class_copy( - struct trace_ir_metadata_maps *trace_ir_metadata_maps, - const bt_field_class *in_field_class); - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H */ diff --git a/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c b/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c deleted file mode 100644 index 1247e598..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Babeltrace - Trace IR field copy - * - * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2018 Philippe Proulx - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-METADATA-FC-COPY" -#include "logging.h" - -#include -#include -#include -#include - -#include "trace-ir-metadata-copy.h" -#include "trace-ir-metadata-field-class-copy.h" - -/* - * This fonction walks througth the nested structures field class to resolve a - * field path object. A field path is made of indexes inside possibly nested - * structures ultimately leading to a field class. - */ -static -const bt_field_class *walk_field_path(const bt_field_path *fp, - const bt_field_class *fc) -{ - uint64_t i, fp_item_count; - const bt_field_class *curr_fc; - - BT_ASSERT(bt_field_class_get_type(fc) == BT_FIELD_CLASS_TYPE_STRUCTURE); - BT_LOGD("Walking field path on field class: fp-addr=%p, fc-addr=%p", - fp, fc); - - fp_item_count = bt_field_path_get_item_count(fp); - curr_fc = fc; - for (i = 0; i < fp_item_count; i++) { - bt_field_class_type fc_type = bt_field_class_get_type(curr_fc); - const bt_field_path_item *fp_item = - bt_field_path_borrow_item_by_index_const(fp, i); - - switch (fc_type) { - case BT_FIELD_CLASS_TYPE_STRUCTURE: - { - const bt_field_class_structure_member *member; - - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - member = bt_field_class_structure_borrow_member_by_index_const( - curr_fc, - bt_field_path_item_index_get_index(fp_item)); - curr_fc = bt_field_class_structure_member_borrow_field_class_const( - member); - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - { - const bt_field_class_variant_option *option; - - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_INDEX); - option = bt_field_class_variant_borrow_option_by_index_const( - curr_fc, - bt_field_path_item_index_get_index(fp_item)); - curr_fc = bt_field_class_variant_option_borrow_field_class_const( - option); - break; - } - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - BT_ASSERT(bt_field_path_item_get_type(fp_item) == - BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); - curr_fc = bt_field_class_array_borrow_element_field_class_const( - curr_fc); - break; - } - default: - abort(); - } - } - - return curr_fc; -} - -static -const bt_field_class *resolve_field_path_to_field_class(const bt_field_path *fp, - struct trace_ir_metadata_maps *md_maps) -{ - struct field_class_resolving_context *fc_resolving_ctx; - const bt_field_class *fc; - bt_scope fp_scope; - - BT_LOGD("Resolving field path: fp-addr=%p", fp); - - fc_resolving_ctx = md_maps->fc_resolving_ctx; - fp_scope = bt_field_path_get_root_scope(fp); - - switch (fp_scope) { - case BT_SCOPE_PACKET_CONTEXT: - fc = walk_field_path(fp, fc_resolving_ctx->packet_context); - break; - case BT_SCOPE_EVENT_COMMON_CONTEXT: - fc = walk_field_path(fp, fc_resolving_ctx->event_common_context); - break; - case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: - fc = walk_field_path(fp, fc_resolving_ctx->event_specific_context); - break; - case BT_SCOPE_EVENT_PAYLOAD: - fc = walk_field_path(fp, fc_resolving_ctx->event_payload); - break; - default: - abort(); - } - - return fc; -} - -static inline -void field_class_integer_set_props(const bt_field_class *input_fc, - bt_field_class *output_fc) -{ - bt_field_class_integer_set_preferred_display_base(output_fc, - bt_field_class_integer_get_preferred_display_base(input_fc)); - bt_field_class_integer_set_field_value_range(output_fc, - bt_field_class_integer_get_field_value_range(input_fc)); -} - -static inline -int field_class_unsigned_integer_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - BT_LOGD("Copying content of unsigned integer field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - field_class_integer_set_props(in_field_class, out_field_class); - - BT_LOGD("Copied content of unsigned integer field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - return 0; -} - -static inline -int field_class_signed_integer_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - BT_LOGD("Copying content of signed integer field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - field_class_integer_set_props(in_field_class, out_field_class); - - BT_LOGD("Copied content of signed integer field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - return 0; -} - -BT_HIDDEN -int field_class_unsigned_enumeration_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - uint64_t i, enum_mapping_count; - int ret = 0; - - BT_LOGD("Copying content of unsigned enumeration field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - /* Copy properties of the inner integer. */ - field_class_integer_set_props(in_field_class, out_field_class); - - /* Copy all enumeration entries. */ - enum_mapping_count = bt_field_class_enumeration_get_mapping_count(in_field_class); - for (i = 0; i < enum_mapping_count; i++) { - const char *label; - const bt_field_class_unsigned_enumeration_mapping *u_mapping; - const bt_field_class_enumeration_mapping *mapping; - uint64_t range_index, range_count; - - /* Get the ranges and the range count. */ - u_mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( - in_field_class, i); - mapping = bt_field_class_unsigned_enumeration_mapping_as_mapping_const( - u_mapping); - range_count = - bt_field_class_enumeration_mapping_get_range_count( - mapping); - label = bt_field_class_enumeration_mapping_get_label( - mapping); - - /* - * Iterate over all the ranges to add them to copied field - * class. - */ - for (range_index = 0; range_index < range_count; range_index++) { - uint64_t lower, upper; - bt_field_class_status status; - bt_field_class_unsigned_enumeration_mapping_get_range_by_index( - u_mapping, range_index, &lower, &upper); - - BT_LOGD("Copying range in enumeration field class: " - "label=%s, lower=%"PRId64", upper=%"PRId64, - label, lower, upper); - - /* Add the label and its range to the copy field class. */ - status = bt_field_class_unsigned_enumeration_map_range( - out_field_class, label, lower, upper); - - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add range to unsigned " - "enumeration."); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_field_class); - ret = -1; - goto error; - } - } - } - - BT_LOGD("Copied content of unsigned enumeration field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - -error: - return ret; -} - -static inline -int field_class_signed_enumeration_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - uint64_t i, enum_mapping_count; - int ret = 0; - - BT_LOGD("Copying content of signed enumeration field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - /* Copy properties of the inner integer. */ - field_class_integer_set_props(in_field_class, out_field_class); - - /* Copy all enumeration entries. */ - enum_mapping_count = - bt_field_class_enumeration_get_mapping_count(in_field_class); - for (i = 0; i < enum_mapping_count; i++) { - const char *label; - const bt_field_class_signed_enumeration_mapping *i_mapping; - const bt_field_class_enumeration_mapping *mapping; - uint64_t range_index, range_count; - - /* Get the ranges and the range count. */ - i_mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const( - in_field_class, i); - mapping = bt_field_class_signed_enumeration_mapping_as_mapping_const( - i_mapping); - range_count = - bt_field_class_enumeration_mapping_get_range_count( - mapping); - label = bt_field_class_enumeration_mapping_get_label( - mapping); - - /* - * Iterate over all the ranges to add them to copied field - * class. - */ - for (range_index = 0; range_index < range_count; range_index++) { - int64_t lower, upper; - bt_field_class_status status; - bt_field_class_signed_enumeration_mapping_get_range_by_index( - i_mapping, range_index, &lower, &upper); - - BT_LOGD("Copying range in enumeration field class: " - "label=%s, lower=%"PRId64", upper=%"PRId64, - label, lower, upper); - - /* Add the label and its range to the copy field class. */ - status = bt_field_class_signed_enumeration_map_range( - out_field_class, label, lower, upper); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Failed to add range to signed " - "enumeration."); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_field_class); - ret = -1; - goto error; - } - } - } - - BT_LOGD("Copied content of signed enumeration field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - -error: - return ret; -} - -static inline -int field_class_real_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - BT_LOGD("Copying content of real field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - bt_field_class_real_set_is_single_precision(out_field_class, - bt_field_class_real_is_single_precision(in_field_class)); - - BT_LOGD("Copied content real field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - - return 0; -} - -static inline -int field_class_structure_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - uint64_t i, struct_member_count; - bt_field_class_status status; - int ret = 0; - - BT_LOGD("Copying content of structure field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - /* Get the number of member in that struct. */ - struct_member_count = - bt_field_class_structure_get_member_count(in_field_class); - - /* Iterate over all the members of the struct. */ - for (i = 0; i < struct_member_count; i++) { - const bt_field_class_structure_member *member; - const char *member_name; - const bt_field_class *member_fc; - bt_field_class *out_member_field_class; - - member = bt_field_class_structure_borrow_member_by_index_const( - in_field_class, i); - member_fc = bt_field_class_structure_member_borrow_field_class_const( - member); - member_name = bt_field_class_structure_member_get_name(member); - BT_LOGD("Copying structure field class's field: " - "index=%" PRId64 ", " - "member-fc-addr=%p, field-name=\"%s\"", - i, member_fc, member_name); - - out_member_field_class = create_field_class_copy(md_maps, - member_fc); - if (!out_member_field_class) { - BT_LOGE("Cannot copy structure field class's field: " - "index=%" PRId64 ", " - "field-fc-addr=%p, field-name=\"%s\"", - i, member_fc, member_name); - ret = -1; - goto error; - } - ret = copy_field_class_content(md_maps, member_fc, - out_member_field_class); - if (ret) { - goto error; - } - - status = bt_field_class_structure_append_member(out_field_class, - member_name, out_member_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE("Cannot append structure field class's field: " - "index=%" PRId64 ", " - "field-fc-addr=%p, field-name=\"%s\"", - i, member_fc, member_name); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_member_field_class); - ret = -1; - goto error; - } - } - - BT_LOGD("Copied structure field class: original-fc-addr=%p, copy-fc-addr=%p", - in_field_class, out_field_class); - -error: - return ret; -} - -static inline -int field_class_variant_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - bt_field_class *out_tag_field_class = NULL; - uint64_t i, variant_option_count; - const bt_field_path *tag_fp; - const bt_field_class *tag_fc; - int ret = 0; - - BT_LOGD("Copying content of variant field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - tag_fp = bt_field_class_variant_borrow_selector_field_path_const( - in_field_class); - if (tag_fp) { - tag_fc = resolve_field_path_to_field_class(tag_fp, - md_maps); - - out_tag_field_class = g_hash_table_lookup( - md_maps->field_class_map, tag_fc); - if (!out_tag_field_class) { - BT_LOGE_STR("Cannot find the tag field class."); - ret = -1; - goto error; - } - bt_field_class_variant_set_selector_field_class(out_field_class, - out_tag_field_class); - } - - variant_option_count = - bt_field_class_variant_get_option_count(in_field_class); - for (i = 0; i < variant_option_count; i++) { - const bt_field_class *option_fc; - const char *option_name; - bt_field_class *out_option_field_class; - bt_field_class_status status; - const bt_field_class_variant_option *option; - - option = bt_field_class_variant_borrow_option_by_index_const( - in_field_class, i); - option_fc = bt_field_class_variant_option_borrow_field_class_const( - option); - option_name = bt_field_class_variant_option_get_name(option); - out_option_field_class = create_field_class_copy_internal( - md_maps, option_fc); - if (!out_option_field_class) { - BT_LOGE_STR("Cannot copy field class."); - ret = -1; - goto error; - } - ret = copy_field_class_content_internal(md_maps, option_fc, - out_option_field_class); - if (ret) { - BT_LOGE_STR("Error copying content of option variant " - "field class'"); - goto error; - } - - status = bt_field_class_variant_append_option( - out_field_class, option_name, - out_option_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Cannot append option to variant field class'"); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_tag_field_class); - ret = -1; - goto error; - } - } - - BT_LOGD("Copied content of variant field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - -error: - return ret; -} - -static inline -int field_class_static_array_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - BT_LOGD("Copying content of static array field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - /* - * There is no content to copy. Keep this function call anyway for - * logging purposes. - */ - BT_LOGD("Copied content of static array field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - - return 0; -} - -static inline -int field_class_dynamic_array_copy( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - const bt_field_class *len_fc; - const bt_field_path *len_fp; - bt_field_class_status status; - bt_field_class *out_len_field_class; - int ret = 0; - - BT_LOGD("Copying content of dynamic array field class: " - "in-fc-addr=%p, out-fc-addr=%p", - in_field_class, out_field_class); - - len_fp = bt_field_class_dynamic_array_borrow_length_field_path_const( - in_field_class); - - if (len_fp) { - BT_LOGD("Copying dynamic array length field class using " - "field path: in-len-fp=%p", len_fp); - len_fc = resolve_field_path_to_field_class( - len_fp, md_maps); - out_len_field_class = g_hash_table_lookup( - md_maps->field_class_map, len_fc); - if (!out_len_field_class) { - BT_LOGE_STR("Cannot find the output matching length" - "field class."); - ret = -1; - goto error; - } - - status = bt_field_class_dynamic_array_set_length_field_class( - out_field_class, out_len_field_class); - if (status != BT_FIELD_CLASS_STATUS_OK) { - BT_LOGE_STR("Cannot set dynamic array field class' " - "length field class."); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_len_field_class); - ret = -1; - goto error; - } - } - - BT_LOGD("Copied dynamic array field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - -error: - return ret; -} - -static inline -int field_class_string_copy(struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - BT_LOGD("Copying content of string field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - /* - * There is no content to copy. Keep this function call anyway for - * logging purposes. - */ - BT_LOGD("Copied content of string field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - - return 0; -} - -static -bt_field_class *copy_field_class_array_element(struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_elem_fc) -{ - int ret; - bt_field_class *out_elem_fc = - create_field_class_copy_internal(md_maps, in_elem_fc); - if (!out_elem_fc) { - BT_LOGE("Error creating output elem field class " - "from input elem field class for static array: " - "in-fc-addr=%p", in_elem_fc); - goto error; - } - - ret = copy_field_class_content_internal(md_maps, in_elem_fc, out_elem_fc); - if (ret) { - BT_LOGE("Error creating output elem field class " - "from input elem field class for static array: " - "in-fc-addr=%p", in_elem_fc); - BT_FIELD_CLASS_PUT_REF_AND_RESET(out_elem_fc); - goto error; - } - -error: - return out_elem_fc; -} - -BT_HIDDEN -bt_field_class *create_field_class_copy_internal(struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class) -{ - bt_field_class *out_field_class = NULL; - - BT_LOGD("Creating bare field class based on field class: in-fc-addr=%p", - in_field_class); - - switch(bt_field_class_get_type(in_field_class)) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - out_field_class = bt_field_class_unsigned_integer_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - out_field_class = bt_field_class_signed_integer_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - out_field_class = bt_field_class_unsigned_enumeration_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - out_field_class = bt_field_class_signed_enumeration_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_REAL: - out_field_class = bt_field_class_real_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_STRING: - out_field_class = bt_field_class_string_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_STRUCTURE: - out_field_class = bt_field_class_structure_create( - md_maps->output_trace_class); - break; - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - { - const bt_field_class *in_elem_fc = - bt_field_class_array_borrow_element_field_class_const( - in_field_class); - uint64_t array_len = - bt_field_class_static_array_get_length(in_field_class); - - bt_field_class *out_elem_fc = copy_field_class_array_element( - md_maps, in_elem_fc); - if (!out_elem_fc) { - out_field_class = NULL; - goto error; - } - - out_field_class = bt_field_class_static_array_create( - md_maps->output_trace_class, - out_elem_fc, array_len); - break; - } - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - { - const bt_field_class *in_elem_fc = - bt_field_class_array_borrow_element_field_class_const( - in_field_class); - - bt_field_class *out_elem_fc = copy_field_class_array_element( - md_maps, in_elem_fc); - if (!out_elem_fc) { - out_field_class = NULL; - goto error; - } - - out_field_class = bt_field_class_dynamic_array_create( - md_maps->output_trace_class, - out_elem_fc); - break; - } - case BT_FIELD_CLASS_TYPE_VARIANT: - out_field_class = bt_field_class_variant_create( - md_maps->output_trace_class); - break; - default: - abort(); - } - - /* - * Add mapping from in_field_class to out_field_class. This simplifies - * the resolution of field paths in variant and dynamic array field - * classes. - */ - g_hash_table_insert(md_maps->field_class_map, - (gpointer) in_field_class, out_field_class); - -error: - if(out_field_class){ - BT_LOGD("Created bare field class based on field class: in-fc-addr=%p, " - "out-fc-addr=%p", in_field_class, out_field_class); - } else { - BT_LOGE("Error creating output field class from input field " - "class: in-fc-addr=%p", in_field_class); - } - - return out_field_class; -} - -BT_HIDDEN -int copy_field_class_content_internal( - struct trace_ir_metadata_maps *md_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class) -{ - int ret = 0; - switch(bt_field_class_get_type(in_field_class)) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - ret = field_class_unsigned_integer_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - ret = field_class_signed_integer_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - ret = field_class_unsigned_enumeration_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - ret = field_class_signed_enumeration_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_REAL: - ret = field_class_real_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_STRING: - ret = field_class_string_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_STRUCTURE: - ret = field_class_structure_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - ret = field_class_static_array_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - ret = field_class_dynamic_array_copy(md_maps, - in_field_class, out_field_class); - break; - case BT_FIELD_CLASS_TYPE_VARIANT: - ret = field_class_variant_copy(md_maps, - in_field_class, out_field_class); - break; - default: - abort(); - } - - return ret; -} diff --git a/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h b/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h deleted file mode 100644 index 3bd5b80f..00000000 --- a/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H - -/* - * Babeltrace - Trace IR metadata field class copy - * - * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "trace-ir-mapping.h" - -BT_HIDDEN -int copy_field_class_content_internal(struct trace_ir_metadata_maps *trace_ir_metadata_maps, - const bt_field_class *in_field_class, - bt_field_class *out_field_class); - -BT_HIDDEN -bt_field_class *create_field_class_copy_internal( - struct trace_ir_metadata_maps *trace_ir_metadata_maps, - const bt_field_class *in_field_class); - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H */ diff --git a/plugins/lttng-utils/debug-info/utils.c b/plugins/lttng-utils/debug-info/utils.c deleted file mode 100644 index 8f1bddea..00000000 --- a/plugins/lttng-utils/debug-info/utils.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Babeltrace - Debug info utilities - * - * Copyright (c) 2016 Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "utils.h" - -BT_HIDDEN -const char *get_filename_from_path(const char *path) -{ - size_t i = strlen(path); - - if (i == 0) { - goto end; - } - - if (path[i - 1] == '/') { - /* - * Path ends with a trailing slash, no filename to return. - * Return the original path. - */ - goto end; - } - - while (i-- > 0) { - if (path[i] == '/') { - path = &path[i + 1]; - goto end; - } - } -end: - return path; -} - -BT_HIDDEN -bt_bool is_event_common_ctx_dbg_info_compatible(const bt_field_class *in_field_class, - const char *debug_info_field_class_name) -{ - const bt_field_class_structure_member *member; - const bt_field_class *ip_fc, *vpid_fc; - bt_bool match = BT_FALSE; - - /* - * If the debug info field is already present in the event common - * context. Do not try to add it. - */ - member = - bt_field_class_structure_borrow_member_by_name_const( - in_field_class, debug_info_field_class_name); - if (member) { - goto end; - } - - /* - * Verify that the ip and vpid field are present and of the right field - * class. - */ - member = bt_field_class_structure_borrow_member_by_name_const( - in_field_class, IP_FIELD_NAME); - if (!member) { - goto end; - } - - ip_fc = bt_field_class_structure_member_borrow_field_class_const( - member); - if (bt_field_class_get_type(ip_fc) != - BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER) { - match = BT_FALSE; - goto end; - } - - member = bt_field_class_structure_borrow_member_by_name_const( - in_field_class, VPID_FIELD_NAME); - if (!member) { - goto end; - } - - vpid_fc = bt_field_class_structure_member_borrow_field_class_const( - member); - - if (bt_field_class_get_type(vpid_fc) != - BT_FIELD_CLASS_TYPE_SIGNED_INTEGER) { - goto end; - } - - if (bt_field_class_integer_get_field_value_range(vpid_fc) != 32) { - goto end; - } - - match = BT_TRUE; - -end: - return match; -} diff --git a/plugins/lttng-utils/debug-info/utils.h b/plugins/lttng-utils/debug-info/utils.h deleted file mode 100644 index 82820fc0..00000000 --- a/plugins/lttng-utils/debug-info/utils.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H -#define BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H -/* - * Babeltrace - Debug Info Utilities - * - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "trace-ir-mapping.h" - -/* - * Return the location of a path's file (the last element of the path). - * Returns the original path on error. - */ -BT_HIDDEN -const char *get_filename_from_path(const char *path); - -BT_HIDDEN -bt_bool is_event_common_ctx_dbg_info_compatible( - const bt_field_class *in_field_class, - const char *debug_info_field_class_name); - -#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H */ diff --git a/plugins/lttng-utils/plugin.c b/plugins/lttng-utils/plugin.c deleted file mode 100644 index 8d50b0e8..00000000 --- a/plugins/lttng-utils/plugin.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * plugin.c - * - * Babeltrace Debug Info Plug-in - * - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "debug-info/debug-info.h" - -#ifndef BT_BUILT_IN_PLUGINS -BT_PLUGIN_MODULE(); -#endif - -/* Initialize plug-in entry points. */ -BT_PLUGIN_WITH_ID(lttng_utils, "lttng-utils"); -BT_PLUGIN_DESCRIPTION_WITH_ID(lttng_utils, "LTTng utilities"); -BT_PLUGIN_AUTHOR_WITH_ID(lttng_utils, "Julien Desfossez"); -BT_PLUGIN_LICENSE_WITH_ID(lttng_utils, "MIT"); - -BT_PLUGIN_FILTER_COMPONENT_CLASS_WITH_ID(lttng_utils, debug_info, "debug-info", - debug_info_msg_iter_next); -BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION_WITH_ID(lttng_utils, debug_info, - "Augment compatible events with debugging information."); -BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD_WITH_ID(lttng_utils, - debug_info, debug_info_comp_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(lttng_utils, - debug_info, debug_info_comp_finalize); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD_WITH_ID( - lttng_utils, debug_info, debug_info_msg_iter_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD_WITH_ID( - lttng_utils, debug_info, debug_info_msg_iter_seek_beginning); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD_WITH_ID( - lttng_utils, debug_info, debug_info_msg_iter_can_seek_beginning); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD_WITH_ID( - lttng_utils, debug_info, debug_info_msg_iter_finalize); diff --git a/plugins/plugins-common.h b/plugins/plugins-common.h deleted file mode 100644 index c7377bf8..00000000 --- a/plugins/plugins-common.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef PLUGINS_COMMON_H -#define PLUGINS_COMMON_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * UNUSED_VAR: tag a variable or parameter as explicitly unused so that - * the compiler does not warn. - */ -#define UNUSED_VAR __attribute__((unused)) - -#endif /* PLUGINS_COMMON_H */ diff --git a/plugins/text/Makefile.am b/plugins/text/Makefile.am deleted file mode 100644 index c9b26c26..00000000 --- a/plugins/text/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -SUBDIRS = pretty dmesg - -plugindir = "$(PLUGINSDIR)" -plugin_LTLIBRARIES = babeltrace-plugin-text.la - -babeltrace_plugin_text_la_SOURCES = plugin.c -babeltrace_plugin_text_la_LDFLAGS = \ - $(LT_NO_UNDEFINED) \ - -avoid-version -module - -babeltrace_plugin_text_la_LIBADD = \ - pretty/libbabeltrace2-plugin-text-pretty-cc.la \ - dmesg/libbabeltrace2-plugin-text-dmesg-cc.la - -if !ENABLE_BUILT_IN_PLUGINS -babeltrace_plugin_text_la_LIBADD += \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/compat/libcompat.la -endif diff --git a/plugins/text/dmesg/Makefile.am b/plugins/text/dmesg/Makefile.am deleted file mode 100644 index 6e0f12ad..00000000 --- a/plugins/text/dmesg/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-text-dmesg-cc.la - -# ctf-text plugin -libbabeltrace2_plugin_text_dmesg_cc_la_SOURCES = \ - dmesg.c \ - dmesg.h \ - logging.h \ - logging.c diff --git a/plugins/text/dmesg/dmesg.c b/plugins/text/dmesg/dmesg.c deleted file mode 100644 index b10c9425..00000000 --- a/plugins/text/dmesg/dmesg.c +++ /dev/null @@ -1,927 +0,0 @@ -/* - * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-TEXT-DMESG-SRC" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NSEC_PER_USEC 1000UL -#define NSEC_PER_MSEC 1000000UL -#define NSEC_PER_SEC 1000000000ULL -#define USEC_PER_SEC 1000000UL - -struct dmesg_component; - -struct dmesg_msg_iter { - struct dmesg_component *dmesg_comp; - bt_self_message_iterator *pc_msg_iter; /* Weak */ - char *linebuf; - size_t linebuf_len; - FILE *fp; - bt_message *tmp_event_msg; - uint64_t last_clock_value; - - enum { - STATE_EMIT_STREAM_BEGINNING, - STATE_EMIT_STREAM_ACTIVITY_BEGINNING, - STATE_EMIT_PACKET_BEGINNING, - STATE_EMIT_EVENT, - STATE_EMIT_PACKET_END, - STATE_EMIT_STREAM_ACTIVITY_END, - STATE_EMIT_STREAM_END, - STATE_DONE, - } state; -}; - -struct dmesg_component { - struct { - GString *path; - bt_bool read_from_stdin; - bt_bool no_timestamp; - } params; - - bt_self_component_source *self_comp; - bt_trace_class *trace_class; - bt_stream_class *stream_class; - bt_event_class *event_class; - bt_trace *trace; - bt_stream *stream; - bt_packet *packet; - bt_clock_class *clock_class; -}; - -static -bt_field_class *create_event_payload_fc(bt_trace_class *trace_class) -{ - bt_field_class *root_fc = NULL; - bt_field_class *fc = NULL; - int ret; - - root_fc = bt_field_class_structure_create(trace_class); - if (!root_fc) { - BT_LOGE_STR("Cannot create an empty structure field class object."); - goto error; - } - - fc = bt_field_class_string_create(trace_class); - if (!fc) { - BT_LOGE_STR("Cannot create a string field class object."); - goto error; - } - - ret = bt_field_class_structure_append_member(root_fc, - "str", fc); - if (ret) { - BT_LOGE("Cannot add `str` member to structure field class: " - "ret=%d", ret); - goto error; - } - - goto end; - -error: - BT_FIELD_CLASS_PUT_REF_AND_RESET(root_fc); - -end: - bt_field_class_put_ref(fc); - return root_fc; -} - -static -int create_meta(struct dmesg_component *dmesg_comp, bool has_ts) -{ - bt_field_class *fc = NULL; - int ret = 0; - - dmesg_comp->trace_class = bt_trace_class_create( - bt_self_component_source_as_self_component( - dmesg_comp->self_comp)); - if (!dmesg_comp->trace_class) { - BT_LOGE_STR("Cannot create an empty trace class object."); - goto error; - } - - dmesg_comp->stream_class = bt_stream_class_create( - dmesg_comp->trace_class); - if (!dmesg_comp->stream_class) { - BT_LOGE_STR("Cannot create a stream class object."); - goto error; - } - - if (has_ts) { - dmesg_comp->clock_class = bt_clock_class_create( - bt_self_component_source_as_self_component( - dmesg_comp->self_comp)); - if (!dmesg_comp->clock_class) { - BT_LOGE_STR("Cannot create clock class."); - goto error; - } - - /* - * The `dmesg` timestamp's origin is not the Unix epoch, - * it's the boot time. - */ - bt_clock_class_set_origin_is_unix_epoch(dmesg_comp->clock_class, - BT_FALSE); - - ret = bt_stream_class_set_default_clock_class( - dmesg_comp->stream_class, dmesg_comp->clock_class); - if (ret) { - BT_LOGE_STR("Cannot set stream class's default clock class."); - goto error; - } - - bt_stream_class_set_packets_have_beginning_default_clock_snapshot( - dmesg_comp->stream_class, BT_TRUE); - bt_stream_class_set_packets_have_end_default_clock_snapshot( - dmesg_comp->stream_class, BT_TRUE); - } - - dmesg_comp->event_class = bt_event_class_create( - dmesg_comp->stream_class); - if (!dmesg_comp->event_class) { - BT_LOGE_STR("Cannot create an event class object."); - goto error; - } - - ret = bt_event_class_set_name(dmesg_comp->event_class, "string"); - if (ret) { - BT_LOGE_STR("Cannot set event class's name."); - goto error; - } - - fc = create_event_payload_fc(dmesg_comp->trace_class); - if (!fc) { - BT_LOGE_STR("Cannot create event payload field class."); - goto error; - } - - ret = bt_event_class_set_payload_field_class(dmesg_comp->event_class, fc); - if (ret) { - BT_LOGE_STR("Cannot set event class's event payload field class."); - goto error; - } - - goto end; - -error: - ret = -1; - -end: - bt_field_class_put_ref(fc); - return ret; -} - -static -int handle_params(struct dmesg_component *dmesg_comp, - const bt_value *params) -{ - const bt_value *no_timestamp = NULL; - const bt_value *path = NULL; - const char *path_str; - int ret = 0; - - no_timestamp = bt_value_map_borrow_entry_value_const(params, - "no-extract-timestamp"); - if (no_timestamp) { - if (!bt_value_is_bool(no_timestamp)) { - BT_LOGE("Expecting a boolean value for the `no-extract-timestamp` parameter: " - "type=%s", - bt_common_value_type_string( - bt_value_get_type(no_timestamp))); - goto error; - } - - dmesg_comp->params.no_timestamp = - bt_value_bool_get(no_timestamp); - } - - path = bt_value_map_borrow_entry_value_const(params, "path"); - if (path) { - if (dmesg_comp->params.read_from_stdin) { - BT_LOGE_STR("Cannot specify both `read-from-stdin` and `path` parameters."); - goto error; - } - - if (!bt_value_is_string(path)) { - BT_LOGE("Expecting a string value for the `path` parameter: " - "type=%s", - bt_common_value_type_string( - bt_value_get_type(path))); - goto error; - } - - path_str = bt_value_string_get(path); - g_string_assign(dmesg_comp->params.path, path_str); - } else { - dmesg_comp->params.read_from_stdin = true; - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int create_packet_and_stream_and_trace(struct dmesg_component *dmesg_comp) -{ - int ret = 0; - const char *trace_name = NULL; - gchar *basename = NULL; - - dmesg_comp->trace = bt_trace_create(dmesg_comp->trace_class); - if (!dmesg_comp->trace) { - BT_LOGE_STR("Cannot create trace object."); - goto error; - } - - if (dmesg_comp->params.read_from_stdin) { - trace_name = "STDIN"; - } else { - basename = g_path_get_basename(dmesg_comp->params.path->str); - BT_ASSERT(basename); - - if (strcmp(basename, G_DIR_SEPARATOR_S) != 0 && - strcmp(basename, ".") != 0) { - trace_name = basename; - } - } - - if (trace_name) { - ret = bt_trace_set_name(dmesg_comp->trace, trace_name); - if (ret) { - BT_LOGE("Cannot set trace's name: name=\"%s\"", - trace_name); - goto error; - } - } - - dmesg_comp->stream = bt_stream_create(dmesg_comp->stream_class, - dmesg_comp->trace); - if (!dmesg_comp->stream) { - BT_LOGE_STR("Cannot create stream object."); - goto error; - } - - dmesg_comp->packet = bt_packet_create(dmesg_comp->stream); - if (!dmesg_comp->packet) { - BT_LOGE_STR("Cannot create packet object."); - goto error; - } - - goto end; - -error: - ret = -1; - -end: - if (basename) { - g_free(basename); - } - - return ret; -} - -static -int try_create_meta_stream_packet(struct dmesg_component *dmesg_comp, - bool has_ts) -{ - int ret = 0; - - if (dmesg_comp->trace) { - /* Already created */ - goto end; - } - - ret = create_meta(dmesg_comp, has_ts); - if (ret) { - BT_LOGE("Cannot create metadata objects: dmesg-comp-addr=%p", - dmesg_comp); - goto error; - } - - ret = create_packet_and_stream_and_trace(dmesg_comp); - if (ret) { - BT_LOGE("Cannot create packet and stream objects: " - "dmesg-comp-addr=%p", dmesg_comp); - goto error; - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -void destroy_dmesg_component(struct dmesg_component *dmesg_comp) -{ - if (!dmesg_comp) { - return; - } - - if (dmesg_comp->params.path) { - g_string_free(dmesg_comp->params.path, TRUE); - } - - bt_packet_put_ref(dmesg_comp->packet); - bt_trace_put_ref(dmesg_comp->trace); - bt_stream_class_put_ref(dmesg_comp->stream_class); - bt_event_class_put_ref(dmesg_comp->event_class); - bt_stream_put_ref(dmesg_comp->stream); - bt_clock_class_put_ref(dmesg_comp->clock_class); - bt_trace_class_put_ref(dmesg_comp->trace_class); - g_free(dmesg_comp); -} - -static -bt_self_component_status create_port( - bt_self_component_source *self_comp) -{ - return bt_self_component_source_add_output_port(self_comp, - "out", NULL, NULL); -} - -BT_HIDDEN -bt_self_component_status dmesg_init( - bt_self_component_source *self_comp, - bt_value *params, void *init_method_data) -{ - int ret = 0; - struct dmesg_component *dmesg_comp = g_new0(struct dmesg_component, 1); - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - - if (!dmesg_comp) { - BT_LOGE_STR("Failed to allocate one dmesg component structure."); - goto error; - } - - dmesg_comp->self_comp = self_comp; - dmesg_comp->params.path = g_string_new(NULL); - if (!dmesg_comp->params.path) { - BT_LOGE_STR("Failed to allocate a GString."); - goto error; - } - - ret = handle_params(dmesg_comp, params); - if (ret) { - BT_LOGE("Invalid parameters: comp-addr=%p", self_comp); - goto error; - } - - if (!dmesg_comp->params.read_from_stdin && - !g_file_test(dmesg_comp->params.path->str, - G_FILE_TEST_IS_REGULAR)) { - BT_LOGE("Input path is not a regular file: " - "comp-addr=%p, path=\"%s\"", self_comp, - dmesg_comp->params.path->str); - goto error; - } - - status = create_port(self_comp); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - bt_self_component_set_data( - bt_self_component_source_as_self_component(self_comp), - dmesg_comp); - goto end; - -error: - destroy_dmesg_component(dmesg_comp); - bt_self_component_set_data( - bt_self_component_source_as_self_component(self_comp), - NULL); - - if (status >= 0) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - } - -end: - return status; -} - -BT_HIDDEN -void dmesg_finalize(bt_self_component_source *self_comp) -{ - destroy_dmesg_component(bt_self_component_get_data( - bt_self_component_source_as_self_component(self_comp))); -} - -static -bt_message *create_init_event_msg_from_line( - struct dmesg_msg_iter *msg_iter, - const char *line, const char **new_start) -{ - bt_event *event; - bt_message *msg = NULL; - bool has_timestamp = false; - unsigned long sec, usec, msec; - unsigned int year, mon, mday, hour, min; - uint64_t ts = 0; - int ret = 0; - struct dmesg_component *dmesg_comp = msg_iter->dmesg_comp; - - *new_start = line; - - if (dmesg_comp->params.no_timestamp) { - goto skip_ts; - } - - /* Extract time from input line */ - if (sscanf(line, "[%lu.%lu] ", &sec, &usec) == 2) { - ts = (uint64_t) sec * USEC_PER_SEC + (uint64_t) usec; - - /* - * The clock class we use has a 1 GHz frequency: convert - * from µs to ns. - */ - ts *= NSEC_PER_USEC; - has_timestamp = true; - } else if (sscanf(line, "[%u-%u-%u %u:%u:%lu.%lu] ", - &year, &mon, &mday, &hour, &min, - &sec, &msec) == 7) { - time_t ep_sec; - struct tm ti; - - memset(&ti, 0, sizeof(ti)); - ti.tm_year = year - 1900; /* From 1900 */ - ti.tm_mon = mon - 1; /* 0 to 11 */ - ti.tm_mday = mday; - ti.tm_hour = hour; - ti.tm_min = min; - ti.tm_sec = sec; - - ep_sec = bt_timegm(&ti); - if (ep_sec != (time_t) -1) { - ts = (uint64_t) ep_sec * NSEC_PER_SEC - + (uint64_t) msec * NSEC_PER_MSEC; - } - - has_timestamp = true; - } - - if (has_timestamp) { - /* Set new start for the message portion of the line */ - *new_start = strchr(line, ']'); - BT_ASSERT(*new_start); - (*new_start)++; - - if ((*new_start)[0] == ' ') { - (*new_start)++; - } - } - -skip_ts: - /* - * At this point, we know if the stream class's event header - * field class should have a timestamp or not, so we can lazily - * create the metadata, stream, and packet objects. - */ - ret = try_create_meta_stream_packet(dmesg_comp, has_timestamp); - if (ret) { - /* try_create_meta_stream_packet() logs errors */ - goto error; - } - - if (dmesg_comp->clock_class) { - msg = bt_message_event_create_with_default_clock_snapshot( - msg_iter->pc_msg_iter, - dmesg_comp->event_class, dmesg_comp->packet, ts); - msg_iter->last_clock_value = ts; - } else { - msg = bt_message_event_create(msg_iter->pc_msg_iter, - dmesg_comp->event_class, dmesg_comp->packet); - } - - if (!msg) { - BT_LOGE_STR("Cannot create event message."); - goto error; - } - - event = bt_message_event_borrow_event(msg); - BT_ASSERT(event); - goto end; - -error: - BT_MESSAGE_PUT_REF_AND_RESET(msg); - -end: - return msg; -} - -static -int fill_event_payload_from_line(const char *line, - bt_event *event) -{ - bt_field *ep_field = NULL; - bt_field *str_field = NULL; - size_t len; - int ret; - - ep_field = bt_event_borrow_payload_field(event); - BT_ASSERT(ep_field); - str_field = bt_field_structure_borrow_member_field_by_index( - ep_field, 0); - if (!str_field) { - BT_LOGE_STR("Cannot borrow `timestamp` field from event payload structure field."); - goto error; - } - - len = strlen(line); - if (line[len - 1] == '\n') { - /* Do not include the newline character in the payload */ - len--; - } - - ret = bt_field_string_clear(str_field); - if (ret) { - BT_LOGE_STR("Cannot clear string field object."); - goto error; - } - - ret = bt_field_string_append_with_length(str_field, line, len); - if (ret) { - BT_LOGE("Cannot append value to string field object: " - "len=%zu", len); - goto error; - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -bt_message *create_msg_from_line( - struct dmesg_msg_iter *dmesg_msg_iter, const char *line) -{ - bt_event *event = NULL; - bt_message *msg = NULL; - const char *new_start; - int ret; - - msg = create_init_event_msg_from_line(dmesg_msg_iter, - line, &new_start); - if (!msg) { - BT_LOGE_STR("Cannot create and initialize event message from line."); - goto error; - } - - event = bt_message_event_borrow_event(msg); - BT_ASSERT(event); - ret = fill_event_payload_from_line(new_start, event); - if (ret) { - BT_LOGE("Cannot fill event payload field from line: " - "ret=%d", ret); - goto error; - } - - goto end; - -error: - BT_MESSAGE_PUT_REF_AND_RESET(msg); - -end: - return msg; -} - -static -void destroy_dmesg_msg_iter(struct dmesg_msg_iter *dmesg_msg_iter) -{ - if (!dmesg_msg_iter) { - return; - } - - if (dmesg_msg_iter->fp && dmesg_msg_iter->fp != stdin) { - if (fclose(dmesg_msg_iter->fp)) { - BT_LOGE_ERRNO("Cannot close input file", "."); - } - } - - bt_message_put_ref(dmesg_msg_iter->tmp_event_msg); - free(dmesg_msg_iter->linebuf); - g_free(dmesg_msg_iter); -} - - - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_source *self_comp, - bt_self_component_port_output *self_port) -{ - struct dmesg_component *dmesg_comp; - struct dmesg_msg_iter *dmesg_msg_iter = - g_new0(struct dmesg_msg_iter, 1); - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - if (!dmesg_msg_iter) { - BT_LOGE_STR("Failed to allocate on dmesg message iterator structure."); - goto error; - } - - dmesg_comp = bt_self_component_get_data( - bt_self_component_source_as_self_component(self_comp)); - BT_ASSERT(dmesg_comp); - dmesg_msg_iter->dmesg_comp = dmesg_comp; - dmesg_msg_iter->pc_msg_iter = self_msg_iter; - - if (dmesg_comp->params.read_from_stdin) { - dmesg_msg_iter->fp = stdin; - } else { - dmesg_msg_iter->fp = fopen(dmesg_comp->params.path->str, "r"); - if (!dmesg_msg_iter->fp) { - BT_LOGE_ERRNO("Cannot open input file in read mode", ": path=\"%s\"", - dmesg_comp->params.path->str); - goto error; - } - } - - bt_self_message_iterator_set_data(self_msg_iter, - dmesg_msg_iter); - goto end; - -error: - destroy_dmesg_msg_iter(dmesg_msg_iter); - bt_self_message_iterator_set_data(self_msg_iter, NULL); - if (status >= 0) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } - -end: - return status; -} - -BT_HIDDEN -void dmesg_msg_iter_finalize( - bt_self_message_iterator *priv_msg_iter) -{ - destroy_dmesg_msg_iter(bt_self_message_iterator_get_data( - priv_msg_iter)); -} - -static -bt_self_message_iterator_status dmesg_msg_iter_next_one( - struct dmesg_msg_iter *dmesg_msg_iter, - bt_message **msg) -{ - ssize_t len; - struct dmesg_component *dmesg_comp; - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(dmesg_msg_iter); - dmesg_comp = dmesg_msg_iter->dmesg_comp; - BT_ASSERT(dmesg_comp); - - if (dmesg_msg_iter->state == STATE_DONE) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - goto end; - } - - if (dmesg_msg_iter->tmp_event_msg || - dmesg_msg_iter->state == STATE_EMIT_PACKET_END || - dmesg_msg_iter->state == STATE_EMIT_STREAM_ACTIVITY_END || - dmesg_msg_iter->state == STATE_EMIT_STREAM_END) { - goto handle_state; - } - - while (true) { - const char *ch; - bool only_spaces = true; - - len = bt_getline(&dmesg_msg_iter->linebuf, - &dmesg_msg_iter->linebuf_len, dmesg_msg_iter->fp); - if (len < 0) { - if (errno == EINVAL) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } else if (errno == ENOMEM) { - status = - BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - } else { - if (dmesg_msg_iter->state == STATE_EMIT_STREAM_BEGINNING) { - /* Stream did not even begin */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - goto end; - } else { - /* End current packet now */ - dmesg_msg_iter->state = - STATE_EMIT_PACKET_END; - goto handle_state; - } - } - - goto end; - } - - BT_ASSERT(dmesg_msg_iter->linebuf); - - /* Ignore empty lines, once trimmed */ - for (ch = dmesg_msg_iter->linebuf; *ch != '\0'; ch++) { - if (!isspace(*ch)) { - only_spaces = false; - break; - } - } - - if (!only_spaces) { - break; - } - } - - dmesg_msg_iter->tmp_event_msg = create_msg_from_line( - dmesg_msg_iter, dmesg_msg_iter->linebuf); - if (!dmesg_msg_iter->tmp_event_msg) { - BT_LOGE("Cannot create event message from line: " - "dmesg-comp-addr=%p, line=\"%s\"", dmesg_comp, - dmesg_msg_iter->linebuf); - goto end; - } - -handle_state: - BT_ASSERT(dmesg_comp->trace); - - switch (dmesg_msg_iter->state) { - case STATE_EMIT_STREAM_BEGINNING: - BT_ASSERT(dmesg_msg_iter->tmp_event_msg); - *msg = bt_message_stream_beginning_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); - dmesg_msg_iter->state = STATE_EMIT_STREAM_ACTIVITY_BEGINNING; - break; - case STATE_EMIT_STREAM_ACTIVITY_BEGINNING: - BT_ASSERT(dmesg_msg_iter->tmp_event_msg); - *msg = bt_message_stream_activity_beginning_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); - dmesg_msg_iter->state = STATE_EMIT_PACKET_BEGINNING; - break; - case STATE_EMIT_PACKET_BEGINNING: - BT_ASSERT(dmesg_msg_iter->tmp_event_msg); - - if (dmesg_comp->clock_class) { - *msg = bt_message_packet_beginning_create_with_default_clock_snapshot( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet, - dmesg_msg_iter->last_clock_value); - } else { - *msg = bt_message_packet_beginning_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet); - } - - dmesg_msg_iter->state = STATE_EMIT_EVENT; - break; - case STATE_EMIT_EVENT: - BT_ASSERT(dmesg_msg_iter->tmp_event_msg); - *msg = dmesg_msg_iter->tmp_event_msg; - dmesg_msg_iter->tmp_event_msg = NULL; - break; - case STATE_EMIT_PACKET_END: - if (dmesg_comp->clock_class) { - *msg = bt_message_packet_end_create_with_default_clock_snapshot( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet, - dmesg_msg_iter->last_clock_value); - } else { - *msg = bt_message_packet_end_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet); - } - - dmesg_msg_iter->state = STATE_EMIT_STREAM_ACTIVITY_END; - break; - case STATE_EMIT_STREAM_ACTIVITY_END: - *msg = bt_message_stream_activity_end_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); - dmesg_msg_iter->state = STATE_EMIT_STREAM_END; - break; - case STATE_EMIT_STREAM_END: - *msg = bt_message_stream_end_create( - dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); - dmesg_msg_iter->state = STATE_DONE; - break; - default: - break; - } - - if (!*msg) { - BT_LOGE("Cannot create message: dmesg-comp-addr=%p", - dmesg_comp); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - } - -end: - return status; -} - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - struct dmesg_msg_iter *dmesg_msg_iter = - bt_self_message_iterator_get_data( - self_msg_iter); - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - uint64_t i = 0; - - while (i < capacity && - status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - bt_message *priv_msg = NULL; - - status = dmesg_msg_iter_next_one(dmesg_msg_iter, - &priv_msg); - msgs[i] = priv_msg; - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - i++; - } - } - - if (i > 0) { - /* - * Even if dmesg_msg_iter_next_one() returned - * something else than - * BT_SELF_MESSAGE_ITERATOR_STATUS_OK, we - * accumulated message objects in the output - * message array, so we need to return - * BT_SELF_MESSAGE_ITERATOR_STATUS_OK so that they - * are transfered to downstream. This other status - * occurs again the next time muxer_msg_iter_do_next() - * is called, possibly without any accumulated - * message, in which case we'll return it. - */ - *count = i; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - } - - return status; -} - -BT_HIDDEN -bt_bool dmesg_msg_iter_can_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct dmesg_msg_iter *dmesg_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - - /* Can't seek the beginning of the standard input stream */ - return !dmesg_msg_iter->dmesg_comp->params.read_from_stdin; -} - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct dmesg_msg_iter *dmesg_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - - BT_ASSERT(!dmesg_msg_iter->dmesg_comp->params.read_from_stdin); - - BT_MESSAGE_PUT_REF_AND_RESET(dmesg_msg_iter->tmp_event_msg); - dmesg_msg_iter->last_clock_value = 0; - dmesg_msg_iter->state = STATE_EMIT_STREAM_BEGINNING; - return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; -} diff --git a/plugins/text/dmesg/dmesg.h b/plugins/text/dmesg/dmesg.h deleted file mode 100644 index 6e75ea44..00000000 --- a/plugins/text/dmesg/dmesg.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H -#define BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -BT_HIDDEN -bt_self_component_status dmesg_init( - bt_self_component_source *self_comp, - const bt_value *params, void *init_method_data); - -BT_HIDDEN -void dmesg_finalize(bt_self_component_source *self_comp); - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_source *self_comp, - bt_self_component_port_output *self_port); - -BT_HIDDEN -void dmesg_msg_iter_finalize( - bt_self_message_iterator *self_msg_iter); - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -BT_HIDDEN -bt_bool dmesg_msg_iter_can_seek_beginning( - bt_self_message_iterator *message_iterator); - -BT_HIDDEN -bt_self_message_iterator_status dmesg_msg_iter_seek_beginning( - bt_self_message_iterator *message_iterator); - -#endif /* BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H */ diff --git a/plugins/text/dmesg/logging.c b/plugins/text/dmesg/logging.c deleted file mode 100644 index 3df7f49e..00000000 --- a/plugins/text/dmesg/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_dmesg_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_text_dmesg_log_level, - "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL"); diff --git a/plugins/text/dmesg/logging.h b/plugins/text/dmesg/logging.h deleted file mode 100644 index 564479dd..00000000 --- a/plugins/text/dmesg/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_TEXT_DMESG_LOG_LEVEL -#define PLUGINS_TEXT_DMESG_LOG_LEVEL - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_dmesg_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_text_dmesg_log_level); - -#endif /* PLUGINS_TEXT_DMESG_LOG_LEVEL */ diff --git a/plugins/text/plugin.c b/plugins/text/plugin.c deleted file mode 100644 index 17f814b9..00000000 --- a/plugins/text/plugin.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "pretty/pretty.h" -#include "dmesg/dmesg.h" - -#ifndef BT_BUILT_IN_PLUGINS -BT_PLUGIN_MODULE(); -#endif - -BT_PLUGIN(text); -BT_PLUGIN_DESCRIPTION("Plain text component classes"); -BT_PLUGIN_AUTHOR("Julien Desfossez, Mathieu Desnoyers, Philippe Proulx"); -BT_PLUGIN_LICENSE("MIT"); - -/* pretty sink */ -BT_PLUGIN_SINK_COMPONENT_CLASS(pretty, pretty_consume); -BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(pretty, pretty_init); -BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(pretty, pretty_finalize); -BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(pretty, - pretty_graph_is_configured); -BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(pretty, - "Pretty-print messages (`text` format of Babeltrace 1)."); - -/* dmesg source */ -BT_PLUGIN_SOURCE_COMPONENT_CLASS(dmesg, dmesg_msg_iter_next); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(dmesg, - "Read a dmesg output from a file or from standard input."); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD(dmesg, dmesg_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(dmesg, dmesg_finalize); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(dmesg, - dmesg_msg_iter_init); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(dmesg, - dmesg_msg_iter_finalize); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(dmesg, - dmesg_msg_iter_seek_beginning); -BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD(dmesg, - dmesg_msg_iter_can_seek_beginning); diff --git a/plugins/text/pretty/Makefile.am b/plugins/text/pretty/Makefile.am deleted file mode 100644 index 72c0ab10..00000000 --- a/plugins/text/pretty/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-text-pretty-cc.la - -# ctf-text plugin -libbabeltrace2_plugin_text_pretty_cc_la_SOURCES = \ - logging.c \ - logging.h \ - pretty.c \ - pretty.h \ - print.c diff --git a/plugins/text/pretty/logging.c b/plugins/text/pretty/logging.c deleted file mode 100644 index 37beb6d3..00000000 --- a/plugins/text/pretty/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_pretty_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_text_pretty_log_level, - "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL"); diff --git a/plugins/text/pretty/logging.h b/plugins/text/pretty/logging.h deleted file mode 100644 index 5ac1f7b4..00000000 --- a/plugins/text/pretty/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_TEXT_PRETTY_LOG_LEVEL -#define PLUGINS_TEXT_PRETTY_LOG_LEVEL - -/* - * Copyright (c) 2019 Francis Deslauriers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_pretty_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_text_pretty_log_level); - -#endif /* PLUGINS_TEXT_PRETTY_LOG_LEVEL */ diff --git a/plugins/text/pretty/pretty.c b/plugins/text/pretty/pretty.c deleted file mode 100644 index 7c548391..00000000 --- a/plugins/text/pretty/pretty.c +++ /dev/null @@ -1,689 +0,0 @@ -/* - * Copyright 2016 Jérémie Galarneau - * Copyright 2016 Mathieu Desnoyers - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-TEXT-PRETTY-SINK" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pretty.h" - -GQuark stream_packet_context_quarks[STREAM_PACKET_CONTEXT_QUARKS_LEN]; - -static -const char *plugin_options[] = { - "color", - "path", - "no-delta", - "clock-cycles", - "clock-seconds", - "clock-date", - "clock-gmt", - "verbose", - "name-default", /* show/hide */ - "name-payload", - "name-context", - "name-scope", - "name-header", - "field-default", /* show/hide */ - "field-trace", - "field-trace:hostname", - "field-trace:domain", - "field-trace:procname", - "field-trace:vpid", - "field-loglevel", - "field-emf", - "field-callsite", -}; - -static -const char * const in_port_name = "in"; - -static -void destroy_pretty_data(struct pretty_component *pretty) -{ - bt_self_component_port_input_message_iterator_put_ref(pretty->iterator); - - if (pretty->string) { - (void) g_string_free(pretty->string, TRUE); - } - - if (pretty->tmp_string) { - (void) g_string_free(pretty->tmp_string, TRUE); - } - - if (pretty->out != stdout) { - int ret; - - ret = fclose(pretty->out); - if (ret) { - perror("close output file"); - } - } - g_free(pretty->options.output_path); - g_free(pretty); -} - -static -struct pretty_component *create_pretty(void) -{ - struct pretty_component *pretty; - - pretty = g_new0(struct pretty_component, 1); - if (!pretty) { - goto end; - } - pretty->string = g_string_new(""); - if (!pretty->string) { - goto error; - } - pretty->tmp_string = g_string_new(""); - if (!pretty->tmp_string) { - goto error; - } -end: - return pretty; - -error: - g_free(pretty); - return NULL; -} - -BT_HIDDEN -void pretty_finalize(bt_self_component_sink *comp) -{ - destroy_pretty_data( - bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp))); -} - -static -bt_self_component_status handle_message( - struct pretty_component *pretty, - const bt_message *message) -{ - bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; - - BT_ASSERT(pretty); - - switch (bt_message_get_type(message)) { - case BT_MESSAGE_TYPE_EVENT: - if (pretty_print_event(pretty, message)) { - ret = BT_SELF_COMPONENT_STATUS_ERROR; - } - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - BT_LOGD_STR("Message iterator inactivity message."); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - if (pretty_print_discarded_items(pretty, message)) { - ret = BT_SELF_COMPONENT_STATUS_ERROR; - } - break; - default: - break; - } - - return ret; -} - -BT_HIDDEN -bt_self_component_status pretty_graph_is_configured( - bt_self_component_sink *comp) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct pretty_component *pretty; - - pretty = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(pretty); - BT_ASSERT(!pretty->iterator); - pretty->iterator = bt_self_component_port_input_message_iterator_create( - bt_self_component_sink_borrow_input_port_by_name(comp, - in_port_name)); - if (!pretty->iterator) { - status = BT_SELF_COMPONENT_STATUS_NOMEM; - } - - return status; -} - -BT_HIDDEN -bt_self_component_status pretty_consume( - bt_self_component_sink *comp) -{ - bt_self_component_status ret; - bt_message_array_const msgs; - bt_self_component_port_input_message_iterator *it; - struct pretty_component *pretty = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - bt_message_iterator_status it_ret; - uint64_t count = 0; - uint64_t i = 0; - - it = pretty->iterator; - it_ret = bt_self_component_port_input_message_iterator_next(it, - &msgs, &count); - - switch (it_ret) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - break; - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - ret = BT_SELF_COMPONENT_STATUS_AGAIN; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_END: - ret = BT_SELF_COMPONENT_STATUS_END; - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_PUT_REF_AND_RESET( - pretty->iterator); - goto end; - default: - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - BT_ASSERT(it_ret == BT_MESSAGE_ITERATOR_STATUS_OK); - - for (i = 0; i < count; i++) { - ret = handle_message(pretty, msgs[i]); - if (ret) { - goto end; - } - - bt_message_put_ref(msgs[i]); - } - -end: - for (; i < count; i++) { - bt_message_put_ref(msgs[i]); - } - - return ret; -} - -static -int add_params_to_map(bt_value *plugin_opt_map) -{ - int ret = 0; - unsigned int i; - - for (i = 0; i < BT_ARRAY_SIZE(plugin_options); i++) { - const char *key = plugin_options[i]; - bt_value_status status; - - status = bt_value_map_insert_entry(plugin_opt_map, key, - bt_value_null); - switch (status) { - case BT_VALUE_STATUS_OK: - break; - default: - ret = -1; - goto end; - } - } -end: - return ret; -} - -static -bt_bool check_param_exists(const char *key, const bt_value *object, - void *data) -{ - struct pretty_component *pretty = data; - - if (!bt_value_map_has_entry(pretty->plugin_opt_map, - key)) { - fprintf(pretty->err, - "[warning] Parameter \"%s\" unknown to \"text.pretty\" sink component\n", key); - } - return BT_TRUE; -} - -static -void apply_one_string(const char *key, const bt_value *params, char **option) -{ - const bt_value *value = NULL; - const char *str; - - value = bt_value_map_borrow_entry_value_const(params, key); - if (!value) { - goto end; - } - if (bt_value_is_null(value)) { - goto end; - } - str = bt_value_string_get(value); - *option = g_strdup(str); - -end: - return; -} - -static -void apply_one_bool(const char *key, const bt_value *params, bool *option, - bool *found) -{ - const bt_value *value = NULL; - bt_bool bool_val; - - value = bt_value_map_borrow_entry_value_const(params, key); - if (!value) { - goto end; - } - bool_val = bt_value_bool_get(value); - *option = (bool) bool_val; - if (found) { - *found = true; - } - -end: - return; -} - -static -void warn_wrong_color_param(struct pretty_component *pretty) -{ - fprintf(pretty->err, - "[warning] Accepted values for the \"color\" parameter are:\n \"always\", \"auto\", \"never\"\n"); -} - -static -int open_output_file(struct pretty_component *pretty) -{ - int ret = 0; - - if (!pretty->options.output_path) { - goto end; - } - - pretty->out = fopen(pretty->options.output_path, "w"); - if (!pretty->out) { - goto error; - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int apply_params(struct pretty_component *pretty, const bt_value *params) -{ - int ret = 0; - bt_value_status status; - bool value, found; - char *str = NULL; - - pretty->plugin_opt_map = bt_value_map_create(); - if (!pretty->plugin_opt_map) { - ret = -1; - goto end; - } - ret = add_params_to_map(pretty->plugin_opt_map); - if (ret) { - goto end; - } - /* Report unknown parameters. */ - status = bt_value_map_foreach_entry_const(params, - check_param_exists, pretty); - switch (status) { - case BT_VALUE_STATUS_OK: - break; - default: - ret = -1; - goto end; - } - /* Known parameters. */ - pretty->options.color = PRETTY_COLOR_OPT_AUTO; - if (bt_value_map_has_entry(params, "color")) { - const bt_value *color_value; - const char *color; - - color_value = bt_value_map_borrow_entry_value_const(params, - "color"); - if (!color_value) { - goto end; - } - - color = bt_value_string_get(color_value); - - if (strcmp(color, "never") == 0) { - pretty->options.color = PRETTY_COLOR_OPT_NEVER; - } else if (strcmp(color, "auto") == 0) { - pretty->options.color = PRETTY_COLOR_OPT_AUTO; - } else if (strcmp(color, "always") == 0) { - pretty->options.color = PRETTY_COLOR_OPT_ALWAYS; - } else { - warn_wrong_color_param(pretty); - } - } - - apply_one_string("path", params, &pretty->options.output_path); - ret = open_output_file(pretty); - if (ret) { - goto end; - } - - value = false; /* Default. */ - apply_one_bool("no-delta", params, &value, NULL); - pretty->options.print_delta_field = !value; /* Reverse logic. */ - - value = false; /* Default. */ - apply_one_bool("clock-cycles", params, &value, NULL); - pretty->options.print_timestamp_cycles = value; - - value = false; /* Default. */ - apply_one_bool("clock-seconds", params, &value, NULL); - pretty->options.clock_seconds = value; - - value = false; /* Default. */ - apply_one_bool("clock-date", params, &value, NULL); - pretty->options.clock_date = value; - - value = false; /* Default. */ - apply_one_bool("clock-gmt", params, &value, NULL); - pretty->options.clock_gmt = value; - - value = false; /* Default. */ - apply_one_bool("verbose", params, &value, NULL); - pretty->options.verbose = value; - - /* Names. */ - apply_one_string("name-default", params, &str); - if (!str) { - pretty->options.name_default = PRETTY_DEFAULT_UNSET; - } else if (!strcmp(str, "show")) { - pretty->options.name_default = PRETTY_DEFAULT_SHOW; - } else if (!strcmp(str, "hide")) { - pretty->options.name_default = PRETTY_DEFAULT_HIDE; - } else { - ret = -1; - goto end; - } - g_free(str); - str = NULL; - - switch (pretty->options.name_default) { - case PRETTY_DEFAULT_UNSET: - pretty->options.print_payload_field_names = true; - pretty->options.print_context_field_names = true; - pretty->options.print_header_field_names = false; - pretty->options.print_scope_field_names = false; - break; - case PRETTY_DEFAULT_SHOW: - pretty->options.print_payload_field_names = true; - pretty->options.print_context_field_names = true; - pretty->options.print_header_field_names = true; - pretty->options.print_scope_field_names = true; - break; - case PRETTY_DEFAULT_HIDE: - pretty->options.print_payload_field_names = false; - pretty->options.print_context_field_names = false; - pretty->options.print_header_field_names = false; - pretty->options.print_scope_field_names = false; - break; - default: - ret = -1; - goto end; - } - - value = false; - found = false; - apply_one_bool("name-payload", params, &value, &found); - if (found) { - pretty->options.print_payload_field_names = value; - } - - value = false; - found = false; - apply_one_bool("name-context", params, &value, &found); - if (found) { - pretty->options.print_context_field_names = value; - } - - value = false; - found = false; - apply_one_bool("name-header", params, &value, &found); - if (found) { - pretty->options.print_header_field_names = value; - } - - value = false; - found = false; - apply_one_bool("name-scope", params, &value, &found); - if (found) { - pretty->options.print_scope_field_names = value; - } - - /* Fields. */ - apply_one_string("field-default", params, &str); - if (!str) { - pretty->options.field_default = PRETTY_DEFAULT_UNSET; - } else if (!strcmp(str, "show")) { - pretty->options.field_default = PRETTY_DEFAULT_SHOW; - } else if (!strcmp(str, "hide")) { - pretty->options.field_default = PRETTY_DEFAULT_HIDE; - } else { - ret = -1; - goto end; - } - g_free(str); - str = NULL; - - switch (pretty->options.field_default) { - case PRETTY_DEFAULT_UNSET: - pretty->options.print_trace_field = false; - pretty->options.print_trace_hostname_field = true; - pretty->options.print_trace_domain_field = false; - pretty->options.print_trace_procname_field = true; - pretty->options.print_trace_vpid_field = true; - pretty->options.print_loglevel_field = false; - pretty->options.print_emf_field = false; - pretty->options.print_callsite_field = false; - break; - case PRETTY_DEFAULT_SHOW: - pretty->options.print_trace_field = true; - pretty->options.print_trace_hostname_field = true; - pretty->options.print_trace_domain_field = true; - pretty->options.print_trace_procname_field = true; - pretty->options.print_trace_vpid_field = true; - pretty->options.print_loglevel_field = true; - pretty->options.print_emf_field = true; - pretty->options.print_callsite_field = true; - break; - case PRETTY_DEFAULT_HIDE: - pretty->options.print_trace_field = false; - pretty->options.print_trace_hostname_field = false; - pretty->options.print_trace_domain_field = false; - pretty->options.print_trace_procname_field = false; - pretty->options.print_trace_vpid_field = false; - pretty->options.print_loglevel_field = false; - pretty->options.print_emf_field = false; - pretty->options.print_callsite_field = false; - break; - default: - ret = -1; - goto end; - } - - value = false; - found = false; - apply_one_bool("field-trace", params, &value, &found); - if (found) { - pretty->options.print_trace_field = value; - } - - value = false; - found = false; - apply_one_bool("field-trace:hostname", params, &value, &found); - if (found) { - pretty->options.print_trace_hostname_field = value; - } - - value = false; - found = false; - apply_one_bool("field-trace:domain", params, &value, &found); - if (found) { - pretty->options.print_trace_domain_field = value; - } - - value = false; - found = false; - apply_one_bool("field-trace:procname", params, &value, &found); - if (found) { - pretty->options.print_trace_procname_field = value; - } - - value = false; - found = false; - apply_one_bool("field-trace:vpid", params, &value, &found); - if (found) { - pretty->options.print_trace_vpid_field = value; - } - - value = false; - found = false; - apply_one_bool("field-loglevel", params, &value, &found); - if (found) { - pretty->options.print_loglevel_field = value; - } - - value = false; - found = false; - apply_one_bool("field-emf", params, &value, &found); - if (found) { - pretty->options.print_emf_field = value; - } - - value = false; - found = false; - apply_one_bool("field-callsite", params, &value, &found); - if (found) { - pretty->options.print_callsite_field = value; - } - -end: - bt_value_put_ref(pretty->plugin_opt_map); - pretty->plugin_opt_map = NULL; - g_free(str); - return ret; -} - -static -void set_use_colors(struct pretty_component *pretty) -{ - switch (pretty->options.color) { - case PRETTY_COLOR_OPT_ALWAYS: - pretty->use_colors = true; - break; - case PRETTY_COLOR_OPT_AUTO: - pretty->use_colors = pretty->out == stdout && - bt_common_colors_supported(); - break; - case PRETTY_COLOR_OPT_NEVER: - pretty->use_colors = false; - break; - } -} - -static -void init_stream_packet_context_quarks(void) -{ - stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] = - g_quark_from_string("timestamp_begin"); - stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] = - g_quark_from_string("timestamp_begin"); - stream_packet_context_quarks[Q_TIMESTAMP_END] = - g_quark_from_string("timestamp_end"); - stream_packet_context_quarks[Q_EVENTS_DISCARDED] = - g_quark_from_string("events_discarded"); - stream_packet_context_quarks[Q_CONTENT_SIZE] = - g_quark_from_string("content_size"); - stream_packet_context_quarks[Q_PACKET_SIZE] = - g_quark_from_string("packet_size"); - stream_packet_context_quarks[Q_PACKET_SEQ_NUM] = - g_quark_from_string("packet_seq_num"); -} - -BT_HIDDEN -bt_self_component_status pretty_init( - bt_self_component_sink *comp, - const bt_value *params, - UNUSED_VAR void *init_method_data) -{ - bt_self_component_status ret; - struct pretty_component *pretty = create_pretty(); - - if (!pretty) { - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - ret = bt_self_component_sink_add_input_port(comp, in_port_name, - NULL, NULL); - if (ret != BT_SELF_COMPONENT_STATUS_OK) { - goto end; - } - - pretty->out = stdout; - pretty->err = stderr; - - pretty->delta_cycles = -1ULL; - pretty->last_cycles_timestamp = -1ULL; - - pretty->delta_real_timestamp = -1ULL; - pretty->last_real_timestamp = -1ULL; - - if (apply_params(pretty, params)) { - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto error; - } - - set_use_colors(pretty); - bt_self_component_set_data( - bt_self_component_sink_as_self_component(comp), pretty); - init_stream_packet_context_quarks(); - -end: - return ret; - -error: - destroy_pretty_data(pretty); - return ret; -} diff --git a/plugins/text/pretty/pretty.h b/plugins/text/pretty/pretty.h deleted file mode 100644 index 4ebf54b3..00000000 --- a/plugins/text/pretty/pretty.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H -#define BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H - -/* - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -enum pretty_default { - PRETTY_DEFAULT_UNSET, - PRETTY_DEFAULT_SHOW, - PRETTY_DEFAULT_HIDE, -}; - -enum pretty_color_option { - PRETTY_COLOR_OPT_NEVER, - PRETTY_COLOR_OPT_AUTO, - PRETTY_COLOR_OPT_ALWAYS, -}; - -struct pretty_options { - char *output_path; - - enum pretty_default name_default; - enum pretty_default field_default; - - bool print_scope_field_names; - bool print_header_field_names; - bool print_context_field_names; - bool print_payload_field_names; - - bool print_delta_field; - bool print_loglevel_field; - bool print_emf_field; - bool print_callsite_field; - bool print_trace_field; - bool print_trace_domain_field; - bool print_trace_procname_field; - bool print_trace_vpid_field; - bool print_trace_hostname_field; - - bool print_timestamp_cycles; - bool clock_seconds; - bool clock_date; - bool clock_gmt; - enum pretty_color_option color; - bool verbose; -}; - -struct pretty_component { - struct pretty_options options; - bt_self_component_port_input_message_iterator *iterator; - FILE *out, *err; - int depth; /* nesting, used for tabulation alignment. */ - bool start_line; - GString *string; - GString *tmp_string; - bt_value *plugin_opt_map; /* Temporary parameter map. */ - bool use_colors; - - uint64_t last_cycles_timestamp; - uint64_t delta_cycles; - - uint64_t last_real_timestamp; - uint64_t delta_real_timestamp; - - bool negative_timestamp_warning_done; -}; - -enum stream_packet_context_quarks_enum { - Q_TIMESTAMP_BEGIN, - Q_TIMESTAMP_END, - Q_EVENTS_DISCARDED, - Q_CONTENT_SIZE, - Q_PACKET_SIZE, - Q_PACKET_SEQ_NUM, - STREAM_PACKET_CONTEXT_QUARKS_LEN, /* Always the last one of this enum. */ -}; - -extern -GQuark stream_packet_context_quarks[STREAM_PACKET_CONTEXT_QUARKS_LEN]; - -BT_HIDDEN -bt_self_component_status pretty_init( - bt_self_component_sink *component, - const bt_value *params, - void *init_method_data); - -BT_HIDDEN -bt_self_component_status pretty_consume( - bt_self_component_sink *component); - -BT_HIDDEN -bt_self_component_status pretty_graph_is_configured( - bt_self_component_sink *component); - -BT_HIDDEN -void pretty_finalize(bt_self_component_sink *component); - -BT_HIDDEN -int pretty_print_event(struct pretty_component *pretty, - const bt_message *event_msg); - -BT_HIDDEN -int pretty_print_discarded_items(struct pretty_component *pretty, - const bt_message *msg); - -#endif /* BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H */ diff --git a/plugins/text/pretty/print.c b/plugins/text/pretty/print.c deleted file mode 100644 index a30f3a36..00000000 --- a/plugins/text/pretty/print.c +++ /dev/null @@ -1,1390 +0,0 @@ -/* - * Copyright 2016 Jérémie Galarneau - * Copyright 2016 Mathieu Desnoyers - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "pretty.h" - -#define NSEC_PER_SEC 1000000000LL - -#define COLOR_NAME BT_COMMON_COLOR_BOLD -#define COLOR_FIELD_NAME BT_COMMON_COLOR_FG_CYAN -#define COLOR_RST BT_COMMON_COLOR_RESET -#define COLOR_STRING_VALUE BT_COMMON_COLOR_BOLD -#define COLOR_NUMBER_VALUE BT_COMMON_COLOR_BOLD -#define COLOR_ENUM_MAPPING_NAME BT_COMMON_COLOR_BOLD -#define COLOR_UNKNOWN BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_RED -#define COLOR_EVENT_NAME BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_MAGENTA -#define COLOR_TIMESTAMP BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_YELLOW - -struct timestamp { - int64_t real_timestamp; /* Relative to UNIX epoch. */ - uint64_t clock_snapshot; /* In cycles. */ -}; - -static -int print_field(struct pretty_component *pretty, - const bt_field *field, bool print_names, - GQuark *filters_fields, int filter_array_len); - -static -void print_name_equal(struct pretty_component *pretty, const char *name) -{ - if (pretty->use_colors) { - g_string_append_printf(pretty->string, "%s%s%s = ", COLOR_NAME, - name, COLOR_RST); - } else { - g_string_append_printf(pretty->string, "%s = ", name); - } -} - -static -void print_field_name_equal(struct pretty_component *pretty, const char *name) -{ - if (pretty->use_colors) { - g_string_append_printf(pretty->string, "%s%s%s = ", - COLOR_FIELD_NAME, name, COLOR_RST); - } else { - g_string_append_printf(pretty->string, "%s = ", name); - } -} - -static -void print_timestamp_cycles(struct pretty_component *pretty, - const bt_clock_snapshot *clock_snapshot, bool update_last) -{ - uint64_t cycles; - - cycles = bt_clock_snapshot_get_value(clock_snapshot); - g_string_append_printf(pretty->string, "%020" PRIu64, cycles); - - if (update_last) { - if (pretty->last_cycles_timestamp != -1ULL) { - pretty->delta_cycles = cycles - pretty->last_cycles_timestamp; - } - - pretty->last_cycles_timestamp = cycles; - } -} - -static -void print_timestamp_wall(struct pretty_component *pretty, - const bt_clock_snapshot *clock_snapshot, bool update_last) -{ - int ret; - int64_t ts_nsec = 0; /* add configurable offset */ - int64_t ts_sec = 0; /* add configurable offset */ - uint64_t ts_sec_abs, ts_nsec_abs; - bool is_negative; - - if (!clock_snapshot) { - g_string_append(pretty->string, "??:??:??.?????????"); - return; - } - - ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, &ts_nsec); - if (ret) { - // TODO: log, this is unexpected - g_string_append(pretty->string, "Error"); - return; - } - - if (update_last) { - if (pretty->last_real_timestamp != -1ULL) { - pretty->delta_real_timestamp = ts_nsec - pretty->last_real_timestamp; - } - - pretty->last_real_timestamp = ts_nsec; - } - - ts_sec += ts_nsec / NSEC_PER_SEC; - ts_nsec = ts_nsec % NSEC_PER_SEC; - - if (ts_sec >= 0 && ts_nsec >= 0) { - is_negative = false; - ts_sec_abs = ts_sec; - ts_nsec_abs = ts_nsec; - } else if (ts_sec > 0 && ts_nsec < 0) { - is_negative = false; - ts_sec_abs = ts_sec - 1; - ts_nsec_abs = NSEC_PER_SEC + ts_nsec; - } else if (ts_sec == 0 && ts_nsec < 0) { - is_negative = true; - ts_sec_abs = ts_sec; - ts_nsec_abs = -ts_nsec; - } else if (ts_sec < 0 && ts_nsec > 0) { - is_negative = true; - ts_sec_abs = -(ts_sec + 1); - ts_nsec_abs = NSEC_PER_SEC - ts_nsec; - } else if (ts_sec < 0 && ts_nsec == 0) { - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = ts_nsec; - } else { /* (ts_sec < 0 && ts_nsec < 0) */ - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = -ts_nsec; - } - - if (!pretty->options.clock_seconds) { - struct tm tm; - time_t time_s = (time_t) ts_sec_abs; - - if (is_negative && !pretty->negative_timestamp_warning_done) { - // TODO: log instead - fprintf(stderr, "[warning] Fallback to [sec.ns] to print negative time value. Use --clock-seconds.\n"); - pretty->negative_timestamp_warning_done = true; - goto seconds; - } - - if (!pretty->options.clock_gmt) { - struct tm *res; - - res = bt_localtime_r(&time_s, &tm); - if (!res) { - // TODO: log instead - fprintf(stderr, "[warning] Unable to get localtime.\n"); - goto seconds; - } - } else { - struct tm *res; - - res = bt_gmtime_r(&time_s, &tm); - if (!res) { - // TODO: log instead - fprintf(stderr, "[warning] Unable to get gmtime.\n"); - goto seconds; - } - } - if (pretty->options.clock_date) { - char timestr[26]; - size_t res; - - /* Print date and time */ - res = strftime(timestr, sizeof(timestr), - "%Y-%m-%d ", &tm); - if (!res) { - // TODO: log instead - fprintf(stderr, "[warning] Unable to print ascii time.\n"); - goto seconds; - } - - g_string_append(pretty->string, timestr); - } - - /* Print time in HH:MM:SS.ns */ - g_string_append_printf(pretty->string, - "%02d:%02d:%02d.%09" PRIu64, tm.tm_hour, tm.tm_min, - tm.tm_sec, ts_nsec_abs); - goto end; - } -seconds: - g_string_append_printf(pretty->string, "%s%" PRId64 ".%09" PRIu64, - is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); -end: - return; -} - -static -int print_event_timestamp(struct pretty_component *pretty, - const bt_message *event_msg, bool *start_line) -{ - bool print_names = pretty->options.print_header_field_names; - int ret = 0; - const bt_clock_snapshot *clock_snapshot = NULL; - - if (!bt_message_event_borrow_stream_class_default_clock_class_const( - event_msg)) { - /* No default clock class: skip the timestamp without an error */ - goto end; - } - - clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const(event_msg); - - if (print_names) { - print_name_equal(pretty, "timestamp"); - } else { - g_string_append(pretty->string, "["); - } - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_TIMESTAMP); - } - if (pretty->options.print_timestamp_cycles) { - print_timestamp_cycles(pretty, clock_snapshot, true); - } else { - print_timestamp_wall(pretty, clock_snapshot, true); - } - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - - if (!print_names) - g_string_append(pretty->string, "] "); - - if (pretty->options.print_delta_field) { - if (print_names) { - g_string_append(pretty->string, ", "); - print_name_equal(pretty, "delta"); - } else { - g_string_append(pretty->string, "("); - } - if (pretty->options.print_timestamp_cycles) { - if (pretty->delta_cycles == -1ULL) { - g_string_append(pretty->string, - "+??????????\?\?"); /* Not a trigraph. */ - } else { - g_string_append_printf(pretty->string, - "+%012" PRIu64, pretty->delta_cycles); - } - } else { - if (pretty->delta_real_timestamp != -1ULL) { - uint64_t delta_sec, delta_nsec, delta; - - delta = pretty->delta_real_timestamp; - delta_sec = delta / NSEC_PER_SEC; - delta_nsec = delta % NSEC_PER_SEC; - g_string_append_printf(pretty->string, - "+%" PRIu64 ".%09" PRIu64, - delta_sec, delta_nsec); - } else { - g_string_append(pretty->string, "+?.?????????"); - } - } - if (!print_names) { - g_string_append(pretty->string, ") "); - } - } - *start_line = !print_names; - -end: - return ret; -} - -static -int print_event_header(struct pretty_component *pretty, - const bt_message *event_msg) -{ - bool print_names = pretty->options.print_header_field_names; - int ret = 0; - const bt_event_class *event_class = NULL; - const bt_stream_class *stream_class = NULL; - const bt_trace_class *trace_class = NULL; - const bt_packet *packet = NULL; - const bt_stream *stream = NULL; - const bt_trace *trace = NULL; - const bt_event *event = bt_message_event_borrow_event_const(event_msg); - int dom_print = 0; - bt_property_availability prop_avail; - - event_class = bt_event_borrow_class_const(event); - stream_class = bt_event_class_borrow_stream_class_const(event_class); - trace_class = bt_stream_class_borrow_trace_class_const(stream_class); - packet = bt_event_borrow_packet_const(event); - stream = bt_packet_borrow_stream_const(packet); - trace = bt_stream_borrow_trace_const(stream); - ret = print_event_timestamp(pretty, event_msg, &pretty->start_line); - if (ret) { - goto end; - } - if (pretty->options.print_trace_field) { - const char *name; - - name = bt_trace_get_name(trace); - if (name) { - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "trace"); - } - - g_string_append(pretty->string, name); - - if (print_names) { - g_string_append(pretty->string, ", "); - } - } - } - if (pretty->options.print_trace_hostname_field) { - const bt_value *hostname_str; - - hostname_str = bt_trace_class_borrow_environment_entry_value_by_name_const( - trace_class, "hostname"); - if (hostname_str) { - const char *str; - - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "trace:hostname"); - } - str = bt_value_string_get(hostname_str); - g_string_append(pretty->string, str); - dom_print = 1; - } - } - if (pretty->options.print_trace_domain_field) { - const bt_value *domain_str; - - domain_str = bt_trace_class_borrow_environment_entry_value_by_name_const( - trace_class, "domain"); - if (domain_str) { - const char *str; - - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "trace:domain"); - } else if (dom_print) { - g_string_append(pretty->string, ":"); - } - str = bt_value_string_get(domain_str); - g_string_append(pretty->string, str); - dom_print = 1; - } - } - if (pretty->options.print_trace_procname_field) { - const bt_value *procname_str; - - procname_str = bt_trace_class_borrow_environment_entry_value_by_name_const( - trace_class, "procname"); - if (procname_str) { - const char *str; - - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "trace:procname"); - } else if (dom_print) { - g_string_append(pretty->string, ":"); - } - str = bt_value_string_get(procname_str); - g_string_append(pretty->string, str); - dom_print = 1; - } - } - if (pretty->options.print_trace_vpid_field) { - const bt_value *vpid_value; - - vpid_value = bt_trace_class_borrow_environment_entry_value_by_name_const( - trace_class, "vpid"); - if (vpid_value) { - int64_t value; - - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "trace:vpid"); - } else if (dom_print) { - g_string_append(pretty->string, ":"); - } - value = bt_value_signed_integer_get(vpid_value); - g_string_append_printf(pretty->string, - "(%" PRId64 ")", value); - dom_print = 1; - } - } - if (pretty->options.print_loglevel_field) { - static const char *log_level_names[] = { - [ BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY ] = "TRACE_EMERG", - [ BT_EVENT_CLASS_LOG_LEVEL_ALERT ] = "TRACE_ALERT", - [ BT_EVENT_CLASS_LOG_LEVEL_CRITICAL ] = "TRACE_CRIT", - [ BT_EVENT_CLASS_LOG_LEVEL_ERROR ] = "TRACE_ERR", - [ BT_EVENT_CLASS_LOG_LEVEL_WARNING ] = "TRACE_WARNING", - [ BT_EVENT_CLASS_LOG_LEVEL_NOTICE ] = "TRACE_NOTICE", - [ BT_EVENT_CLASS_LOG_LEVEL_INFO ] = "TRACE_INFO", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM ] = "TRACE_DEBUG_SYSTEM", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM ] = "TRACE_DEBUG_PROGRAM", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS ] = "TRACE_DEBUG_PROCESS", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE ] = "TRACE_DEBUG_MODULE", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT ] = "TRACE_DEBUG_UNIT", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION ] = "TRACE_DEBUG_FUNCTION", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE ] = "TRACE_DEBUG_LINE", - [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG ] = "TRACE_DEBUG", - }; - bt_event_class_log_level log_level; - const char *log_level_str = NULL; - - prop_avail = bt_event_class_get_log_level(event_class, - &log_level); - if (prop_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { - log_level_str = log_level_names[log_level]; - BT_ASSERT(log_level_str); - - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "loglevel"); - } else if (dom_print) { - g_string_append(pretty->string, ":"); - } - - g_string_append(pretty->string, log_level_str); - g_string_append_printf( - pretty->string, " (%d)", (int) log_level); - dom_print = 1; - } - } - if (pretty->options.print_emf_field) { - const char *uri_str; - - uri_str = bt_event_class_get_emf_uri(event_class); - if (uri_str) { - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - if (print_names) { - print_name_equal(pretty, "model.emf.uri"); - } else if (dom_print) { - g_string_append(pretty->string, ":"); - } - - g_string_append(pretty->string, uri_str); - dom_print = 1; - } - } - if (dom_print && !print_names) { - g_string_append(pretty->string, " "); - } - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - pretty->start_line = true; - if (print_names) { - print_name_equal(pretty, "name"); - } - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_EVENT_NAME); - } - g_string_append(pretty->string, bt_event_class_get_name(event_class)); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - if (!print_names) { - g_string_append(pretty->string, ": "); - } else { - g_string_append(pretty->string, ", "); - } - -end: - return ret; -} - -static -int print_integer(struct pretty_component *pretty, - const bt_field *field) -{ - int ret = 0; - bt_field_class_integer_preferred_display_base base; - const bt_field_class *int_fc; - union { - uint64_t u; - int64_t s; - } v; - bool rst_color = false; - bt_field_class_type ft_type; - - int_fc = bt_field_borrow_class_const(field); - BT_ASSERT(int_fc); - ft_type = bt_field_get_class_type(field); - if (ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || - ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) { - v.u = bt_field_unsigned_integer_get_value(field); - } else { - v.s = bt_field_signed_integer_get_value(field); - } - - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_NUMBER_VALUE); - rst_color = true; - } - - base = bt_field_class_integer_get_preferred_display_base(int_fc); - switch (base) { - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: - { - int bitnr, len; - - len = bt_field_class_integer_get_field_value_range(int_fc); - g_string_append(pretty->string, "0b"); - _bt_safe_lshift(v.u, 64 - len); - for (bitnr = 0; bitnr < len; bitnr++) { - g_string_append_printf(pretty->string, "%u", (v.u & (1ULL << 63)) ? 1 : 0); - _bt_safe_lshift(v.u, 1); - } - break; - } - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: - { - if (ft_type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || - ft_type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { - int len; - - len = bt_field_class_integer_get_field_value_range( - int_fc); - if (len < 64) { - size_t rounded_len; - - BT_ASSERT(len != 0); - /* Round length to the nearest 3-bit */ - rounded_len = (((len - 1) / 3) + 1) * 3; - v.u &= ((uint64_t) 1 << rounded_len) - 1; - } - } - - g_string_append_printf(pretty->string, "0%" PRIo64, v.u); - break; - } - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL: - if (ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || - ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) { - g_string_append_printf(pretty->string, "%" PRIu64, v.u); - } else { - g_string_append_printf(pretty->string, "%" PRId64, v.s); - } - break; - case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: - { - int len; - - len = bt_field_class_integer_get_field_value_range(int_fc); - if (len < 64) { - /* Round length to the nearest nibble */ - uint8_t rounded_len = ((len + 3) & ~0x3); - - v.u &= ((uint64_t) 1 << rounded_len) - 1; - } - - g_string_append_printf(pretty->string, "0x%" PRIX64, v.u); - break; - } - default: - ret = -1; - goto end; - } -end: - if (rst_color) { - g_string_append(pretty->string, COLOR_RST); - } - return ret; -} - -static -void print_escape_string(struct pretty_component *pretty, const char *str) -{ - int i; - - g_string_append_c(pretty->string, '"'); - - for (i = 0; i < strlen(str); i++) { - /* Escape sequences not recognized by iscntrl(). */ - switch (str[i]) { - case '\\': - g_string_append(pretty->string, "\\\\"); - continue; - case '\'': - g_string_append(pretty->string, "\\\'"); - continue; - case '\"': - g_string_append(pretty->string, "\\\""); - continue; - case '\?': - g_string_append(pretty->string, "\\\?"); - continue; - } - - /* Standard characters. */ - if (!iscntrl(str[i])) { - g_string_append_c(pretty->string, str[i]); - continue; - } - - switch (str[i]) { - case '\0': - g_string_append(pretty->string, "\\0"); - break; - case '\a': - g_string_append(pretty->string, "\\a"); - break; - case '\b': - g_string_append(pretty->string, "\\b"); - break; - case '\e': - g_string_append(pretty->string, "\\e"); - break; - case '\f': - g_string_append(pretty->string, "\\f"); - break; - case '\n': - g_string_append(pretty->string, "\\n"); - break; - case '\r': - g_string_append(pretty->string, "\\r"); - break; - case '\t': - g_string_append(pretty->string, "\\t"); - break; - case '\v': - g_string_append(pretty->string, "\\v"); - break; - default: - /* Unhandled control-sequence, print as hex. */ - g_string_append_printf(pretty->string, "\\x%02x", str[i]); - break; - } - } - - g_string_append_c(pretty->string, '"'); -} - -static -int print_enum(struct pretty_component *pretty, - const bt_field *field) -{ - int ret = 0; - const bt_field_class *enumeration_field_class = NULL; - bt_field_class_enumeration_mapping_label_array label_array; - uint64_t label_count; - uint64_t i; - - enumeration_field_class = bt_field_borrow_class_const(field); - if (!enumeration_field_class) { - ret = -1; - goto end; - } - - switch (bt_field_get_class_type(field)) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - ret = bt_field_unsigned_enumeration_get_mapping_labels(field, - &label_array, &label_count); - break; - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - ret = bt_field_signed_enumeration_get_mapping_labels(field, - &label_array, &label_count); - break; - default: - abort(); - } - - if (ret) { - ret = -1; - goto end; - } - - g_string_append(pretty->string, "( "); - if (label_count == 0) { - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_UNKNOWN); - } - g_string_append(pretty->string, ""); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - goto skip_loop; - } - for (i = 0; i < label_count; i++) { - const char *mapping_name = label_array[i]; - - if (i != 0) { - g_string_append(pretty->string, ", "); - } - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_ENUM_MAPPING_NAME); - } - print_escape_string(pretty, mapping_name); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - } -skip_loop: - g_string_append(pretty->string, " : container = "); - ret = print_integer(pretty, field); - if (ret != 0) { - goto end; - } - g_string_append(pretty->string, " )"); -end: - return ret; -} - -static -int filter_field_name(struct pretty_component *pretty, const char *field_name, - GQuark *filter_fields, int filter_array_len) -{ - int i; - GQuark field_quark = g_quark_try_string(field_name); - - if (!field_quark || pretty->options.verbose) { - return 1; - } - - for (i = 0; i < filter_array_len; i++) { - if (field_quark == filter_fields[i]) { - return 0; - } - } - return 1; -} - -static -int print_struct_field(struct pretty_component *pretty, - const bt_field *_struct, - const bt_field_class *struct_class, - uint64_t i, bool print_names, uint64_t *nr_printed_fields, - GQuark *filter_fields, int filter_array_len) -{ - int ret = 0; - const char *field_name; - const bt_field *field = NULL; - const bt_field_class_structure_member *member; - - field = bt_field_structure_borrow_member_field_by_index_const(_struct, i); - if (!field) { - ret = -1; - goto end; - } - - member = bt_field_class_structure_borrow_member_by_index_const( - struct_class, i); - field_name = bt_field_class_structure_member_get_name(member); - - if (filter_fields && !filter_field_name(pretty, field_name, - filter_fields, filter_array_len)) { - ret = 0; - goto end; - } - - if (*nr_printed_fields > 0) { - g_string_append(pretty->string, ", "); - } else { - g_string_append(pretty->string, " "); - } - if (print_names) { - print_field_name_equal(pretty, field_name); - } - ret = print_field(pretty, field, print_names, NULL, 0); - *nr_printed_fields += 1; - -end: - return ret; -} - -static -int print_struct(struct pretty_component *pretty, - const bt_field *_struct, bool print_names, - GQuark *filter_fields, int filter_array_len) -{ - int ret = 0; - const bt_field_class *struct_class = NULL; - uint64_t nr_fields, i, nr_printed_fields; - - struct_class = bt_field_borrow_class_const(_struct); - if (!struct_class) { - ret = -1; - goto end; - } - - nr_fields = bt_field_class_structure_get_member_count(struct_class); - - g_string_append(pretty->string, "{"); - pretty->depth++; - nr_printed_fields = 0; - for (i = 0; i < nr_fields; i++) { - ret = print_struct_field(pretty, _struct, struct_class, i, - print_names, &nr_printed_fields, filter_fields, - filter_array_len); - if (ret != 0) { - goto end; - } - } - pretty->depth--; - g_string_append(pretty->string, " }"); - -end: - return ret; -} - -static -int print_array_field(struct pretty_component *pretty, - const bt_field *array, uint64_t i, bool print_names) -{ - const bt_field *field = NULL; - - if (i != 0) { - g_string_append(pretty->string, ", "); - } else { - g_string_append(pretty->string, " "); - } - if (print_names) { - g_string_append_printf(pretty->string, "[%" PRIu64 "] = ", i); - } - - field = bt_field_array_borrow_element_field_by_index_const(array, i); - BT_ASSERT(field); - return print_field(pretty, field, print_names, NULL, 0); -} - -static -int print_array(struct pretty_component *pretty, - const bt_field *array, bool print_names) -{ - int ret = 0; - const bt_field_class *array_class = NULL; - uint64_t len; - uint64_t i; - - array_class = bt_field_borrow_class_const(array); - if (!array_class) { - ret = -1; - goto end; - } - len = bt_field_array_get_length(array); - g_string_append(pretty->string, "["); - pretty->depth++; - for (i = 0; i < len; i++) { - ret = print_array_field(pretty, array, i, print_names); - if (ret != 0) { - goto end; - } - } - pretty->depth--; - g_string_append(pretty->string, " ]"); - -end: - return ret; -} - -static -int print_sequence_field(struct pretty_component *pretty, - const bt_field *seq, uint64_t i, bool print_names) -{ - const bt_field *field = NULL; - - if (i != 0) { - g_string_append(pretty->string, ", "); - } else { - g_string_append(pretty->string, " "); - } - if (print_names) { - g_string_append_printf(pretty->string, "[%" PRIu64 "] = ", i); - } - - field = bt_field_array_borrow_element_field_by_index_const(seq, i); - BT_ASSERT(field); - return print_field(pretty, field, print_names, NULL, 0); -} - -static -int print_sequence(struct pretty_component *pretty, - const bt_field *seq, bool print_names) -{ - int ret = 0; - uint64_t len; - uint64_t i; - - len = bt_field_array_get_length(seq); - g_string_append(pretty->string, "["); - - pretty->depth++; - for (i = 0; i < len; i++) { - ret = print_sequence_field(pretty, seq, i, print_names); - if (ret != 0) { - goto end; - } - } - pretty->depth--; - g_string_append(pretty->string, " ]"); - -end: - return ret; -} - -static -int print_variant(struct pretty_component *pretty, - const bt_field *variant, bool print_names) -{ - int ret = 0; - const bt_field *field = NULL; - - field = bt_field_variant_borrow_selected_option_field_const(variant); - BT_ASSERT(field); - g_string_append(pretty->string, "{ "); - pretty->depth++; - if (print_names) { - // TODO: find tag's name using field path - // print_field_name_equal(pretty, tag_choice); - } - ret = print_field(pretty, field, print_names, NULL, 0); - if (ret != 0) { - goto end; - } - pretty->depth--; - g_string_append(pretty->string, " }"); - -end: - return ret; -} - -static -int print_field(struct pretty_component *pretty, - const bt_field *field, bool print_names, - GQuark *filter_fields, int filter_array_len) -{ - bt_field_class_type class_id; - - class_id = bt_field_get_class_type(field); - switch (class_id) { - case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: - case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: - return print_integer(pretty, field); - case BT_FIELD_CLASS_TYPE_REAL: - { - double v; - - v = bt_field_real_get_value(field); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_NUMBER_VALUE); - } - g_string_append_printf(pretty->string, "%g", v); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - return 0; - } - case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: - case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: - return print_enum(pretty, field); - case BT_FIELD_CLASS_TYPE_STRING: - { - const char *str; - - str = bt_field_string_get_value(field); - if (!str) { - return -1; - } - - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_STRING_VALUE); - } - print_escape_string(pretty, str); - if (pretty->use_colors) { - g_string_append(pretty->string, COLOR_RST); - } - return 0; - } - case BT_FIELD_CLASS_TYPE_STRUCTURE: - return print_struct(pretty, field, print_names, filter_fields, - filter_array_len); - case BT_FIELD_CLASS_TYPE_VARIANT: - return print_variant(pretty, field, print_names); - case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: - return print_array(pretty, field, print_names); - case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: - return print_sequence(pretty, field, print_names); - default: - // TODO: log instead - fprintf(pretty->err, "[error] Unknown type id: %d\n", (int) class_id); - return -1; - } -} - -static -int print_stream_packet_context(struct pretty_component *pretty, - const bt_event *event) -{ - int ret = 0; - const bt_packet *packet = NULL; - const bt_field *main_field = NULL; - - packet = bt_event_borrow_packet_const(event); - if (!packet) { - ret = -1; - goto end; - } - main_field = bt_packet_borrow_context_field_const(packet); - if (!main_field) { - goto end; - } - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - pretty->start_line = false; - if (pretty->options.print_scope_field_names) { - print_name_equal(pretty, "stream.packet.context"); - } - ret = print_field(pretty, main_field, - pretty->options.print_context_field_names, - stream_packet_context_quarks, - STREAM_PACKET_CONTEXT_QUARKS_LEN); - -end: - return ret; -} - -static -int print_stream_event_context(struct pretty_component *pretty, - const bt_event *event) -{ - int ret = 0; - const bt_field *main_field = NULL; - - main_field = bt_event_borrow_common_context_field_const(event); - if (!main_field) { - goto end; - } - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - pretty->start_line = false; - if (pretty->options.print_scope_field_names) { - print_name_equal(pretty, "stream.event.context"); - } - ret = print_field(pretty, main_field, - pretty->options.print_context_field_names, NULL, 0); - -end: - return ret; -} - -static -int print_event_context(struct pretty_component *pretty, - const bt_event *event) -{ - int ret = 0; - const bt_field *main_field = NULL; - - main_field = bt_event_borrow_specific_context_field_const(event); - if (!main_field) { - goto end; - } - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - pretty->start_line = false; - if (pretty->options.print_scope_field_names) { - print_name_equal(pretty, "event.context"); - } - ret = print_field(pretty, main_field, - pretty->options.print_context_field_names, NULL, 0); - -end: - return ret; -} - -static -int print_event_payload(struct pretty_component *pretty, - const bt_event *event) -{ - int ret = 0; - const bt_field *main_field = NULL; - - main_field = bt_event_borrow_payload_field_const(event); - if (!main_field) { - goto end; - } - if (!pretty->start_line) { - g_string_append(pretty->string, ", "); - } - pretty->start_line = false; - if (pretty->options.print_scope_field_names) { - print_name_equal(pretty, "event.fields"); - } - ret = print_field(pretty, main_field, - pretty->options.print_payload_field_names, NULL, 0); - -end: - return ret; -} - -static -int flush_buf(FILE *stream, struct pretty_component *pretty) -{ - int ret = 0; - - if (pretty->string->len == 0) { - goto end; - } - - if (fwrite(pretty->string->str, pretty->string->len, 1, stream) != 1) { - ret = -1; - } - -end: - return ret; -} - -BT_HIDDEN -int pretty_print_event(struct pretty_component *pretty, - const bt_message *event_msg) -{ - int ret; - const bt_event *event = - bt_message_event_borrow_event_const(event_msg); - - BT_ASSERT(event); - pretty->start_line = true; - g_string_assign(pretty->string, ""); - ret = print_event_header(pretty, event_msg); - if (ret != 0) { - goto end; - } - - ret = print_stream_packet_context(pretty, event); - if (ret != 0) { - goto end; - } - - ret = print_stream_event_context(pretty, event); - if (ret != 0) { - goto end; - } - - ret = print_event_context(pretty, event); - if (ret != 0) { - goto end; - } - - ret = print_event_payload(pretty, event); - if (ret != 0) { - goto end; - } - - g_string_append_c(pretty->string, '\n'); - if (flush_buf(pretty->out, pretty)) { - ret = -1; - goto end; - } - -end: - return ret; -} - -static -int print_discarded_elements_msg(struct pretty_component *pretty, - const bt_stream *stream, - const bt_clock_snapshot *begin_clock_snapshot, - const bt_clock_snapshot *end_clock_snapshot, - uint64_t count, const char *elem_type) -{ - int ret = 0; - const bt_stream_class *stream_class = NULL; - const bt_trace *trace = NULL; - const char *stream_name; - const char *trace_name; - bt_uuid trace_uuid; - int64_t stream_class_id; - int64_t stream_id; - const char *init_msg; - - /* Stream name */ - stream_name = bt_stream_get_name(stream); - if (!stream_name) { - stream_name = "(unknown)"; - } - - /* Stream class ID */ - stream_class = bt_stream_borrow_class_const(stream); - BT_ASSERT(stream_class); - stream_class_id = bt_stream_class_get_id(stream_class); - - /* Stream ID */ - stream_id = bt_stream_get_id(stream); - - /* Trace name */ - trace = bt_stream_borrow_trace_const(stream); - BT_ASSERT(trace); - trace_name = bt_trace_get_name(trace); - if (!trace_name) { - trace_name = "(unknown)"; - } - - /* Trace UUID */ - trace_uuid = bt_trace_class_get_uuid( - bt_trace_borrow_class_const(trace)); - - /* Format message */ - g_string_assign(pretty->string, ""); - - if (count == UINT64_C(-1)) { - init_msg = "Tracer may have discarded"; - } else { - init_msg = "Tracer discarded"; - } - - g_string_append_printf(pretty->string, - "%s%sWARNING%s%s: %s ", - bt_common_color_fg_yellow(), - bt_common_color_bold(), - bt_common_color_reset(), - bt_common_color_fg_yellow(), init_msg); - - if (count == UINT64_C(-1)) { - g_string_append_printf(pretty->string, "%ss", elem_type); - } else { - g_string_append_printf(pretty->string, - "%" PRIu64 " %s%s", count, elem_type, - count == 1 ? "" : "s"); - } - - g_string_append_c(pretty->string, ' '); - - if (begin_clock_snapshot && end_clock_snapshot) { - g_string_append(pretty->string, "between ["); - print_timestamp_wall(pretty, begin_clock_snapshot, false); - g_string_append(pretty->string, "] and ["); - print_timestamp_wall(pretty, end_clock_snapshot, false); - g_string_append(pretty->string, "]"); - } else { - g_string_append(pretty->string, "(unknown time range)"); - } - - g_string_append_printf(pretty->string, " in trace \"%s\" ", trace_name); - - if (trace_uuid) { - g_string_append_printf(pretty->string, - "(UUID: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x) ", - trace_uuid[0], - trace_uuid[1], - trace_uuid[2], - trace_uuid[3], - trace_uuid[4], - trace_uuid[5], - trace_uuid[6], - trace_uuid[7], - trace_uuid[8], - trace_uuid[9], - trace_uuid[10], - trace_uuid[11], - trace_uuid[12], - trace_uuid[13], - trace_uuid[14], - trace_uuid[15]); - } else { - g_string_append(pretty->string, "(no UUID) "); - } - - g_string_append_printf(pretty->string, - "within stream \"%s\" (stream class ID: %" PRIu64 ", ", - stream_name, stream_class_id); - - if (stream_id >= 0) { - g_string_append_printf(pretty->string, - "stream ID: %" PRIu64, stream_id); - } else { - g_string_append(pretty->string, "no stream ID"); - } - - g_string_append_printf(pretty->string, ").%s\n", - bt_common_color_reset()); - - /* - * Print to standard error stream to remain backward compatible - * with Babeltrace 1. - */ - if (flush_buf(stderr, pretty)) { - ret = -1; - } - - return ret; -} - -BT_HIDDEN -int pretty_print_discarded_items(struct pretty_component *pretty, - const bt_message *msg) -{ - const bt_clock_snapshot *begin = NULL; - const bt_clock_snapshot *end = NULL; - const bt_stream *stream; - const bt_stream_class *stream_class; - uint64_t count = UINT64_C(-1); - const char *elem_type; - - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - stream = bt_message_discarded_events_borrow_stream_const(msg); - - if (bt_message_discarded_events_get_count(msg, &count) == - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { - count = UINT64_C(-1); - } - - elem_type = "event"; - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - stream = bt_message_discarded_packets_borrow_stream_const(msg); - - if (bt_message_discarded_packets_get_count(msg, &count) == - BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { - count = UINT64_C(-1); - } - - elem_type = "packet"; - break; - default: - abort(); - } - - BT_ASSERT(stream); - stream_class = bt_stream_borrow_class_const(stream); - - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - if (bt_stream_class_discarded_events_have_default_clock_snapshots( - stream_class)) { - begin = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - end = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - msg); - } - - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - if (bt_stream_class_discarded_packets_have_default_clock_snapshots( - stream_class)) { - begin = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - end = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - msg); - } - - break; - default: - abort(); - } - - print_discarded_elements_msg(pretty, stream, begin, end, - count, elem_type); - return 0; -} diff --git a/plugins/utils/Makefile.am b/plugins/utils/Makefile.am deleted file mode 100644 index 9211dd9e..00000000 --- a/plugins/utils/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -SUBDIRS = dummy muxer counter trimmer . - -plugindir = "$(PLUGINSDIR)" -plugin_LTLIBRARIES = babeltrace-plugin-utils.la - -babeltrace_plugin_utils_la_SOURCES = plugin.c -babeltrace_plugin_utils_la_LDFLAGS = \ - $(LT_NO_UNDEFINED) \ - -avoid-version -module -babeltrace_plugin_utils_la_LIBADD = \ - dummy/libbabeltrace2-plugin-dummy-cc.la \ - muxer/libbabeltrace2-plugin-muxer.la \ - counter/libbabeltrace2-plugin-counter-cc.la \ - trimmer/libbabeltrace2-plugin-trimmer.la - -if !ENABLE_BUILT_IN_PLUGINS -babeltrace_plugin_utils_la_LIBADD += \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la -endif diff --git a/plugins/utils/counter/Makefile.am b/plugins/utils/counter/Makefile.am deleted file mode 100644 index 67d1656d..00000000 --- a/plugins/utils/counter/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-counter-cc.la -libbabeltrace2_plugin_counter_cc_la_SOURCES = \ - counter.c \ - counter.h \ - logging.c \ - logging.h diff --git a/plugins/utils/counter/counter.c b/plugins/utils/counter/counter.c deleted file mode 100644 index 5a6eadaf..00000000 --- a/plugins/utils/counter/counter.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-UTILS-COUNTER-FLT" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "counter.h" - -#define PRINTF_COUNT(_what, _var, args...) \ - do { \ - if (counter->count._var != 0 || !counter->hide_zero) { \ - printf("%15" PRIu64 " %s message%s\n", \ - counter->count._var, \ - (_what), \ - counter->count._var == 1 ? "" : "s"); \ - } \ - } while (0) - -static -const char * const in_port_name = "in"; - -static -uint64_t get_total_count(struct counter *counter) -{ - return counter->count.event + - counter->count.stream_begin + - counter->count.stream_end + - counter->count.stream_activity_begin + - counter->count.stream_activity_end + - counter->count.packet_begin + - counter->count.packet_end + - counter->count.disc_events + - counter->count.disc_packets + - counter->count.msg_iter_inactivity + - counter->count.other; -} - -static -void print_count(struct counter *counter) -{ - uint64_t total = get_total_count(counter); - - PRINTF_COUNT("Event", event); - PRINTF_COUNT("Stream beginning", stream_begin); - PRINTF_COUNT("Stream end", stream_end); - PRINTF_COUNT("Stream activity beginning", stream_activity_begin); - PRINTF_COUNT("Stream activity end", stream_activity_end); - PRINTF_COUNT("Packet beginning", packet_begin); - PRINTF_COUNT("Packet end", packet_end); - PRINTF_COUNT("Discarded event", disc_events); - PRINTF_COUNT("Discarded packet", disc_packets); - PRINTF_COUNT("Message iterator inactivity", msg_iter_inactivity); - - if (counter->count.other > 0) { - PRINTF_COUNT("Other (unknown)", other); - } - - printf("%s%15" PRIu64 " message%s (TOTAL)%s\n", - bt_common_color_bold(), total, total == 1 ? "" : "s", - bt_common_color_reset()); - counter->last_printed_total = total; -} - -static -void try_print_count(struct counter *counter, uint64_t msg_count) -{ - if (counter->step == 0) { - /* No update */ - return; - } - - counter->at += msg_count; - - if (counter->at >= counter->step) { - counter->at = 0; - print_count(counter); - putchar('\n'); - } -} - -static -void try_print_last(struct counter *counter) -{ - const uint64_t total = get_total_count(counter); - - if (total != counter->last_printed_total) { - print_count(counter); - } -} - -void destroy_private_counter_data(struct counter *counter) -{ - bt_self_component_port_input_message_iterator_put_ref(counter->msg_iter); - g_free(counter); -} - -BT_HIDDEN -void counter_finalize(bt_self_component_sink *comp) -{ - struct counter *counter; - - BT_ASSERT(comp); - counter = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(counter); - try_print_last(counter); - bt_self_component_port_input_message_iterator_put_ref(counter->msg_iter); - g_free(counter); -} - -BT_HIDDEN -bt_self_component_status counter_init( - bt_self_component_sink *component, - const bt_value *params, - UNUSED_VAR void *init_method_data) -{ - bt_self_component_status ret; - struct counter *counter = g_new0(struct counter, 1); - const bt_value *step = NULL; - const bt_value *hide_zero = NULL; - - if (!counter) { - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto error; - } - - ret = bt_self_component_sink_add_input_port(component, - "in", NULL, NULL); - if (ret != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - counter->last_printed_total = -1ULL; - counter->step = 10000; - step = bt_value_map_borrow_entry_value_const(params, "step"); - if (step) { - if (!bt_value_is_unsigned_integer(step)) { - BT_LOGE("`step` parameter: expecting an unsigned integer value: " - "type=%s", bt_common_value_type_string( - bt_value_get_type(step))); - goto error; - } - - counter->step = bt_value_unsigned_integer_get(step); - } - - hide_zero = bt_value_map_borrow_entry_value_const(params, "hide-zero"); - if (hide_zero) { - if (!bt_value_is_bool(hide_zero)) { - BT_LOGE("`hide-zero` parameter: expecting a boolean value: " - "type=%s", bt_common_value_type_string( - bt_value_get_type(hide_zero))); - goto error; - } - - counter->hide_zero = (bool) bt_value_bool_get(hide_zero); - } - - bt_self_component_set_data( - bt_self_component_sink_as_self_component(component), - counter); - goto end; - -error: - destroy_private_counter_data(counter); - ret = BT_SELF_COMPONENT_STATUS_ERROR; - -end: - return ret; -} - -BT_HIDDEN -bt_self_component_status counter_graph_is_configured( - bt_self_component_sink *comp) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct counter *counter; - bt_self_component_port_input_message_iterator *iterator; - - counter = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(counter); - iterator = bt_self_component_port_input_message_iterator_create( - bt_self_component_sink_borrow_input_port_by_name(comp, - in_port_name)); - if (!iterator) { - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( - counter->msg_iter, iterator); - -end: - return status; -} - -BT_HIDDEN -bt_self_component_status counter_consume( - bt_self_component_sink *comp) -{ - bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; - struct counter *counter; - bt_message_iterator_status it_ret; - uint64_t msg_count; - bt_message_array_const msgs; - - counter = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(counter); - - if (unlikely(!counter->msg_iter)) { - try_print_last(counter); - ret = BT_SELF_COMPONENT_STATUS_END; - goto end; - } - - /* Consume messages */ - it_ret = bt_self_component_port_input_message_iterator_next( - counter->msg_iter, &msgs, &msg_count); - if (it_ret < 0) { - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - - switch (it_ret) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - { - uint64_t i; - - for (i = 0; i < msg_count; i++) { - const bt_message *msg = msgs[i]; - - BT_ASSERT(msg); - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_EVENT: - counter->count.event++; - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - counter->count.packet_begin++; - break; - case BT_MESSAGE_TYPE_PACKET_END: - counter->count.packet_end++; - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - counter->count.msg_iter_inactivity++; - break; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - counter->count.stream_begin++; - break; - case BT_MESSAGE_TYPE_STREAM_END: - counter->count.stream_end++; - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - counter->count.stream_activity_begin++; - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - counter->count.stream_activity_end++; - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - counter->count.disc_events++; - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - counter->count.disc_packets++; - break; - default: - counter->count.other++; - } - - bt_message_put_ref(msg); - } - - ret = BT_SELF_COMPONENT_STATUS_OK; - break; - } - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - ret = BT_SELF_COMPONENT_STATUS_AGAIN; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_END: - try_print_last(counter); - ret = BT_SELF_COMPONENT_STATUS_END; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - default: - break; - } - - try_print_count(counter, msg_count); - -end: - return ret; -} diff --git a/plugins/utils/counter/counter.h b/plugins/utils/counter/counter.h deleted file mode 100644 index c29f0fd5..00000000 --- a/plugins/utils/counter/counter.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef BABELTRACE_PLUGINS_UTILS_COUNTER_H -#define BABELTRACE_PLUGINS_UTILS_COUNTER_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct counter { - bt_self_component_port_input_message_iterator *msg_iter; - struct { - uint64_t event; - uint64_t stream_begin; - uint64_t stream_end; - uint64_t stream_activity_begin; - uint64_t stream_activity_end; - uint64_t packet_begin; - uint64_t packet_end; - uint64_t disc_events; - uint64_t disc_packets; - uint64_t msg_iter_inactivity; - uint64_t other; - } count; - uint64_t last_printed_total; - uint64_t at; - uint64_t step; - bool hide_zero; -}; - -BT_HIDDEN -bt_self_component_status counter_init( - bt_self_component_sink *component, - const bt_value *params, void *init_method_data); - -BT_HIDDEN -void counter_finalize(bt_self_component_sink *component); - -BT_HIDDEN -bt_self_component_status counter_graph_is_configured( - bt_self_component_sink *component); - -BT_HIDDEN -bt_self_component_status counter_consume(bt_self_component_sink *component); - -#endif /* BABELTRACE_PLUGINS_UTILS_COUNTER_H */ diff --git a/plugins/utils/counter/logging.c b/plugins/utils/counter/logging.c deleted file mode 100644 index 42de03f8..00000000 --- a/plugins/utils/counter/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_counter_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_counter_log_level, - "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL"); diff --git a/plugins/utils/counter/logging.h b/plugins/utils/counter/logging.h deleted file mode 100644 index 176122f5..00000000 --- a/plugins/utils/counter/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_UTILS_MUXER_LOGGING_H -#define PLUGINS_UTILS_MUXER_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_muxer_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_muxer_log_level); - -#endif /* PLUGINS_UTILS_MUXER_LOGGING_H */ diff --git a/plugins/utils/dummy/Makefile.am b/plugins/utils/dummy/Makefile.am deleted file mode 100644 index 197aaa12..00000000 --- a/plugins/utils/dummy/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-dummy-cc.la -libbabeltrace2_plugin_dummy_cc_la_SOURCES = dummy.c dummy.h diff --git a/plugins/utils/dummy/dummy.c b/plugins/utils/dummy/dummy.c deleted file mode 100644 index 59d85152..00000000 --- a/plugins/utils/dummy/dummy.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include "dummy.h" - -static -const char * const in_port_name = "in"; - -void destroy_private_dummy_data(struct dummy *dummy) -{ - bt_self_component_port_input_message_iterator_put_ref(dummy->msg_iter); - g_free(dummy); - -} - -BT_HIDDEN -void dummy_finalize(bt_self_component_sink *comp) -{ - struct dummy *dummy; - - BT_ASSERT(comp); - dummy = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(dummy); - destroy_private_dummy_data(dummy); -} - -BT_HIDDEN -bt_self_component_status dummy_init( - bt_self_component_sink *component, - const bt_value *params, - UNUSED_VAR void *init_method_data) -{ - bt_self_component_status ret; - struct dummy *dummy = g_new0(struct dummy, 1); - - if (!dummy) { - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - ret = bt_self_component_sink_add_input_port(component, - "in", NULL, NULL); - if (ret != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - bt_self_component_set_data( - bt_self_component_sink_as_self_component(component), dummy); - goto end; - -error: - destroy_private_dummy_data(dummy); - -end: - return ret; -} - -BT_HIDDEN -bt_self_component_status dummy_graph_is_configured( - bt_self_component_sink *comp) -{ - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct dummy *dummy; - bt_self_component_port_input_message_iterator *iterator; - - dummy = bt_self_component_get_data( - bt_self_component_sink_as_self_component(comp)); - BT_ASSERT(dummy); - iterator = bt_self_component_port_input_message_iterator_create( - bt_self_component_sink_borrow_input_port_by_name(comp, - in_port_name)); - if (!iterator) { - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( - dummy->msg_iter, iterator); - -end: - return status; -} - -BT_HIDDEN -bt_self_component_status dummy_consume( - bt_self_component_sink *component) -{ - bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; - bt_message_array_const msgs; - uint64_t count; - struct dummy *dummy; - bt_message_iterator_status it_ret; - uint64_t i; - - dummy = bt_self_component_get_data( - bt_self_component_sink_as_self_component(component)); - BT_ASSERT(dummy); - - if (unlikely(!dummy->msg_iter)) { - ret = BT_SELF_COMPONENT_STATUS_END; - goto end; - } - - /* Consume one message */ - it_ret = bt_self_component_port_input_message_iterator_next( - dummy->msg_iter, &msgs, &count); - switch (it_ret) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - ret = BT_SELF_COMPONENT_STATUS_OK; - - for (i = 0; i < count; i++) { - bt_message_put_ref(msgs[i]); - } - - break; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - ret = BT_SELF_COMPONENT_STATUS_AGAIN; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_END: - ret = BT_SELF_COMPONENT_STATUS_END; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_ERROR: - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - case BT_MESSAGE_ITERATOR_STATUS_NOMEM: - ret = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - default: - break; - } - -end: - return ret; -} diff --git a/plugins/utils/dummy/dummy.h b/plugins/utils/dummy/dummy.h deleted file mode 100644 index dfe21d18..00000000 --- a/plugins/utils/dummy/dummy.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef BABELTRACE_PLUGINS_UTILS_DUMMY_H -#define BABELTRACE_PLUGINS_UTILS_DUMMY_H - -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -struct dummy { - bt_self_component_port_input_message_iterator *msg_iter; -}; - -BT_HIDDEN -bt_self_component_status dummy_init( - bt_self_component_sink *component, - const bt_value *params, void *init_method_data); - -BT_HIDDEN -void dummy_finalize(bt_self_component_sink *component); - -BT_HIDDEN -bt_self_component_status dummy_graph_is_configured( - bt_self_component_sink *comp); - -BT_HIDDEN -bt_self_component_status dummy_consume( - bt_self_component_sink *component); - -#endif /* BABELTRACE_PLUGINS_UTILS_DUMMY_H */ diff --git a/plugins/utils/muxer/Makefile.am b/plugins/utils/muxer/Makefile.am deleted file mode 100644 index 9c305bcd..00000000 --- a/plugins/utils/muxer/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins - -noinst_LTLIBRARIES = libbabeltrace2-plugin-muxer.la -libbabeltrace2_plugin_muxer_la_SOURCES = muxer.c muxer.h logging.c logging.h diff --git a/plugins/utils/muxer/logging.c b/plugins/utils/muxer/logging.c deleted file mode 100644 index 3f16b977..00000000 --- a/plugins/utils/muxer/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_muxer_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_muxer_log_level, - "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL"); diff --git a/plugins/utils/muxer/logging.h b/plugins/utils/muxer/logging.h deleted file mode 100644 index 176122f5..00000000 --- a/plugins/utils/muxer/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_UTILS_MUXER_LOGGING_H -#define PLUGINS_UTILS_MUXER_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_muxer_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_muxer_log_level); - -#endif /* PLUGINS_UTILS_MUXER_LOGGING_H */ diff --git a/plugins/utils/muxer/muxer.c b/plugins/utils/muxer/muxer.c deleted file mode 100644 index 626c437b..00000000 --- a/plugins/utils/muxer/muxer.c +++ /dev/null @@ -1,1534 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-UTILS-MUXER-FLT" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "muxer.h" - -#define ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME "assume-absolute-clock-classes" - -struct muxer_comp { - /* Weak ref */ - bt_self_component_filter *self_comp; - - unsigned int next_port_num; - size_t available_input_ports; - bool initializing_muxer_msg_iter; - bool assume_absolute_clock_classes; -}; - -struct muxer_upstream_msg_iter { - /* Owned by this, NULL if ended */ - bt_self_component_port_input_message_iterator *msg_iter; - - /* Contains `const bt_message *`, owned by this */ - GQueue *msgs; -}; - -enum muxer_msg_iter_clock_class_expectation { - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY = 0, - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE, - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE, - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID, - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID, -}; - -struct muxer_msg_iter { - /* - * Array of struct muxer_upstream_msg_iter * (owned by this). - * - * NOTE: This array is searched in linearly to find the youngest - * current message. Keep this until benchmarks confirm that - * another data structure is faster than this for our typical - * use cases. - */ - GPtrArray *active_muxer_upstream_msg_iters; - - /* - * Array of struct muxer_upstream_msg_iter * (owned by this). - * - * We move ended message iterators from - * `active_muxer_upstream_msg_iters` to this array so as to be - * able to restore them when seeking. - */ - GPtrArray *ended_muxer_upstream_msg_iters; - - /* Last time returned in a message */ - int64_t last_returned_ts_ns; - - /* Clock class expectation state */ - enum muxer_msg_iter_clock_class_expectation clock_class_expectation; - - /* - * Expected clock class UUID, only valid when - * clock_class_expectation is - * MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID. - */ - unsigned char expected_clock_class_uuid[BABELTRACE_UUID_LEN]; -}; - -static -void empty_message_queue(struct muxer_upstream_msg_iter *upstream_msg_iter) -{ - const bt_message *msg; - - while ((msg = g_queue_pop_head(upstream_msg_iter->msgs))) { - bt_message_put_ref(msg); - } -} - -static -void destroy_muxer_upstream_msg_iter( - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter) -{ - if (!muxer_upstream_msg_iter) { - return; - } - - BT_LOGD("Destroying muxer's upstream message iterator wrapper: " - "addr=%p, msg-iter-addr=%p, queue-len=%u", - muxer_upstream_msg_iter, - muxer_upstream_msg_iter->msg_iter, - muxer_upstream_msg_iter->msgs->length); - bt_self_component_port_input_message_iterator_put_ref( - muxer_upstream_msg_iter->msg_iter); - - if (muxer_upstream_msg_iter->msgs) { - empty_message_queue(muxer_upstream_msg_iter); - g_queue_free(muxer_upstream_msg_iter->msgs); - } - - g_free(muxer_upstream_msg_iter); -} - -static -int muxer_msg_iter_add_upstream_msg_iter(struct muxer_msg_iter *muxer_msg_iter, - bt_self_component_port_input_message_iterator *self_msg_iter) -{ - int ret = 0; - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = - g_new0(struct muxer_upstream_msg_iter, 1); - - if (!muxer_upstream_msg_iter) { - BT_LOGE_STR("Failed to allocate one muxer's upstream message iterator wrapper."); - goto error; - } - - muxer_upstream_msg_iter->msg_iter = self_msg_iter; - bt_self_component_port_input_message_iterator_get_ref(muxer_upstream_msg_iter->msg_iter); - muxer_upstream_msg_iter->msgs = g_queue_new(); - if (!muxer_upstream_msg_iter->msgs) { - BT_LOGE_STR("Failed to allocate a GQueue."); - goto error; - } - - g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters, - muxer_upstream_msg_iter); - BT_LOGD("Added muxer's upstream message iterator wrapper: " - "addr=%p, muxer-msg-iter-addr=%p, msg-iter-addr=%p", - muxer_upstream_msg_iter, muxer_msg_iter, - self_msg_iter); - - goto end; - -error: - g_free(muxer_upstream_msg_iter); - ret = -1; - -end: - return ret; -} - -static -bt_self_component_status add_available_input_port( - bt_self_component_filter *self_comp) -{ - struct muxer_comp *muxer_comp = bt_self_component_get_data( - bt_self_component_filter_as_self_component(self_comp)); - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - GString *port_name = NULL; - - BT_ASSERT(muxer_comp); - port_name = g_string_new("in"); - if (!port_name) { - BT_LOGE_STR("Failed to allocate a GString."); - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto end; - } - - g_string_append_printf(port_name, "%u", muxer_comp->next_port_num); - status = bt_self_component_filter_add_input_port( - self_comp, port_name->str, NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGE("Cannot add input port to muxer component: " - "port-name=\"%s\", comp-addr=%p, status=%s", - port_name->str, self_comp, - bt_self_component_status_string(status)); - goto end; - } - - muxer_comp->available_input_ports++; - muxer_comp->next_port_num++; - BT_LOGD("Added one input port to muxer component: " - "port-name=\"%s\", comp-addr=%p", - port_name->str, self_comp); - -end: - if (port_name) { - g_string_free(port_name, TRUE); - } - - return status; -} - -static -bt_self_component_status create_output_port( - bt_self_component_filter *self_comp) -{ - return bt_self_component_filter_add_output_port( - self_comp, "out", NULL, NULL); -} - -static -void destroy_muxer_comp(struct muxer_comp *muxer_comp) -{ - if (!muxer_comp) { - return; - } - - g_free(muxer_comp); -} - -static -bt_value *get_default_params(void) -{ - bt_value *params; - int ret; - - params = bt_value_map_create(); - if (!params) { - BT_LOGE_STR("Cannot create a map value object."); - goto error; - } - - ret = bt_value_map_insert_bool_entry(params, - ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME, false); - if (ret) { - BT_LOGE_STR("Cannot add boolean value to map value object."); - goto error; - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(params); - -end: - return params; -} - -static -int configure_muxer_comp(struct muxer_comp *muxer_comp, - const bt_value *params) -{ - bt_value *default_params = NULL; - bt_value *real_params = NULL; - const bt_value *assume_absolute_clock_classes = NULL; - int ret = 0; - bt_bool bool_val; - - default_params = get_default_params(); - if (!default_params) { - BT_LOGE("Cannot get default parameters: " - "muxer-comp-addr=%p", muxer_comp); - goto error; - } - - ret = bt_value_map_extend(default_params, params, &real_params); - if (ret) { - BT_LOGE("Cannot extend default parameters map value: " - "muxer-comp-addr=%p, def-params-addr=%p, " - "params-addr=%p", muxer_comp, default_params, - params); - goto error; - } - - assume_absolute_clock_classes = bt_value_map_borrow_entry_value(real_params, - ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME); - if (assume_absolute_clock_classes && - !bt_value_is_bool(assume_absolute_clock_classes)) { - BT_LOGE("Expecting a boolean value for the `%s` parameter: " - "muxer-comp-addr=%p, value-type=%s", - ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME, muxer_comp, - bt_common_value_type_string( - bt_value_get_type(assume_absolute_clock_classes))); - goto error; - } - - bool_val = bt_value_bool_get(assume_absolute_clock_classes); - muxer_comp->assume_absolute_clock_classes = (bool) bool_val; - BT_LOGD("Configured muxer component: muxer-comp-addr=%p, " - "assume-absolute-clock-classes=%d", - muxer_comp, muxer_comp->assume_absolute_clock_classes); - goto end; - -error: - ret = -1; - -end: - bt_value_put_ref(default_params); - bt_value_put_ref(real_params); - return ret; -} - -BT_HIDDEN -bt_self_component_status muxer_init( - bt_self_component_filter *self_comp, - const bt_value *params, void *init_data) -{ - int ret; - bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; - struct muxer_comp *muxer_comp = g_new0(struct muxer_comp, 1); - - BT_LOGD("Initializing muxer component: " - "comp-addr=%p, params-addr=%p", self_comp, params); - - if (!muxer_comp) { - BT_LOGE_STR("Failed to allocate one muxer component."); - goto error; - } - - ret = configure_muxer_comp(muxer_comp, params); - if (ret) { - BT_LOGE("Cannot configure muxer component: " - "muxer-comp-addr=%p, params-addr=%p", - muxer_comp, params); - goto error; - } - - muxer_comp->self_comp = self_comp; - bt_self_component_set_data( - bt_self_component_filter_as_self_component(self_comp), - muxer_comp); - status = add_available_input_port(self_comp); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - BT_LOGE("Cannot ensure that at least one muxer component's input port is available: " - "muxer-comp-addr=%p, status=%s", - muxer_comp, - bt_self_component_status_string(status)); - goto error; - } - - status = create_output_port(self_comp); - if (status) { - BT_LOGE("Cannot create muxer component's output port: " - "muxer-comp-addr=%p, status=%s", - muxer_comp, - bt_self_component_status_string(status)); - goto error; - } - - BT_LOGD("Initialized muxer component: " - "comp-addr=%p, params-addr=%p, muxer-comp-addr=%p", - self_comp, params, muxer_comp); - - goto end; - -error: - destroy_muxer_comp(muxer_comp); - bt_self_component_set_data( - bt_self_component_filter_as_self_component(self_comp), - NULL); - - if (status == BT_SELF_COMPONENT_STATUS_OK) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - } - -end: - return status; -} - -BT_HIDDEN -void muxer_finalize(bt_self_component_filter *self_comp) -{ - struct muxer_comp *muxer_comp = bt_self_component_get_data( - bt_self_component_filter_as_self_component(self_comp)); - - BT_LOGD("Finalizing muxer component: comp-addr=%p", - self_comp); - destroy_muxer_comp(muxer_comp); -} - -static -bt_self_component_port_input_message_iterator * -create_msg_iter_on_input_port(bt_self_component_port_input *self_port) -{ - const bt_port *port = bt_self_component_port_as_port( - bt_self_component_port_input_as_self_component_port( - self_port)); - bt_self_component_port_input_message_iterator *msg_iter = - NULL; - - BT_ASSERT(port); - BT_ASSERT(bt_port_is_connected(port)); - - // TODO: Advance the iterator to >= the time of the latest - // returned message by the muxer message - // iterator which creates it. - msg_iter = bt_self_component_port_input_message_iterator_create( - self_port); - if (!msg_iter) { - BT_LOGE("Cannot create upstream message iterator on input port: " - "port-addr=%p, port-name=\"%s\"", - port, bt_port_get_name(port)); - goto end; - } - - BT_LOGD("Created upstream message iterator on input port: " - "port-addr=%p, port-name=\"%s\", msg-iter-addr=%p", - port, bt_port_get_name(port), msg_iter); - -end: - return msg_iter; -} - -static -bt_self_message_iterator_status muxer_upstream_msg_iter_next( - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter, - bool *is_ended) -{ - bt_self_message_iterator_status status; - bt_message_iterator_status input_port_iter_status; - bt_message_array_const msgs; - uint64_t i; - uint64_t count; - - BT_LOGV("Calling upstream message iterator's \"next\" method: " - "muxer-upstream-msg-iter-wrap-addr=%p, msg-iter-addr=%p", - muxer_upstream_msg_iter, - muxer_upstream_msg_iter->msg_iter); - input_port_iter_status = bt_self_component_port_input_message_iterator_next( - muxer_upstream_msg_iter->msg_iter, &msgs, &count); - BT_LOGV("Upstream message iterator's \"next\" method returned: " - "status=%s", bt_message_iterator_status_string(input_port_iter_status)); - - switch (input_port_iter_status) { - case BT_MESSAGE_ITERATOR_STATUS_OK: - /* - * Message iterator's current message is - * valid: it must be considered for muxing operations. - */ - BT_LOGV_STR("Validated upstream message iterator wrapper."); - BT_ASSERT(count > 0); - - /* Move messages to our queue */ - for (i = 0; i < count; i++) { - /* - * Push to tail in order; other side - * (muxer_msg_iter_do_next_one()) consumes - * from the head first. - */ - g_queue_push_tail(muxer_upstream_msg_iter->msgs, - (void *) msgs[i]); - } - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - break; - case BT_MESSAGE_ITERATOR_STATUS_AGAIN: - /* - * Message iterator's current message is not - * valid anymore. Return - * BT_MESSAGE_ITERATOR_STATUS_AGAIN immediately. - */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; - break; - case BT_MESSAGE_ITERATOR_STATUS_END: /* Fall-through. */ - /* - * Message iterator reached the end: release it. It - * won't be considered again to find the youngest - * message. - */ - *is_ended = true; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - break; - default: - /* Error or unsupported status code */ - BT_LOGE("Error or unsupported status code: " - "status-code=%d", input_port_iter_status); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - break; - } - - return status; -} - -static -int get_msg_ts_ns(struct muxer_comp *muxer_comp, - struct muxer_msg_iter *muxer_msg_iter, - const bt_message *msg, int64_t last_returned_ts_ns, - int64_t *ts_ns) -{ - const bt_clock_snapshot *clock_snapshot = NULL; - int ret = 0; - bt_message_stream_activity_clock_snapshot_state sa_cs_state; - const bt_stream_class *stream_class = NULL; - bt_message_type msg_type; - - BT_ASSERT(msg); - BT_ASSERT(ts_ns); - BT_LOGV("Getting message's timestamp: " - "muxer-msg-iter-addr=%p, msg-addr=%p, " - "last-returned-ts=%" PRId64, - muxer_msg_iter, msg, last_returned_ts_ns); - - if (unlikely(muxer_msg_iter->clock_class_expectation == - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE)) { - *ts_ns = last_returned_ts_ns; - goto end; - } - - msg_type = bt_message_get_type(msg); - - if (unlikely(msg_type == BT_MESSAGE_TYPE_PACKET_BEGINNING)) { - stream_class = bt_stream_borrow_class_const( - bt_packet_borrow_stream_const( - bt_message_packet_beginning_borrow_packet_const( - msg))); - } else if (unlikely(msg_type == BT_MESSAGE_TYPE_PACKET_END)) { - stream_class = bt_stream_borrow_class_const( - bt_packet_borrow_stream_const( - bt_message_packet_end_borrow_packet_const( - msg))); - } else if (unlikely(msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS)) { - stream_class = bt_stream_borrow_class_const( - bt_message_discarded_events_borrow_stream_const(msg)); - } else if (unlikely(msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS)) { - stream_class = bt_stream_borrow_class_const( - bt_message_discarded_packets_borrow_stream_const(msg)); - } - - switch (msg_type) { - case BT_MESSAGE_TYPE_EVENT: - BT_ASSERT(bt_message_event_borrow_stream_class_default_clock_class_const( - msg)); - clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - if (bt_stream_class_packets_have_beginning_default_clock_snapshot( - stream_class)) { - clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( - msg); - } else { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_PACKET_END: - if (bt_stream_class_packets_have_end_default_clock_snapshot( - stream_class)) { - clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( - msg); - } else { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - if (bt_stream_class_discarded_events_have_default_clock_snapshots( - stream_class)) { - clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - } else { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - if (bt_stream_class_discarded_packets_have_default_clock_snapshots( - stream_class)) { - clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - } else { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - BT_ASSERT(bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( - msg)); - sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - BT_ASSERT(bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( - msg)); - sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - clock_snapshot = bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - msg); - break; - default: - /* All the other messages have a higher priority */ - BT_LOGV_STR("Message has no timestamp: using the last returned timestamp."); - *ts_ns = last_returned_ts_ns; - goto end; - } - - ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns); - if (ret) { - BT_LOGE("Cannot get nanoseconds from Epoch of clock snapshot: " - "clock-snapshot-addr=%p", clock_snapshot); - goto error; - } - - goto end; - -no_clock_snapshot: - BT_LOGV_STR("Message's default clock snapshot is missing: " - "using the last returned timestamp."); - *ts_ns = last_returned_ts_ns; - goto end; - -error: - ret = -1; - -end: - if (ret == 0) { - BT_LOGV("Found message's timestamp: " - "muxer-msg-iter-addr=%p, msg-addr=%p, " - "last-returned-ts=%" PRId64 ", ts=%" PRId64, - muxer_msg_iter, msg, last_returned_ts_ns, - *ts_ns); - } - - return ret; -} - -static inline -int validate_clock_class(struct muxer_msg_iter *muxer_msg_iter, - struct muxer_comp *muxer_comp, - const bt_clock_class *clock_class) -{ - int ret = 0; - const unsigned char *cc_uuid; - const char *cc_name; - - BT_ASSERT(clock_class); - cc_uuid = bt_clock_class_get_uuid(clock_class); - cc_name = bt_clock_class_get_name(clock_class); - - if (muxer_msg_iter->clock_class_expectation == - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) { - /* - * This is the first clock class that this muxer - * message iterator encounters. Its properties - * determine what to expect for the whole lifetime of - * the iterator without a true - * `assume-absolute-clock-classes` parameter. - */ - if (bt_clock_class_origin_is_unix_epoch(clock_class)) { - /* Expect absolute clock classes */ - muxer_msg_iter->clock_class_expectation = - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE; - } else { - if (cc_uuid) { - /* - * Expect non-absolute clock classes - * with a specific UUID. - */ - muxer_msg_iter->clock_class_expectation = - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID; - memcpy(muxer_msg_iter->expected_clock_class_uuid, - cc_uuid, BABELTRACE_UUID_LEN); - } else { - /* - * Expect non-absolute clock classes - * with no UUID. - */ - muxer_msg_iter->clock_class_expectation = - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID; - } - } - } - - if (!muxer_comp->assume_absolute_clock_classes) { - switch (muxer_msg_iter->clock_class_expectation) { - case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE: - if (!bt_clock_class_origin_is_unix_epoch(clock_class)) { - BT_LOGE("Expecting an absolute clock class, " - "but got a non-absolute one: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - clock_class, cc_name); - goto error; - } - break; - case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID: - if (bt_clock_class_origin_is_unix_epoch(clock_class)) { - BT_LOGE("Expecting a non-absolute clock class with no UUID, " - "but got an absolute one: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - clock_class, cc_name); - goto error; - } - - if (cc_uuid) { - BT_LOGE("Expecting a non-absolute clock class with no UUID, " - "but got one with a UUID: " - "clock-class-addr=%p, clock-class-name=\"%s\", " - "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - clock_class, cc_name, - (unsigned int) cc_uuid[0], - (unsigned int) cc_uuid[1], - (unsigned int) cc_uuid[2], - (unsigned int) cc_uuid[3], - (unsigned int) cc_uuid[4], - (unsigned int) cc_uuid[5], - (unsigned int) cc_uuid[6], - (unsigned int) cc_uuid[7], - (unsigned int) cc_uuid[8], - (unsigned int) cc_uuid[9], - (unsigned int) cc_uuid[10], - (unsigned int) cc_uuid[11], - (unsigned int) cc_uuid[12], - (unsigned int) cc_uuid[13], - (unsigned int) cc_uuid[14], - (unsigned int) cc_uuid[15]); - goto error; - } - break; - case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID: - if (bt_clock_class_origin_is_unix_epoch(clock_class)) { - BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " - "but got an absolute one: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - clock_class, cc_name); - goto error; - } - - if (!cc_uuid) { - BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " - "but got one with no UUID: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - clock_class, cc_name); - goto error; - } - - if (memcmp(muxer_msg_iter->expected_clock_class_uuid, - cc_uuid, BABELTRACE_UUID_LEN) != 0) { - BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " - "but got one with different UUID: " - "clock-class-addr=%p, clock-class-name=\"%s\", " - "expected-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " - "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", - clock_class, cc_name, - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[0], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[1], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[2], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[3], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[4], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[5], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[6], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[7], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[8], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[9], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[10], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[11], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[12], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[13], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[14], - (unsigned int) muxer_msg_iter->expected_clock_class_uuid[15], - (unsigned int) cc_uuid[0], - (unsigned int) cc_uuid[1], - (unsigned int) cc_uuid[2], - (unsigned int) cc_uuid[3], - (unsigned int) cc_uuid[4], - (unsigned int) cc_uuid[5], - (unsigned int) cc_uuid[6], - (unsigned int) cc_uuid[7], - (unsigned int) cc_uuid[8], - (unsigned int) cc_uuid[9], - (unsigned int) cc_uuid[10], - (unsigned int) cc_uuid[11], - (unsigned int) cc_uuid[12], - (unsigned int) cc_uuid[13], - (unsigned int) cc_uuid[14], - (unsigned int) cc_uuid[15]); - goto error; - } - break; - case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE: - BT_LOGE("Expecting no clock class, but got one: " - "clock-class-addr=%p, clock-class-name=\"%s\"", - clock_class, cc_name); - goto error; - default: - /* Unexpected */ - BT_LOGF("Unexpected clock class expectation: " - "expectation-code=%d", - muxer_msg_iter->clock_class_expectation); - abort(); - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static inline -int validate_new_stream_clock_class(struct muxer_msg_iter *muxer_msg_iter, - struct muxer_comp *muxer_comp, const bt_stream *stream) -{ - int ret = 0; - const bt_stream_class *stream_class = - bt_stream_borrow_class_const(stream); - const bt_clock_class *clock_class = - bt_stream_class_borrow_default_clock_class_const(stream_class); - - if (!clock_class) { - if (muxer_msg_iter->clock_class_expectation == - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) { - /* Expect no clock class */ - muxer_msg_iter->clock_class_expectation = - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE; - } else { - BT_LOGE("Expecting stream class with a default clock class: " - "stream-class-addr=%p, stream-class-name=\"%s\", " - "stream-class-id=%" PRIu64, - stream_class, bt_stream_class_get_name(stream_class), - bt_stream_class_get_id(stream_class)); - ret = -1; - } - - goto end; - } - - ret = validate_clock_class(muxer_msg_iter, muxer_comp, clock_class); - -end: - return ret; -} - -/* - * This function finds the youngest available message amongst the - * non-ended upstream message iterators and returns the upstream - * message iterator which has it, or - * BT_MESSAGE_ITERATOR_STATUS_END if there's no available - * message. - * - * This function does NOT: - * - * * Update any upstream message iterator. - * * Check the upstream message iterators to retry. - * - * On sucess, this function sets *muxer_upstream_msg_iter to the - * upstream message iterator of which the current message is - * the youngest, and sets *ts_ns to its time. - */ -static -bt_self_message_iterator_status -muxer_msg_iter_youngest_upstream_msg_iter( - struct muxer_comp *muxer_comp, - struct muxer_msg_iter *muxer_msg_iter, - struct muxer_upstream_msg_iter **muxer_upstream_msg_iter, - int64_t *ts_ns) -{ - size_t i; - int ret; - int64_t youngest_ts_ns = INT64_MAX; - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(muxer_comp); - BT_ASSERT(muxer_msg_iter); - BT_ASSERT(muxer_upstream_msg_iter); - *muxer_upstream_msg_iter = NULL; - - for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; - i++) { - const bt_message *msg; - struct muxer_upstream_msg_iter *cur_muxer_upstream_msg_iter = - g_ptr_array_index( - muxer_msg_iter->active_muxer_upstream_msg_iters, - i); - int64_t msg_ts_ns; - - if (!cur_muxer_upstream_msg_iter->msg_iter) { - /* This upstream message iterator is ended */ - BT_LOGV("Skipping ended upstream message iterator: " - "muxer-upstream-msg-iter-wrap-addr=%p", - cur_muxer_upstream_msg_iter); - continue; - } - - BT_ASSERT(cur_muxer_upstream_msg_iter->msgs->length > 0); - msg = g_queue_peek_head(cur_muxer_upstream_msg_iter->msgs); - BT_ASSERT(msg); - - if (unlikely(bt_message_get_type(msg) == - BT_MESSAGE_TYPE_STREAM_BEGINNING)) { - ret = validate_new_stream_clock_class( - muxer_msg_iter, muxer_comp, - bt_message_stream_beginning_borrow_stream_const( - msg)); - if (ret) { - /* - * validate_new_stream_clock_class() logs - * errors. - */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - } else if (unlikely(bt_message_get_type(msg) == - BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY)) { - const bt_clock_snapshot *cs; - - cs = bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - msg); - ret = validate_clock_class(muxer_msg_iter, muxer_comp, - bt_clock_snapshot_borrow_clock_class_const(cs)); - if (ret) { - /* validate_clock_class() logs errors */ - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - } - - ret = get_msg_ts_ns(muxer_comp, muxer_msg_iter, msg, - muxer_msg_iter->last_returned_ts_ns, &msg_ts_ns); - if (ret) { - /* get_msg_ts_ns() logs errors */ - *muxer_upstream_msg_iter = NULL; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (msg_ts_ns <= youngest_ts_ns) { - *muxer_upstream_msg_iter = - cur_muxer_upstream_msg_iter; - youngest_ts_ns = msg_ts_ns; - *ts_ns = youngest_ts_ns; - } - } - - if (!*muxer_upstream_msg_iter) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - *ts_ns = INT64_MIN; - } - -end: - return status; -} - -static -bt_self_message_iterator_status validate_muxer_upstream_msg_iter( - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter, - bool *is_ended) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_LOGV("Validating muxer's upstream message iterator wrapper: " - "muxer-upstream-msg-iter-wrap-addr=%p", - muxer_upstream_msg_iter); - - if (muxer_upstream_msg_iter->msgs->length > 0 || - !muxer_upstream_msg_iter->msg_iter) { - BT_LOGV("Already valid or not considered: " - "queue-len=%u, upstream-msg-iter-addr=%p", - muxer_upstream_msg_iter->msgs->length, - muxer_upstream_msg_iter->msg_iter); - goto end; - } - - /* muxer_upstream_msg_iter_next() logs details/errors */ - status = muxer_upstream_msg_iter_next(muxer_upstream_msg_iter, - is_ended); - -end: - return status; -} - -static -bt_self_message_iterator_status validate_muxer_upstream_msg_iters( - struct muxer_msg_iter *muxer_msg_iter) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - size_t i; - - BT_LOGV("Validating muxer's upstream message iterator wrappers: " - "muxer-msg-iter-addr=%p", muxer_msg_iter); - - for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; - i++) { - bool is_ended = false; - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = - g_ptr_array_index( - muxer_msg_iter->active_muxer_upstream_msg_iters, - i); - - status = validate_muxer_upstream_msg_iter( - muxer_upstream_msg_iter, &is_ended); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - if (status < 0) { - BT_LOGE("Cannot validate muxer's upstream message iterator wrapper: " - "muxer-msg-iter-addr=%p, " - "muxer-upstream-msg-iter-wrap-addr=%p", - muxer_msg_iter, - muxer_upstream_msg_iter); - } else { - BT_LOGV("Cannot validate muxer's upstream message iterator wrapper: " - "muxer-msg-iter-addr=%p, " - "muxer-upstream-msg-iter-wrap-addr=%p", - muxer_msg_iter, - muxer_upstream_msg_iter); - } - - goto end; - } - - /* - * Move this muxer upstream message iterator to the - * array of ended iterators if it's ended. - */ - if (unlikely(is_ended)) { - BT_LOGV("Muxer's upstream message iterator wrapper: ended or canceled: " - "muxer-msg-iter-addr=%p, " - "muxer-upstream-msg-iter-wrap-addr=%p", - muxer_msg_iter, muxer_upstream_msg_iter); - g_ptr_array_add( - muxer_msg_iter->ended_muxer_upstream_msg_iters, - muxer_upstream_msg_iter); - muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i] = NULL; - - /* - * Use g_ptr_array_remove_fast() because the - * order of those elements is not important. - */ - g_ptr_array_remove_index_fast( - muxer_msg_iter->active_muxer_upstream_msg_iters, - i); - i--; - } - } - -end: - return status; -} - -static inline -bt_self_message_iterator_status muxer_msg_iter_do_next_one( - struct muxer_comp *muxer_comp, - struct muxer_msg_iter *muxer_msg_iter, - const bt_message **msg) -{ - bt_self_message_iterator_status status; - struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = NULL; - int64_t next_return_ts; - - status = validate_muxer_upstream_msg_iters(muxer_msg_iter); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - /* validate_muxer_upstream_msg_iters() logs details */ - goto end; - } - - /* - * At this point we know that all the existing upstream - * message iterators are valid. We can find the one, - * amongst those, of which the current message is the - * youngest. - */ - status = muxer_msg_iter_youngest_upstream_msg_iter(muxer_comp, - muxer_msg_iter, &muxer_upstream_msg_iter, - &next_return_ts); - if (status < 0 || status == BT_SELF_MESSAGE_ITERATOR_STATUS_END) { - if (status < 0) { - BT_LOGE("Cannot find the youngest upstream message iterator wrapper: " - "status=%s", - bt_common_self_message_iterator_status_string(status)); - } else { - BT_LOGV("Cannot find the youngest upstream message iterator wrapper: " - "status=%s", - bt_common_self_message_iterator_status_string(status)); - } - - goto end; - } - - if (next_return_ts < muxer_msg_iter->last_returned_ts_ns) { - BT_LOGE("Youngest upstream message iterator wrapper's timestamp is less than muxer's message iterator's last returned timestamp: " - "muxer-msg-iter-addr=%p, ts=%" PRId64 ", " - "last-returned-ts=%" PRId64, - muxer_msg_iter, next_return_ts, - muxer_msg_iter->last_returned_ts_ns); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - BT_LOGV("Found youngest upstream message iterator wrapper: " - "muxer-msg-iter-addr=%p, " - "muxer-upstream-msg-iter-wrap-addr=%p, " - "ts=%" PRId64, - muxer_msg_iter, muxer_upstream_msg_iter, next_return_ts); - BT_ASSERT(status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK); - BT_ASSERT(muxer_upstream_msg_iter); - - /* - * Consume from the queue's head: other side - * (muxer_upstream_msg_iter_next()) writes to the tail. - */ - *msg = g_queue_pop_head(muxer_upstream_msg_iter->msgs); - BT_ASSERT(*msg); - muxer_msg_iter->last_returned_ts_ns = next_return_ts; - -end: - return status; -} - -static -bt_self_message_iterator_status muxer_msg_iter_do_next( - struct muxer_comp *muxer_comp, - struct muxer_msg_iter *muxer_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - uint64_t i = 0; - - while (i < capacity && status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - status = muxer_msg_iter_do_next_one(muxer_comp, - muxer_msg_iter, &msgs[i]); - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - i++; - } - } - - if (i > 0) { - /* - * Even if muxer_msg_iter_do_next_one() returned - * something else than - * BT_MESSAGE_ITERATOR_STATUS_OK, we accumulated - * message objects in the output message - * array, so we need to return - * BT_MESSAGE_ITERATOR_STATUS_OK so that they are - * transfered to downstream. This other status occurs - * again the next time muxer_msg_iter_do_next() is - * called, possibly without any accumulated - * message, in which case we'll return it. - */ - *count = i; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - } - - return status; -} - -static -void destroy_muxer_msg_iter(struct muxer_msg_iter *muxer_msg_iter) -{ - if (!muxer_msg_iter) { - return; - } - - BT_LOGD("Destroying muxer component's message iterator: " - "muxer-msg-iter-addr=%p", muxer_msg_iter); - - if (muxer_msg_iter->active_muxer_upstream_msg_iters) { - BT_LOGD_STR("Destroying muxer's active upstream message iterator wrappers."); - g_ptr_array_free( - muxer_msg_iter->active_muxer_upstream_msg_iters, TRUE); - } - - if (muxer_msg_iter->ended_muxer_upstream_msg_iters) { - BT_LOGD_STR("Destroying muxer's ended upstream message iterator wrappers."); - g_ptr_array_free( - muxer_msg_iter->ended_muxer_upstream_msg_iters, TRUE); - } - - g_free(muxer_msg_iter); -} - -static -int muxer_msg_iter_init_upstream_iterators(struct muxer_comp *muxer_comp, - struct muxer_msg_iter *muxer_msg_iter) -{ - int64_t count; - int64_t i; - int ret = 0; - - count = bt_component_filter_get_input_port_count( - bt_self_component_filter_as_component_filter( - muxer_comp->self_comp)); - if (count < 0) { - BT_LOGD("No input port to initialize for muxer component's message iterator: " - "muxer-comp-addr=%p, muxer-msg-iter-addr=%p", - muxer_comp, muxer_msg_iter); - goto end; - } - - for (i = 0; i < count; i++) { - bt_self_component_port_input_message_iterator *upstream_msg_iter; - bt_self_component_port_input *self_port = - bt_self_component_filter_borrow_input_port_by_index( - muxer_comp->self_comp, i); - const bt_port *port; - - BT_ASSERT(self_port); - port = bt_self_component_port_as_port( - bt_self_component_port_input_as_self_component_port( - self_port)); - BT_ASSERT(port); - - if (!bt_port_is_connected(port)) { - /* Skip non-connected port */ - continue; - } - - upstream_msg_iter = create_msg_iter_on_input_port(self_port); - if (!upstream_msg_iter) { - /* create_msg_iter_on_input_port() logs errors */ - BT_ASSERT(!upstream_msg_iter); - ret = -1; - goto end; - } - - ret = muxer_msg_iter_add_upstream_msg_iter(muxer_msg_iter, - upstream_msg_iter); - bt_self_component_port_input_message_iterator_put_ref( - upstream_msg_iter); - if (ret) { - /* muxer_msg_iter_add_upstream_msg_iter() logs errors */ - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *port) -{ - struct muxer_comp *muxer_comp = NULL; - struct muxer_msg_iter *muxer_msg_iter = NULL; - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - int ret; - - muxer_comp = bt_self_component_get_data( - bt_self_component_filter_as_self_component(self_comp)); - BT_ASSERT(muxer_comp); - BT_LOGD("Initializing muxer component's message iterator: " - "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p", - self_comp, muxer_comp, self_msg_iter); - - if (muxer_comp->initializing_muxer_msg_iter) { - /* - * Weird, unhandled situation detected: downstream - * creates a muxer message iterator while creating - * another muxer message iterator (same component). - */ - BT_LOGE("Recursive initialization of muxer component's message iterator: " - "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p", - self_comp, muxer_comp, self_msg_iter); - goto error; - } - - muxer_comp->initializing_muxer_msg_iter = true; - muxer_msg_iter = g_new0(struct muxer_msg_iter, 1); - if (!muxer_msg_iter) { - BT_LOGE_STR("Failed to allocate one muxer component's message iterator."); - goto error; - } - - muxer_msg_iter->last_returned_ts_ns = INT64_MIN; - muxer_msg_iter->active_muxer_upstream_msg_iters = - g_ptr_array_new_with_free_func( - (GDestroyNotify) destroy_muxer_upstream_msg_iter); - if (!muxer_msg_iter->active_muxer_upstream_msg_iters) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - muxer_msg_iter->ended_muxer_upstream_msg_iters = - g_ptr_array_new_with_free_func( - (GDestroyNotify) destroy_muxer_upstream_msg_iter); - if (!muxer_msg_iter->ended_muxer_upstream_msg_iters) { - BT_LOGE_STR("Failed to allocate a GPtrArray."); - goto error; - } - - ret = muxer_msg_iter_init_upstream_iterators(muxer_comp, - muxer_msg_iter); - if (ret) { - BT_LOGE("Cannot initialize connected input ports for muxer component's message iterator: " - "comp-addr=%p, muxer-comp-addr=%p, " - "muxer-msg-iter-addr=%p, msg-iter-addr=%p, ret=%d", - self_comp, muxer_comp, muxer_msg_iter, - self_msg_iter, ret); - goto error; - } - - bt_self_message_iterator_set_data(self_msg_iter, muxer_msg_iter); - BT_LOGD("Initialized muxer component's message iterator: " - "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " - "msg-iter-addr=%p", - self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); - goto end; - -error: - destroy_muxer_msg_iter(muxer_msg_iter); - bt_self_message_iterator_set_data(self_msg_iter, NULL); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - -end: - muxer_comp->initializing_muxer_msg_iter = false; - return status; -} - -BT_HIDDEN -void muxer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) -{ - struct muxer_msg_iter *muxer_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - bt_self_component *self_comp = NULL; - struct muxer_comp *muxer_comp = NULL; - - self_comp = bt_self_message_iterator_borrow_component( - self_msg_iter); - BT_ASSERT(self_comp); - muxer_comp = bt_self_component_get_data(self_comp); - BT_LOGD("Finalizing muxer component's message iterator: " - "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " - "msg-iter-addr=%p", - self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); - - if (muxer_msg_iter) { - destroy_muxer_msg_iter(muxer_msg_iter); - } -} - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status; - struct muxer_msg_iter *muxer_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - bt_self_component *self_comp = NULL; - struct muxer_comp *muxer_comp = NULL; - - BT_ASSERT(muxer_msg_iter); - self_comp = bt_self_message_iterator_borrow_component( - self_msg_iter); - BT_ASSERT(self_comp); - muxer_comp = bt_self_component_get_data(self_comp); - BT_ASSERT(muxer_comp); - BT_LOGV("Muxer component's message iterator's \"next\" method called: " - "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " - "msg-iter-addr=%p", - self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); - - status = muxer_msg_iter_do_next(muxer_comp, muxer_msg_iter, - msgs, capacity, count); - if (status < 0) { - BT_LOGE("Cannot get next message: " - "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " - "msg-iter-addr=%p, status=%s", - self_comp, muxer_comp, muxer_msg_iter, self_msg_iter, - bt_common_self_message_iterator_status_string(status)); - } else { - BT_LOGV("Returning from muxer component's message iterator's \"next\" method: " - "status=%s", - bt_common_self_message_iterator_status_string(status)); - } - - return status; -} - -BT_HIDDEN -bt_self_component_status muxer_input_port_connected( - bt_self_component_filter *self_comp, - bt_self_component_port_input *self_port, - const bt_port_output *other_port) -{ - bt_self_component_status status; - - status = add_available_input_port(self_comp); - if (status) { - /* - * Only way to report an error later since this - * method does not return anything. - */ - BT_LOGE("Cannot add one muxer component's input port: " - "status=%s", - bt_self_component_status_string(status)); - goto end; - } - -end: - return status; -} - -static inline -bt_bool muxer_upstream_msg_iters_can_all_seek_beginning( - GPtrArray *muxer_upstream_msg_iters) -{ - uint64_t i; - bt_bool ret = BT_TRUE; - - for (i = 0; i < muxer_upstream_msg_iters->len; i++) { - struct muxer_upstream_msg_iter *upstream_msg_iter = - muxer_upstream_msg_iters->pdata[i]; - - if (!bt_self_component_port_input_message_iterator_can_seek_beginning( - upstream_msg_iter->msg_iter)) { - ret = BT_FALSE; - goto end; - } - } - -end: - return ret; -} - -BT_HIDDEN -bt_bool muxer_msg_iter_can_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct muxer_msg_iter *muxer_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - bt_bool ret = BT_TRUE; - - if (!muxer_upstream_msg_iters_can_all_seek_beginning( - muxer_msg_iter->active_muxer_upstream_msg_iters)) { - ret = BT_FALSE; - goto end; - } - - if (!muxer_upstream_msg_iters_can_all_seek_beginning( - muxer_msg_iter->ended_muxer_upstream_msg_iters)) { - ret = BT_FALSE; - goto end; - } - -end: - return ret; -} - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_seek_beginning( - bt_self_message_iterator *self_msg_iter) -{ - struct muxer_msg_iter *muxer_msg_iter = - bt_self_message_iterator_get_data(self_msg_iter); - bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; - uint64_t i; - - /* Seek all ended upstream iterators first */ - for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len; - i++) { - struct muxer_upstream_msg_iter *upstream_msg_iter = - muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i]; - - status = bt_self_component_port_input_message_iterator_seek_beginning( - upstream_msg_iter->msg_iter); - if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - empty_message_queue(upstream_msg_iter); - } - - /* Seek all previously active upstream iterators */ - for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; - i++) { - struct muxer_upstream_msg_iter *upstream_msg_iter = - muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i]; - - status = bt_self_component_port_input_message_iterator_seek_beginning( - upstream_msg_iter->msg_iter); - if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - empty_message_queue(upstream_msg_iter); - } - - /* Make them all active */ - for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len; - i++) { - struct muxer_upstream_msg_iter *upstream_msg_iter = - muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i]; - - g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters, - upstream_msg_iter); - muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i] = NULL; - } - - g_ptr_array_remove_range(muxer_msg_iter->ended_muxer_upstream_msg_iters, - 0, muxer_msg_iter->ended_muxer_upstream_msg_iters->len); - muxer_msg_iter->last_returned_ts_ns = INT64_MIN; - muxer_msg_iter->clock_class_expectation = - MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY; - -end: - return (bt_self_message_iterator_status) status; -} diff --git a/plugins/utils/muxer/muxer.h b/plugins/utils/muxer/muxer.h deleted file mode 100644 index 23ec8c20..00000000 --- a/plugins/utils/muxer/muxer.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_H -#define BABELTRACE_PLUGINS_UTILS_MUXER_H - -/* - * Copyright 2016 Jérémie Galarneau - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -BT_HIDDEN -bt_self_component_status muxer_init( - bt_self_component_filter *self_comp, - const bt_value *params, void *init_data); - -BT_HIDDEN -void muxer_finalize(bt_self_component_filter *self_comp); - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *self_port); - -BT_HIDDEN -void muxer_msg_iter_finalize( - bt_self_message_iterator *self_msg_iter); - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -BT_HIDDEN -bt_self_component_status muxer_input_port_connected( - bt_self_component_filter *comp, - bt_self_component_port_input *self_port, - const bt_port_output *other_port); - -BT_HIDDEN -bt_bool muxer_msg_iter_can_seek_beginning( - bt_self_message_iterator *message_iterator); - -BT_HIDDEN -bt_self_message_iterator_status muxer_msg_iter_seek_beginning( - bt_self_message_iterator *message_iterator); - -#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_H */ diff --git a/plugins/utils/plugin.c b/plugins/utils/plugin.c deleted file mode 100644 index d6b5b244..00000000 --- a/plugins/utils/plugin.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include "dummy/dummy.h" -#include "counter/counter.h" -#include "muxer/muxer.h" -#include "trimmer/trimmer.h" - -#ifndef BT_BUILT_IN_PLUGINS -BT_PLUGIN_MODULE(); -#endif - -BT_PLUGIN(utils); -BT_PLUGIN_DESCRIPTION("Graph utilities"); -BT_PLUGIN_AUTHOR("Julien Desfossez, Jérémie Galarneau, Philippe Proulx"); -BT_PLUGIN_LICENSE("MIT"); - -/* sink.utils.dummy */ -BT_PLUGIN_SINK_COMPONENT_CLASS(dummy, dummy_consume); -BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(dummy, dummy_init); -BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(dummy, dummy_finalize); -BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(dummy, - dummy_graph_is_configured); -BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(dummy, - "Consume messages and discard them."); - -/* sink.utils.counter */ -BT_PLUGIN_SINK_COMPONENT_CLASS(counter, counter_consume); -BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(counter, counter_init); -BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(counter, counter_finalize); -BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(counter, - counter_graph_is_configured); -BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(counter, - "Count messages and print the results."); - -/* flt.utils.trimmer */ -BT_PLUGIN_FILTER_COMPONENT_CLASS(trimmer, trimmer_msg_iter_next); -BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(trimmer, - "Keep messages that occur within a specific time range."); -BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD(trimmer, trimmer_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(trimmer, trimmer_finalize); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(trimmer, - trimmer_msg_iter_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(trimmer, - trimmer_msg_iter_finalize); - -/* flt.utils.muxer */ -BT_PLUGIN_FILTER_COMPONENT_CLASS(muxer, muxer_msg_iter_next); -BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(muxer, - "Sort messages from multiple input ports to a single output port by time."); -BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD(muxer, muxer_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(muxer, muxer_finalize); -BT_PLUGIN_FILTER_COMPONENT_CLASS_INPUT_PORT_CONNECTED_METHOD(muxer, - muxer_input_port_connected); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(muxer, - muxer_msg_iter_init); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(muxer, - muxer_msg_iter_finalize); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(muxer, - muxer_msg_iter_seek_beginning); -BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD(muxer, - muxer_msg_iter_can_seek_beginning); diff --git a/plugins/utils/trimmer/Makefile.am b/plugins/utils/trimmer/Makefile.am deleted file mode 100644 index 050db0c4..00000000 --- a/plugins/utils/trimmer/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -AM_CPPFLAGS += -I$(top_srcdir)/plugins \ - -I$(top_srcdir)/plugins/libctfcopytrace - -noinst_LTLIBRARIES = libbabeltrace2-plugin-trimmer.la -libbabeltrace2_plugin_trimmer_la_SOURCES = \ - trimmer.c \ - trimmer.h \ - logging.c \ - logging.h diff --git a/plugins/utils/trimmer/logging.c b/plugins/utils/trimmer/logging.c deleted file mode 100644 index f8b9be15..00000000 --- a/plugins/utils/trimmer/logging.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_trimmer_log_level -#include - -BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_trimmer_log_level, - "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL"); diff --git a/plugins/utils/trimmer/logging.h b/plugins/utils/trimmer/logging.h deleted file mode 100644 index 3bcd7ea8..00000000 --- a/plugins/utils/trimmer/logging.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLUGINS_UTILS_TRIMMER_LOGGING_H -#define PLUGINS_UTILS_TRIMMER_LOGGING_H - -/* - * Copyright (c) 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_trimmer_log_level -#include - -BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_trimmer_log_level); - -#endif /* PLUGINS_UTILS_TRIMMER_LOGGING_H */ diff --git a/plugins/utils/trimmer/trimmer.c b/plugins/utils/trimmer/trimmer.c deleted file mode 100644 index 1227dc50..00000000 --- a/plugins/utils/trimmer/trimmer.c +++ /dev/null @@ -1,2049 +0,0 @@ -/* - * Copyright 2016 Jérémie Galarneau - * Copyright 2019 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-UTILS-TRIMMER-FLT" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "trimmer.h" - -#define NS_PER_S INT64_C(1000000000) - -static const char * const in_port_name = "in"; - -struct trimmer_time { - unsigned int hour, minute, second, ns; -}; - -struct trimmer_bound { - /* - * Nanoseconds from origin, valid if `is_set` is set and - * `is_infinite` is false. - */ - int64_t ns_from_origin; - - /* True if this bound's full time (`ns_from_origin`) is set */ - bool is_set; - - /* - * True if this bound represents the infinity (negative or - * positive depending on which bound it is). If this is true, - * then we don't care about `ns_from_origin` above. - */ - bool is_infinite; - - /* - * This bound's time without the date; this time is used to set - * `ns_from_origin` once we know the date. - */ - struct trimmer_time time; -}; - -struct trimmer_comp { - struct trimmer_bound begin, end; - bool is_gmt; -}; - -enum trimmer_iterator_state { - /* - * Find the first message's date and set the bounds's times - * accordingly. - */ - TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN, - - /* - * Initially seek to the trimming range's beginning time. - */ - TRIMMER_ITERATOR_STATE_SEEK_INITIALLY, - - /* - * Fill the output message queue for as long as received input - * messages are within the trimming time range. - */ - TRIMMER_ITERATOR_STATE_TRIM, - - /* Flush the remaining messages in the output message queue */ - TRIMMER_ITERATOR_STATE_ENDING, - - /* Trimming operation and message iterator is ended */ - TRIMMER_ITERATOR_STATE_ENDED, -}; - -struct trimmer_iterator { - /* Weak */ - struct trimmer_comp *trimmer_comp; - - /* Weak */ - bt_self_message_iterator *self_msg_iter; - - enum trimmer_iterator_state state; - - /* Owned by this */ - bt_self_component_port_input_message_iterator *upstream_iter; - struct trimmer_bound begin, end; - - /* - * Queue of `const bt_message *` (owned by the queue). - * - * This is where the trimming operation pushes the messages to - * output by this message iterator. - */ - GQueue *output_messages; - - /* - * Hash table of `bt_stream *` (weak) to - * `struct trimmer_iterator_stream_state *` (owned by the HT). - */ - GHashTable *stream_states; -}; - -struct trimmer_iterator_stream_state { - /* - * True if both stream beginning and initial stream activity - * beginning messages were pushed for this stream. - */ - bool inited; - - /* - * True if the last pushed message for this stream was a stream - * activity end message. - */ - bool last_msg_is_stream_activity_end; - - /* - * Time to use for a generated stream end activity message when - * ending the stream. - */ - int64_t stream_act_end_ns_from_origin; - - /* Weak */ - const bt_stream *stream; - - /* Owned by this (`NULL` initially and between packets) */ - const bt_packet *cur_packet; - - /* Owned by this */ - const bt_message *stream_beginning_msg; -}; - -static -void destroy_trimmer_comp(struct trimmer_comp *trimmer_comp) -{ - BT_ASSERT(trimmer_comp); - g_free(trimmer_comp); -} - -static -struct trimmer_comp *create_trimmer_comp(void) -{ - return g_new0(struct trimmer_comp, 1); -} - -BT_HIDDEN -void trimmer_finalize(bt_self_component_filter *self_comp) -{ - struct trimmer_comp *trimmer_comp = - bt_self_component_get_data( - bt_self_component_filter_as_self_component(self_comp)); - - if (trimmer_comp) { - destroy_trimmer_comp(trimmer_comp); - } -} - -/* - * Sets the time (in ns from origin) of a trimmer bound from date and - * time components. - * - * Returns a negative value if anything goes wrong. - */ -static -int set_bound_ns_from_origin(struct trimmer_bound *bound, - unsigned int year, unsigned int month, unsigned int day, - unsigned int hour, unsigned int minute, unsigned int second, - unsigned int ns, bool is_gmt) -{ - int ret = 0; - time_t result; - struct tm tm = { - .tm_sec = second, - .tm_min = minute, - .tm_hour = hour, - .tm_mday = day, - .tm_mon = month - 1, - .tm_year = year - 1900, - .tm_isdst = -1, - }; - - if (is_gmt) { - result = bt_timegm(&tm); - } else { - result = mktime(&tm); - } - - if (result < 0) { - ret = -1; - goto end; - } - - BT_ASSERT(bound); - bound->ns_from_origin = (int64_t) result; - bound->ns_from_origin *= NS_PER_S; - bound->ns_from_origin += ns; - bound->is_set = true; - -end: - return ret; -} - -/* - * Parses a timestamp, figuring out its format. - * - * Returns a negative value if anything goes wrong. - * - * Expected formats: - * - * YYYY-MM-DD hh:mm[:ss[.ns]] - * [hh:mm:]ss[.ns] - * [-]s[.ns] - * - * TODO: Check overflows. - */ -static -int set_bound_from_str(const char *str, struct trimmer_bound *bound, - bool is_gmt) -{ - int ret = 0; - int s_ret; - unsigned int year, month, day, hour, minute, second, ns; - char dummy; - - /* Try `YYYY-MM-DD hh:mm:ss.ns` format */ - s_ret = sscanf(str, "%u-%u-%u %u:%u:%u.%u%c", &year, &month, &day, - &hour, &minute, &second, &ns, &dummy); - if (s_ret == 7) { - ret = set_bound_ns_from_origin(bound, year, month, day, - hour, minute, second, ns, is_gmt); - goto end; - } - - /* Try `YYYY-MM-DD hh:mm:ss` format */ - s_ret = sscanf(str, "%u-%u-%u %u:%u:%u%c", &year, &month, &day, - &hour, &minute, &second, &dummy); - if (s_ret == 6) { - ret = set_bound_ns_from_origin(bound, year, month, day, - hour, minute, second, 0, is_gmt); - goto end; - } - - /* Try `YYYY-MM-DD hh:mm` format */ - s_ret = sscanf(str, "%u-%u-%u %u:%u%c", &year, &month, &day, - &hour, &minute, &dummy); - if (s_ret == 5) { - ret = set_bound_ns_from_origin(bound, year, month, day, - hour, minute, 0, 0, is_gmt); - goto end; - } - - /* Try `YYYY-MM-DD` format */ - s_ret = sscanf(str, "%u-%u-%u%c", &year, &month, &day, &dummy); - if (s_ret == 3) { - ret = set_bound_ns_from_origin(bound, year, month, day, - 0, 0, 0, 0, is_gmt); - goto end; - } - - /* Try `hh:mm:ss.ns` format */ - s_ret = sscanf(str, "%u:%u:%u.%u%c", &hour, &minute, &second, &ns, - &dummy); - if (s_ret == 4) { - bound->time.hour = hour; - bound->time.minute = minute; - bound->time.second = second; - bound->time.ns = ns; - goto end; - } - - /* Try `hh:mm:ss` format */ - s_ret = sscanf(str, "%u:%u:%u%c", &hour, &minute, &second, &dummy); - if (s_ret == 3) { - bound->time.hour = hour; - bound->time.minute = minute; - bound->time.second = second; - bound->time.ns = 0; - goto end; - } - - /* Try `-s.ns` format */ - s_ret = sscanf(str, "-%u.%u%c", &second, &ns, &dummy); - if (s_ret == 2) { - bound->ns_from_origin = -((int64_t) second) * NS_PER_S; - bound->ns_from_origin -= (int64_t) ns; - bound->is_set = true; - goto end; - } - - /* Try `s.ns` format */ - s_ret = sscanf(str, "%u.%u%c", &second, &ns, &dummy); - if (s_ret == 2) { - bound->ns_from_origin = ((int64_t) second) * NS_PER_S; - bound->ns_from_origin += (int64_t) ns; - bound->is_set = true; - goto end; - } - - /* Try `-s` format */ - s_ret = sscanf(str, "-%u%c", &second, &dummy); - if (s_ret == 1) { - bound->ns_from_origin = -((int64_t) second) * NS_PER_S; - bound->is_set = true; - goto end; - } - - /* Try `s` format */ - s_ret = sscanf(str, "%u%c", &second, &dummy); - if (s_ret == 1) { - bound->ns_from_origin = (int64_t) second * NS_PER_S; - bound->is_set = true; - goto end; - } - - BT_LOGE("Invalid date/time format: param=\"%s\"", str); - ret = -1; - -end: - return ret; -} - -/* - * Sets a trimmer bound's properties from a parameter string/integer - * value. - * - * Returns a negative value if anything goes wrong. - */ -static -int set_bound_from_param(const char *param_name, const bt_value *param, - struct trimmer_bound *bound, bool is_gmt) -{ - int ret; - const char *arg; - char tmp_arg[64]; - - if (bt_value_is_signed_integer(param)) { - int64_t value = bt_value_signed_integer_get(param); - - /* - * Just convert it to a temporary string to handle - * everything the same way. - */ - sprintf(tmp_arg, "%" PRId64, value); - arg = tmp_arg; - } else if (bt_value_is_string(param)) { - arg = bt_value_string_get(param); - } else { - BT_LOGE("`%s` parameter must be an integer or a string value.", - param_name); - ret = -1; - goto end; - } - - ret = set_bound_from_str(arg, bound, is_gmt); - -end: - return ret; -} - -static -int validate_trimmer_bounds(struct trimmer_bound *begin, - struct trimmer_bound *end) -{ - int ret = 0; - - BT_ASSERT(begin->is_set); - BT_ASSERT(end->is_set); - - if (!begin->is_infinite && !end->is_infinite && - begin->ns_from_origin > end->ns_from_origin) { - BT_LOGE("Trimming time range's beginning time is greater than end time: " - "begin-ns-from-origin=%" PRId64 ", " - "end-ns-from-origin=%" PRId64, - begin->ns_from_origin, - end->ns_from_origin); - ret = -1; - goto end; - } - - if (!begin->is_infinite && begin->ns_from_origin == INT64_MIN) { - BT_LOGE("Invalid trimming time range's beginning time: " - "ns-from-origin=%" PRId64, - begin->ns_from_origin); - ret = -1; - goto end; - } - - if (!end->is_infinite && end->ns_from_origin == INT64_MIN) { - BT_LOGE("Invalid trimming time range's end time: " - "ns-from-origin=%" PRId64, - end->ns_from_origin); - ret = -1; - goto end; - } - -end: - return ret; -} - -static -int init_trimmer_comp_from_params(struct trimmer_comp *trimmer_comp, - const bt_value *params) -{ - const bt_value *value; - int ret = 0; - - BT_ASSERT(params); - value = bt_value_map_borrow_entry_value_const(params, "gmt"); - if (value) { - trimmer_comp->is_gmt = (bool) bt_value_bool_get(value); - } - - value = bt_value_map_borrow_entry_value_const(params, "begin"); - if (value) { - if (set_bound_from_param("begin", value, - &trimmer_comp->begin, trimmer_comp->is_gmt)) { - /* set_bound_from_param() logs errors */ - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - } else { - trimmer_comp->begin.is_infinite = true; - trimmer_comp->begin.is_set = true; - } - - value = bt_value_map_borrow_entry_value_const(params, "end"); - if (value) { - if (set_bound_from_param("end", value, - &trimmer_comp->end, trimmer_comp->is_gmt)) { - /* set_bound_from_param() logs errors */ - ret = BT_SELF_COMPONENT_STATUS_ERROR; - goto end; - } - } else { - trimmer_comp->end.is_infinite = true; - trimmer_comp->end.is_set = true; - } - -end: - if (trimmer_comp->begin.is_set && trimmer_comp->end.is_set) { - /* validate_trimmer_bounds() logs errors */ - ret = validate_trimmer_bounds(&trimmer_comp->begin, - &trimmer_comp->end); - } - - return ret; -} - -bt_self_component_status trimmer_init(bt_self_component_filter *self_comp, - const bt_value *params, void *init_data) -{ - int ret; - bt_self_component_status status; - struct trimmer_comp *trimmer_comp = create_trimmer_comp(); - - if (!trimmer_comp) { - status = BT_SELF_COMPONENT_STATUS_NOMEM; - goto error; - } - - status = bt_self_component_filter_add_input_port( - self_comp, in_port_name, NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - status = bt_self_component_filter_add_output_port( - self_comp, "out", NULL, NULL); - if (status != BT_SELF_COMPONENT_STATUS_OK) { - goto error; - } - - ret = init_trimmer_comp_from_params(trimmer_comp, params); - if (ret) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - goto error; - } - - bt_self_component_set_data( - bt_self_component_filter_as_self_component(self_comp), - trimmer_comp); - goto end; - -error: - if (status == BT_SELF_COMPONENT_STATUS_OK) { - status = BT_SELF_COMPONENT_STATUS_ERROR; - } - - if (trimmer_comp) { - destroy_trimmer_comp(trimmer_comp); - } - -end: - return status; -} - -static -void destroy_trimmer_iterator(struct trimmer_iterator *trimmer_it) -{ - BT_ASSERT(trimmer_it); - bt_self_component_port_input_message_iterator_put_ref( - trimmer_it->upstream_iter); - - if (trimmer_it->output_messages) { - g_queue_free(trimmer_it->output_messages); - } - - if (trimmer_it->stream_states) { - g_hash_table_destroy(trimmer_it->stream_states); - } - - g_free(trimmer_it); -} - -static -void destroy_trimmer_iterator_stream_state( - struct trimmer_iterator_stream_state *sstate) -{ - BT_ASSERT(sstate); - BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); - BT_MESSAGE_PUT_REF_AND_RESET(sstate->stream_beginning_msg); - g_free(sstate); -} - -BT_HIDDEN -bt_self_message_iterator_status trimmer_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *port) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - struct trimmer_iterator *trimmer_it; - - trimmer_it = g_new0(struct trimmer_iterator, 1); - if (!trimmer_it) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - trimmer_it->trimmer_comp = bt_self_component_get_data( - bt_self_component_filter_as_self_component(self_comp)); - BT_ASSERT(trimmer_it->trimmer_comp); - - if (trimmer_it->trimmer_comp->begin.is_set && - trimmer_it->trimmer_comp->end.is_set) { - /* - * Both trimming time range's bounds are set, so skip - * the - * `TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN` - * phase. - */ - trimmer_it->state = TRIMMER_ITERATOR_STATE_SEEK_INITIALLY; - } - - trimmer_it->begin = trimmer_it->trimmer_comp->begin; - trimmer_it->end = trimmer_it->trimmer_comp->end; - trimmer_it->upstream_iter = - bt_self_component_port_input_message_iterator_create( - bt_self_component_filter_borrow_input_port_by_name( - self_comp, in_port_name)); - if (!trimmer_it->upstream_iter) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - trimmer_it->output_messages = g_queue_new(); - if (!trimmer_it->output_messages) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - trimmer_it->stream_states = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, - (GDestroyNotify) destroy_trimmer_iterator_stream_state); - if (!trimmer_it->stream_states) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - trimmer_it->self_msg_iter = self_msg_iter; - bt_self_message_iterator_set_data(self_msg_iter, trimmer_it); - -end: - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK && trimmer_it) { - destroy_trimmer_iterator(trimmer_it); - } - - return status; -} - -static inline -int get_msg_ns_from_origin(const bt_message *msg, int64_t *ns_from_origin, - bool *skip) -{ - const bt_clock_class *clock_class = NULL; - const bt_clock_snapshot *clock_snapshot = NULL; - bt_message_stream_activity_clock_snapshot_state sa_cs_state; - int ret = 0; - - BT_ASSERT(msg); - BT_ASSERT(ns_from_origin); - BT_ASSERT(skip); - - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_EVENT: - clock_class = - bt_message_event_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - clock_class = - bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_PACKET_END: - clock_class = - bt_message_packet_end_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - clock_class = - bt_message_discarded_events_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - clock_class = - bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - clock_class = - bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN || - sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE) { - /* Lowest possible time to always include them */ - *ns_from_origin = INT64_MIN; - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - clock_class = - bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( - msg); - if (unlikely(!clock_class)) { - goto error; - } - - sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( - msg, &clock_snapshot); - if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN) { - /* Lowest time to always include it */ - *ns_from_origin = INT64_MIN; - goto no_clock_snapshot; - } else if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE) { - /* Greatest time to always exclude it */ - *ns_from_origin = INT64_MAX; - goto no_clock_snapshot; - } - - break; - case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: - clock_snapshot = - bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( - msg); - break; - default: - goto no_clock_snapshot; - } - - ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, - ns_from_origin); - if (unlikely(ret)) { - goto error; - } - - goto end; - -no_clock_snapshot: - *skip = true; - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static inline -void put_messages(bt_message_array_const msgs, uint64_t count) -{ - uint64_t i; - - for (i = 0; i < count; i++) { - BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); - } -} - -static inline -int set_trimmer_iterator_bound(struct trimmer_bound *bound, - int64_t ns_from_origin, bool is_gmt) -{ - struct tm tm; - time_t time_seconds = (time_t) (ns_from_origin / NS_PER_S); - int ret = 0; - - BT_ASSERT(!bound->is_set); - errno = 0; - - /* We only need to extract the date from this time */ - if (is_gmt) { - bt_gmtime_r(&time_seconds, &tm); - } else { - bt_localtime_r(&time_seconds, &tm); - } - - if (errno) { - BT_LOGE_ERRNO("Cannot convert timestamp to date and time", - "ts=%" PRId64, (int64_t) time_seconds); - ret = -1; - goto end; - } - - ret = set_bound_ns_from_origin(bound, tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, bound->time.hour, bound->time.minute, - bound->time.second, bound->time.ns, is_gmt); - -end: - return ret; -} - -static -bt_self_message_iterator_status state_set_trimmer_iterator_bounds( - struct trimmer_iterator *trimmer_it) -{ - bt_message_iterator_status upstream_iter_status = - BT_MESSAGE_ITERATOR_STATUS_OK; - struct trimmer_comp *trimmer_comp = trimmer_it->trimmer_comp; - bt_message_array_const msgs; - uint64_t count = 0; - int64_t ns_from_origin = INT64_MIN; - uint64_t i; - int ret; - - BT_ASSERT(!trimmer_it->begin.is_set || - !trimmer_it->end.is_set); - - while (true) { - upstream_iter_status = - bt_self_component_port_input_message_iterator_next( - trimmer_it->upstream_iter, &msgs, &count); - if (upstream_iter_status != BT_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - for (i = 0; i < count; i++) { - const bt_message *msg = msgs[i]; - bool skip = false; - int ret; - - ret = get_msg_ns_from_origin(msg, &ns_from_origin, - &skip); - if (ret) { - goto error; - } - - if (skip) { - continue; - } - - BT_ASSERT(ns_from_origin != INT64_MIN && - ns_from_origin != INT64_MAX); - put_messages(msgs, count); - goto found; - } - - put_messages(msgs, count); - } - -found: - if (!trimmer_it->begin.is_set) { - BT_ASSERT(!trimmer_it->begin.is_infinite); - ret = set_trimmer_iterator_bound(&trimmer_it->begin, - ns_from_origin, trimmer_comp->is_gmt); - if (ret) { - goto error; - } - } - - if (!trimmer_it->end.is_set) { - BT_ASSERT(!trimmer_it->end.is_infinite); - ret = set_trimmer_iterator_bound(&trimmer_it->end, - ns_from_origin, trimmer_comp->is_gmt); - if (ret) { - goto error; - } - } - - ret = validate_trimmer_bounds(&trimmer_it->begin, - &trimmer_it->end); - if (ret) { - goto error; - } - - goto end; - -error: - put_messages(msgs, count); - upstream_iter_status = BT_MESSAGE_ITERATOR_STATUS_ERROR; - -end: - return (int) upstream_iter_status; -} - -static -bt_self_message_iterator_status state_seek_initially( - struct trimmer_iterator *trimmer_it) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(trimmer_it->begin.is_set); - - if (trimmer_it->begin.is_infinite) { - if (!bt_self_component_port_input_message_iterator_can_seek_beginning( - trimmer_it->upstream_iter)) { - BT_LOGE_STR("Cannot make upstream message iterator initially seek its beginning."); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - status = (int) bt_self_component_port_input_message_iterator_seek_beginning( - trimmer_it->upstream_iter); - } else { - if (!bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( - trimmer_it->upstream_iter, - trimmer_it->begin.ns_from_origin)) { - BT_LOGE("Cannot make upstream message iterator initially seek: " - "seek-ns-from-origin=%" PRId64, - trimmer_it->begin.ns_from_origin); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - status = (int) bt_self_component_port_input_message_iterator_seek_ns_from_origin( - trimmer_it->upstream_iter, trimmer_it->begin.ns_from_origin); - } - - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - trimmer_it->state = TRIMMER_ITERATOR_STATE_TRIM; - } - -end: - return status; -} - -static inline -void push_message(struct trimmer_iterator *trimmer_it, const bt_message *msg) -{ - g_queue_push_head(trimmer_it->output_messages, (void *) msg); -} - -static inline -const bt_message *pop_message(struct trimmer_iterator *trimmer_it) -{ - return g_queue_pop_tail(trimmer_it->output_messages); -} - -static inline -int clock_raw_value_from_ns_from_origin(const bt_clock_class *clock_class, - int64_t ns_from_origin, uint64_t *raw_value) -{ - - int64_t cc_offset_s; - uint64_t cc_offset_cycles; - uint64_t cc_freq; - - bt_clock_class_get_offset(clock_class, &cc_offset_s, &cc_offset_cycles); - cc_freq = bt_clock_class_get_frequency(clock_class); - return bt_common_clock_value_from_ns_from_origin(cc_offset_s, - cc_offset_cycles, cc_freq, ns_from_origin, raw_value); -} - -static inline -bt_self_message_iterator_status end_stream(struct trimmer_iterator *trimmer_it, - struct trimmer_iterator_stream_state *sstate) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - uint64_t raw_value; - const bt_clock_class *clock_class; - int ret; - bt_message *msg = NULL; - - BT_ASSERT(!trimmer_it->end.is_infinite); - - if (!sstate->stream) { - goto end; - } - - if (sstate->cur_packet) { - /* - * The last message could not have been a stream - * activity end message if we have a current packet. - */ - BT_ASSERT(!sstate->last_msg_is_stream_activity_end); - - /* - * Create and push a packet end message, making its time - * the trimming range's end time. - */ - clock_class = bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(sstate->stream)); - BT_ASSERT(clock_class); - ret = clock_raw_value_from_ns_from_origin(clock_class, - trimmer_it->end.ns_from_origin, &raw_value); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - msg = bt_message_packet_end_create_with_default_clock_snapshot( - trimmer_it->self_msg_iter, sstate->cur_packet, - raw_value); - if (!msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - push_message(trimmer_it, msg); - msg = NULL; - BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); - - /* - * Because we generated a packet end message, set the - * stream activity end message's time to use to the - * trimming range's end time (this packet end message's - * time). - */ - sstate->stream_act_end_ns_from_origin = - trimmer_it->end.ns_from_origin; - } - - if (!sstate->last_msg_is_stream_activity_end) { - /* Create and push a stream activity end message */ - msg = bt_message_stream_activity_end_create( - trimmer_it->self_msg_iter, sstate->stream); - if (!msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - clock_class = bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(sstate->stream)); - BT_ASSERT(clock_class); - - if (sstate->stream_act_end_ns_from_origin == INT64_MIN) { - /* - * We received at least what is necessary to - * have a stream state (stream beginning and - * stream activity beginning messages), but - * nothing else: use the trimmer range's end - * time. - */ - sstate->stream_act_end_ns_from_origin = - trimmer_it->end.ns_from_origin; - } - - ret = clock_raw_value_from_ns_from_origin(clock_class, - sstate->stream_act_end_ns_from_origin, &raw_value); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - bt_message_stream_activity_end_set_default_clock_snapshot( - msg, raw_value); - push_message(trimmer_it, msg); - msg = NULL; - } - - /* Create and push a stream end message */ - msg = bt_message_stream_end_create(trimmer_it->self_msg_iter, - sstate->stream); - if (!msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - push_message(trimmer_it, msg); - msg = NULL; - - /* - * Just to make sure that we don't use this stream state again - * in the future without an obvious error. - */ - sstate->stream = NULL; - -end: - bt_message_put_ref(msg); - return status; -} - -static inline -bt_self_message_iterator_status end_iterator_streams( - struct trimmer_iterator *trimmer_it) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - GHashTableIter iter; - gpointer key, sstate; - - if (trimmer_it->end.is_infinite) { - /* - * An infinite trimming range's end time guarantees that - * we received (and pushed) all the appropriate end - * messages. - */ - goto remove_all; - } - - /* - * End each stream and then remove them from the hash table of - * stream states to release unneeded references. - */ - g_hash_table_iter_init(&iter, trimmer_it->stream_states); - - while (g_hash_table_iter_next(&iter, &key, &sstate)) { - status = end_stream(trimmer_it, sstate); - if (status) { - goto end; - } - } - -remove_all: - g_hash_table_remove_all(trimmer_it->stream_states); - -end: - return status; -} - -static inline -bt_self_message_iterator_status create_stream_beginning_activity_message( - struct trimmer_iterator *trimmer_it, - const bt_stream *stream, - const bt_clock_class *clock_class, bt_message **msg) -{ - bt_message *local_msg; - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(msg); - BT_ASSERT(!trimmer_it->begin.is_infinite); - - local_msg = bt_message_stream_activity_beginning_create( - trimmer_it->self_msg_iter, stream); - if (!local_msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - if (clock_class) { - int ret; - uint64_t raw_value; - - ret = clock_raw_value_from_ns_from_origin(clock_class, - trimmer_it->begin.ns_from_origin, &raw_value); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - bt_message_put_ref(local_msg); - goto end; - } - - bt_message_stream_activity_beginning_set_default_clock_snapshot( - local_msg, raw_value); - } - - BT_MESSAGE_MOVE_REF(*msg, local_msg); - -end: - return status; -} - -/* - * Makes sure to initialize a stream state, pushing the appropriate - * initial messages. - * - * `stream_act_beginning_msg` is an initial stream activity beginning - * message to potentially use, depending on its clock snapshot state. - * This function consumes `stream_act_beginning_msg` unconditionally. - */ -static inline -bt_self_message_iterator_status ensure_stream_state_is_inited( - struct trimmer_iterator *trimmer_it, - struct trimmer_iterator_stream_state *sstate, - const bt_message *stream_act_beginning_msg) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - bt_message *new_msg = NULL; - const bt_clock_class *clock_class = - bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(sstate->stream)); - - BT_ASSERT(!sstate->inited); - - if (!sstate->stream_beginning_msg) { - /* No initial stream beginning message: create one */ - sstate->stream_beginning_msg = - bt_message_stream_beginning_create( - trimmer_it->self_msg_iter, sstate->stream); - if (!sstate->stream_beginning_msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - } - - /* Push initial stream beginning message */ - BT_ASSERT(sstate->stream_beginning_msg); - push_message(trimmer_it, sstate->stream_beginning_msg); - sstate->stream_beginning_msg = NULL; - - if (stream_act_beginning_msg) { - /* - * Initial stream activity beginning message exists: if - * its time is -inf, then create and push a new one - * having the trimming range's beginning time. Otherwise - * push it as is (known and unknown). - */ - const bt_clock_snapshot *cs; - bt_message_stream_activity_clock_snapshot_state sa_cs_state; - - sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( - stream_act_beginning_msg, &cs); - if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE && - !trimmer_it->begin.is_infinite) { - /* - * -inf time: use trimming range's beginning - * time (which is not -inf). - */ - status = create_stream_beginning_activity_message( - trimmer_it, sstate->stream, clock_class, - &new_msg); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - push_message(trimmer_it, new_msg); - new_msg = NULL; - } else { - /* Known/unknown: push as is */ - push_message(trimmer_it, stream_act_beginning_msg); - stream_act_beginning_msg = NULL; - } - } else { - BT_ASSERT(!trimmer_it->begin.is_infinite); - - /* - * No stream beginning activity message: create and push - * a new message. - */ - status = create_stream_beginning_activity_message( - trimmer_it, sstate->stream, clock_class, &new_msg); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - push_message(trimmer_it, new_msg); - new_msg = NULL; - } - - sstate->inited = true; - -end: - bt_message_put_ref(new_msg); - bt_message_put_ref(stream_act_beginning_msg); - return status; -} - -static inline -bt_self_message_iterator_status ensure_cur_packet_exists( - struct trimmer_iterator *trimmer_it, - struct trimmer_iterator_stream_state *sstate, - const bt_packet *packet) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - int ret; - const bt_clock_class *clock_class = - bt_stream_class_borrow_default_clock_class_const( - bt_stream_borrow_class_const(sstate->stream)); - bt_message *msg = NULL; - uint64_t raw_value; - - BT_ASSERT(!trimmer_it->begin.is_infinite); - BT_ASSERT(!sstate->cur_packet); - - /* - * Create and push an initial packet beginning message, - * making its time the trimming range's beginning time. - */ - ret = clock_raw_value_from_ns_from_origin(clock_class, - trimmer_it->begin.ns_from_origin, &raw_value); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - msg = bt_message_packet_beginning_create_with_default_clock_snapshot( - trimmer_it->self_msg_iter, packet, raw_value); - if (!msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - push_message(trimmer_it, msg); - msg = NULL; - - /* Set packet as this stream's current packet */ - sstate->cur_packet = packet; - bt_packet_get_ref(sstate->cur_packet); - -end: - bt_message_put_ref(msg); - return status; -} - -/* - * Handles a message which is associated to a given stream state. This - * _could_ make the iterator's output message queue grow; this could - * also consume the message without pushing anything to this queue, only - * modifying the stream state. - * - * This function consumes the `msg` reference, _whatever the outcome_. - * - * `ns_from_origin` is the message's time, as given by - * get_msg_ns_from_origin(). - * - * This function sets `reached_end` if handling this message made the - * iterator reach the end of the trimming range. Note that the output - * message queue could contain messages even if this function sets - * `reached_end`. - */ -static inline -bt_self_message_iterator_status handle_message_with_stream_state( - struct trimmer_iterator *trimmer_it, const bt_message *msg, - struct trimmer_iterator_stream_state *sstate, - int64_t ns_from_origin, bool *reached_end) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - bt_message_type msg_type = bt_message_get_type(msg); - int ret; - - switch (msg_type) { - case BT_MESSAGE_TYPE_EVENT: - if (unlikely(!trimmer_it->end.is_infinite && - ns_from_origin > trimmer_it->end.ns_from_origin)) { - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (unlikely(!sstate->inited)) { - status = ensure_stream_state_is_inited(trimmer_it, - sstate, NULL); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - if (unlikely(!sstate->cur_packet)) { - const bt_event *event = - bt_message_event_borrow_event_const(msg); - const bt_packet *packet = bt_event_borrow_packet_const( - event); - - status = ensure_cur_packet_exists(trimmer_it, sstate, - packet); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - BT_ASSERT(sstate->cur_packet); - push_message(trimmer_it, msg); - msg = NULL; - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - if (unlikely(!trimmer_it->end.is_infinite && - ns_from_origin > trimmer_it->end.ns_from_origin)) { - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (unlikely(!sstate->inited)) { - status = ensure_stream_state_is_inited(trimmer_it, - sstate, NULL); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - BT_ASSERT(!sstate->cur_packet); - sstate->cur_packet = - bt_message_packet_beginning_borrow_packet_const(msg); - bt_packet_get_ref(sstate->cur_packet); - push_message(trimmer_it, msg); - msg = NULL; - break; - case BT_MESSAGE_TYPE_PACKET_END: - sstate->stream_act_end_ns_from_origin = ns_from_origin; - - if (unlikely(!trimmer_it->end.is_infinite && - ns_from_origin > trimmer_it->end.ns_from_origin)) { - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (unlikely(!sstate->inited)) { - status = ensure_stream_state_is_inited(trimmer_it, - sstate, NULL); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - if (unlikely(!sstate->cur_packet)) { - const bt_packet *packet = - bt_message_packet_end_borrow_packet_const(msg); - - status = ensure_cur_packet_exists(trimmer_it, sstate, - packet); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - BT_ASSERT(sstate->cur_packet); - BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); - push_message(trimmer_it, msg); - msg = NULL; - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - { - /* - * `ns_from_origin` is the message's time range's - * beginning time here. - */ - int64_t end_ns_from_origin; - const bt_clock_snapshot *end_cs; - - if (bt_message_get_type(msg) == - BT_MESSAGE_TYPE_DISCARDED_EVENTS) { - /* - * Safe to ignore the return value because we - * know there's a default clock and it's always - * known. - */ - end_cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( - msg); - } else { - /* - * Safe to ignore the return value because we - * know there's a default clock and it's always - * known. - */ - end_cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( - msg); - } - - if (bt_clock_snapshot_get_ns_from_origin(end_cs, - &end_ns_from_origin)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - sstate->stream_act_end_ns_from_origin = end_ns_from_origin; - - if (!trimmer_it->end.is_infinite && - ns_from_origin > trimmer_it->end.ns_from_origin) { - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (!trimmer_it->end.is_infinite && - end_ns_from_origin > trimmer_it->end.ns_from_origin) { - /* - * This message's end time is outside the - * trimming time range: replace it with a new - * message having an end time equal to the - * trimming time range's end and without a - * count. - */ - const bt_clock_class *clock_class = - bt_clock_snapshot_borrow_clock_class_const( - end_cs); - const bt_clock_snapshot *begin_cs; - bt_message *new_msg; - uint64_t end_raw_value; - - ret = clock_raw_value_from_ns_from_origin(clock_class, - trimmer_it->end.ns_from_origin, &end_raw_value); - if (ret) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS) { - begin_cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( - msg); - new_msg = bt_message_discarded_events_create_with_default_clock_snapshots( - trimmer_it->self_msg_iter, - sstate->stream, - bt_clock_snapshot_get_value(begin_cs), - end_raw_value); - } else { - begin_cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( - msg); - new_msg = bt_message_discarded_packets_create_with_default_clock_snapshots( - trimmer_it->self_msg_iter, - sstate->stream, - bt_clock_snapshot_get_value(begin_cs), - end_raw_value); - } - - if (!new_msg) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - /* Replace the original message */ - BT_MESSAGE_MOVE_REF(msg, new_msg); - } - - if (unlikely(!sstate->inited)) { - status = ensure_stream_state_is_inited(trimmer_it, - sstate, NULL); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } - - push_message(trimmer_it, msg); - msg = NULL; - break; - } - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - if (!trimmer_it->end.is_infinite && - ns_from_origin > trimmer_it->end.ns_from_origin) { - /* - * This only happens when the message's time is - * known and is greater than the trimming - * range's end time. Unknown and -inf times are - * always less than - * `trimmer_it->end.ns_from_origin`. - */ - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (!sstate->inited) { - status = ensure_stream_state_is_inited(trimmer_it, - sstate, msg); - msg = NULL; - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } else { - push_message(trimmer_it, msg); - msg = NULL; - } - - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - if (trimmer_it->end.is_infinite) { - push_message(trimmer_it, msg); - msg = NULL; - break; - } - - if (ns_from_origin == INT64_MIN) { - /* Unknown: push as is if stream state is inited */ - if (sstate->inited) { - push_message(trimmer_it, msg); - msg = NULL; - sstate->last_msg_is_stream_activity_end = true; - } - } else if (ns_from_origin == INT64_MAX) { - /* Infinite: use trimming range's end time */ - sstate->stream_act_end_ns_from_origin = - trimmer_it->end.ns_from_origin; - } else { - /* Known: check if outside of trimming range */ - if (ns_from_origin > trimmer_it->end.ns_from_origin) { - sstate->stream_act_end_ns_from_origin = - trimmer_it->end.ns_from_origin; - status = end_iterator_streams(trimmer_it); - *reached_end = true; - break; - } - - if (!sstate->inited) { - /* - * First message for this stream is a - * stream activity end: we can't deduce - * anything about the stream activity - * beginning's time, and using this - * message's time would make a useless - * pair of stream activity beginning/end - * with the same time. Just skip this - * message and wait for something - * useful. - */ - break; - } - - push_message(trimmer_it, msg); - msg = NULL; - sstate->last_msg_is_stream_activity_end = true; - sstate->stream_act_end_ns_from_origin = ns_from_origin; - } - - break; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - /* - * We don't know what follows at this point, so just - * keep this message until we know what to do with it - * (it will be used in ensure_stream_state_is_inited()). - */ - BT_ASSERT(!sstate->inited); - BT_MESSAGE_MOVE_REF(sstate->stream_beginning_msg, msg); - break; - case BT_MESSAGE_TYPE_STREAM_END: - if (sstate->inited) { - /* - * This is the end of an inited stream: end this - * stream if its stream activity end message - * time is not the trimming range's end time - * (which means the final stream activity end - * message had an infinite time). end_stream() - * will generate its own stream end message. - */ - if (trimmer_it->end.is_infinite) { - push_message(trimmer_it, msg); - msg = NULL; - g_hash_table_remove(trimmer_it->stream_states, - sstate->stream); - } else if (sstate->stream_act_end_ns_from_origin < - trimmer_it->end.ns_from_origin) { - status = end_stream(trimmer_it, sstate); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - /* We won't need this stream state again */ - g_hash_table_remove(trimmer_it->stream_states, - sstate->stream); - } - } else { - /* We dont't need this stream state anymore */ - g_hash_table_remove(trimmer_it->stream_states, sstate->stream); - } - - break; - default: - break; - } - -end: - /* We release the message's reference whatever the outcome */ - bt_message_put_ref(msg); - return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; -} - -/* - * Handles an input message. This _could_ make the iterator's output - * message queue grow; this could also consume the message without - * pushing anything to this queue, only modifying the stream state. - * - * This function consumes the `msg` reference, _whatever the outcome_. - * - * This function sets `reached_end` if handling this message made the - * iterator reach the end of the trimming range. Note that the output - * message queue could contain messages even if this function sets - * `reached_end`. - */ -static inline -bt_self_message_iterator_status handle_message( - struct trimmer_iterator *trimmer_it, const bt_message *msg, - bool *reached_end) -{ - bt_self_message_iterator_status status; - const bt_stream *stream = NULL; - int64_t ns_from_origin = INT64_MIN; - bool skip; - int ret; - struct trimmer_iterator_stream_state *sstate = NULL; - - /* Find message's associated stream */ - switch (bt_message_get_type(msg)) { - case BT_MESSAGE_TYPE_EVENT: - stream = bt_event_borrow_stream_const( - bt_message_event_borrow_event_const(msg)); - break; - case BT_MESSAGE_TYPE_PACKET_BEGINNING: - stream = bt_packet_borrow_stream_const( - bt_message_packet_beginning_borrow_packet_const(msg)); - break; - case BT_MESSAGE_TYPE_PACKET_END: - stream = bt_packet_borrow_stream_const( - bt_message_packet_end_borrow_packet_const(msg)); - break; - case BT_MESSAGE_TYPE_DISCARDED_EVENTS: - stream = bt_message_discarded_events_borrow_stream_const(msg); - break; - case BT_MESSAGE_TYPE_DISCARDED_PACKETS: - stream = bt_message_discarded_packets_borrow_stream_const(msg); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: - stream = bt_message_stream_activity_beginning_borrow_stream_const(msg); - break; - case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: - stream = bt_message_stream_activity_end_borrow_stream_const(msg); - break; - case BT_MESSAGE_TYPE_STREAM_BEGINNING: - stream = bt_message_stream_beginning_borrow_stream_const(msg); - break; - case BT_MESSAGE_TYPE_STREAM_END: - stream = bt_message_stream_end_borrow_stream_const(msg); - break; - default: - break; - } - - if (likely(stream)) { - /* Find stream state */ - sstate = g_hash_table_lookup(trimmer_it->stream_states, - stream); - if (unlikely(!sstate)) { - /* No stream state yet: create one now */ - const bt_stream_class *sc; - - /* - * Validate right now that the stream's class - * has a registered default clock class so that - * an existing stream state guarantees existing - * default clock snapshots for its associated - * messages. - * - * Also check that clock snapshots are always - * known. - */ - sc = bt_stream_borrow_class_const(stream); - if (!bt_stream_class_borrow_default_clock_class_const(sc)) { - BT_LOGE("Unsupported stream: stream class does " - "not have a default clock class: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - stream, bt_stream_get_id(stream), - bt_stream_get_name(stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - /* - * Temporary: make sure packet beginning, packet - * end, discarded events, and discarded packets - * messages have default clock snapshots until - * the support for not having them is - * implemented. - */ - if (!bt_stream_class_packets_have_beginning_default_clock_snapshot( - sc)) { - BT_LOGE("Unsupported stream: packets have " - "no beginning clock snapshot: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - stream, bt_stream_get_id(stream), - bt_stream_get_name(stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (!bt_stream_class_packets_have_end_default_clock_snapshot( - sc)) { - BT_LOGE("Unsupported stream: packets have " - "no end clock snapshot: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - stream, bt_stream_get_id(stream), - bt_stream_get_name(stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (bt_stream_class_supports_discarded_events(sc) && - !bt_stream_class_discarded_events_have_default_clock_snapshots(sc)) { - BT_LOGE("Unsupported stream: discarded events " - "have no clock snapshots: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - stream, bt_stream_get_id(stream), - bt_stream_get_name(stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (bt_stream_class_supports_discarded_packets(sc) && - !bt_stream_class_discarded_packets_have_default_clock_snapshots(sc)) { - BT_LOGE("Unsupported stream: discarded packets " - "have no clock snapshots: " - "stream-addr=%p, " - "stream-id=%" PRIu64 ", " - "stream-name=\"%s\"", - stream, bt_stream_get_id(stream), - bt_stream_get_name(stream)); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - sstate = g_new0(struct trimmer_iterator_stream_state, - 1); - if (!sstate) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; - goto end; - } - - sstate->stream = stream; - sstate->stream_act_end_ns_from_origin = INT64_MIN; - g_hash_table_insert(trimmer_it->stream_states, - (void *) stream, sstate); - } - } - - /* Retrieve the message's time */ - ret = get_msg_ns_from_origin(msg, &ns_from_origin, &skip); - if (unlikely(ret)) { - status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; - goto end; - } - - if (likely(sstate)) { - /* Message associated to a stream */ - status = handle_message_with_stream_state(trimmer_it, msg, - sstate, ns_from_origin, reached_end); - - /* - * handle_message_with_stream_state() unconditionally - * consumes `msg`. - */ - msg = NULL; - } else { - /* - * Message not associated to a stream (message iterator - * inactivity). - */ - if (unlikely(ns_from_origin > trimmer_it->end.ns_from_origin)) { - BT_MESSAGE_PUT_REF_AND_RESET(msg); - status = end_iterator_streams(trimmer_it); - *reached_end = true; - } else { - push_message(trimmer_it, msg); - status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - msg = NULL; - } - } - -end: - /* We release the message's reference whatever the outcome */ - bt_message_put_ref(msg); - return status; -} - -static inline -void fill_message_array_from_output_messages( - struct trimmer_iterator *trimmer_it, - bt_message_array_const msgs, uint64_t capacity, uint64_t *count) -{ - *count = 0; - - /* - * Move auto-seek messages to the output array (which is this - * iterator's base message array). - */ - while (capacity > 0 && !g_queue_is_empty(trimmer_it->output_messages)) { - msgs[*count] = pop_message(trimmer_it); - capacity--; - (*count)++; - } - - BT_ASSERT(*count > 0); -} - -static inline -bt_self_message_iterator_status state_ending( - struct trimmer_iterator *trimmer_it, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - if (g_queue_is_empty(trimmer_it->output_messages)) { - trimmer_it->state = TRIMMER_ITERATOR_STATE_ENDED; - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - goto end; - } - - fill_message_array_from_output_messages(trimmer_it, msgs, - capacity, count); - -end: - return status; -} - -static inline -bt_self_message_iterator_status state_trim(struct trimmer_iterator *trimmer_it, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - bt_message_array_const my_msgs; - uint64_t my_count; - uint64_t i; - bool reached_end = false; - - while (g_queue_is_empty(trimmer_it->output_messages)) { - status = (int) bt_self_component_port_input_message_iterator_next( - trimmer_it->upstream_iter, &my_msgs, &my_count); - if (unlikely(status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK)) { - if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_END) { - status = end_iterator_streams(trimmer_it); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - trimmer_it->state = - TRIMMER_ITERATOR_STATE_ENDING; - status = state_ending(trimmer_it, msgs, - capacity, count); - } - - goto end; - } - - BT_ASSERT(my_count > 0); - - for (i = 0; i < my_count; i++) { - status = handle_message(trimmer_it, my_msgs[i], - &reached_end); - - /* - * handle_message() unconditionally consumes the - * message reference. - */ - my_msgs[i] = NULL; - - if (unlikely(status != - BT_SELF_MESSAGE_ITERATOR_STATUS_OK)) { - put_messages(my_msgs, my_count); - goto end; - } - - if (unlikely(reached_end)) { - /* - * This message's time was passed the - * trimming time range's end time: we - * are done. Their might still be - * messages in the output message queue, - * so move to the "ending" state and - * apply it immediately since - * state_trim() is called within the - * "next" method. - */ - put_messages(my_msgs, my_count); - trimmer_it->state = - TRIMMER_ITERATOR_STATE_ENDING; - status = state_ending(trimmer_it, msgs, - capacity, count); - goto end; - } - } - } - - /* - * There's at least one message in the output message queue: - * move the messages to the output message array. - */ - BT_ASSERT(!g_queue_is_empty(trimmer_it->output_messages)); - fill_message_array_from_output_messages(trimmer_it, msgs, - capacity, count); - -end: - return status; -} - -BT_HIDDEN -bt_self_message_iterator_status trimmer_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count) -{ - struct trimmer_iterator *trimmer_it = - bt_self_message_iterator_get_data(self_msg_iter); - bt_self_message_iterator_status status = - BT_SELF_MESSAGE_ITERATOR_STATUS_OK; - - BT_ASSERT(trimmer_it); - - if (likely(trimmer_it->state == TRIMMER_ITERATOR_STATE_TRIM)) { - status = state_trim(trimmer_it, msgs, capacity, count); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - } else { - switch (trimmer_it->state) { - case TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN: - status = state_set_trimmer_iterator_bounds(trimmer_it); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - status = state_seek_initially(trimmer_it); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - status = state_trim(trimmer_it, msgs, capacity, count); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - break; - case TRIMMER_ITERATOR_STATE_SEEK_INITIALLY: - status = state_seek_initially(trimmer_it); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - status = state_trim(trimmer_it, msgs, capacity, count); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - break; - case TRIMMER_ITERATOR_STATE_ENDING: - status = state_ending(trimmer_it, msgs, capacity, - count); - if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { - goto end; - } - - break; - case TRIMMER_ITERATOR_STATE_ENDED: - status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; - break; - default: - abort(); - } - } - -end: - return status; -} - -BT_HIDDEN -void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) -{ - struct trimmer_iterator *trimmer_it = - bt_self_message_iterator_get_data(self_msg_iter); - - BT_ASSERT(trimmer_it); - destroy_trimmer_iterator(trimmer_it); -} diff --git a/plugins/utils/trimmer/trimmer.h b/plugins/utils/trimmer/trimmer.h deleted file mode 100644 index e792c3e5..00000000 --- a/plugins/utils/trimmer/trimmer.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef BABELTRACE_PLUGINS_UTILS_TRIMMER_H -#define BABELTRACE_PLUGINS_UTILS_TRIMMER_H - -/* - * BabelTrace - Trace Trimmer Plug-in - * - * Copyright 2016 Jérémie Galarneau - * - * Author: Jérémie Galarneau - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include - -BT_HIDDEN -void trimmer_finalize(bt_self_component_filter *self_comp); - -BT_HIDDEN -bt_self_component_status trimmer_init(bt_self_component_filter *self_comp, - const bt_value *params, void *init_data); - -BT_HIDDEN -bt_self_message_iterator_status trimmer_msg_iter_init( - bt_self_message_iterator *self_msg_iter, - bt_self_component_filter *self_comp, - bt_self_component_port_output *port); - -BT_HIDDEN -bt_self_message_iterator_status trimmer_msg_iter_next( - bt_self_message_iterator *self_msg_iter, - bt_message_array_const msgs, uint64_t capacity, - uint64_t *count); - -BT_HIDDEN -void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter); - -#endif /* BABELTRACE_PLUGINS_UTILS_TRIMMER_H */ diff --git a/python-plugin-provider/Makefile.am b/python-plugin-provider/Makefile.am deleted file mode 100644 index 2a269f0f..00000000 --- a/python-plugin-provider/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -if ENABLE_PYTHON_PLUGINS -AM_CPPFLAGS += $(PYTHON_INCLUDE) - -lib_LTLIBRARIES = libbabeltrace2-python-plugin-provider.la - -libbabeltrace2_python_plugin_provider_la_SOURCES = \ - python-plugin-provider.c -libbabeltrace2_python_plugin_provider_la_LDFLAGS = \ - $(LT_NO_UNDEFINED) \ - -version-info $(BABELTRACE_LIBRARY_VERSION) \ - $(PYTHON_LIBS) - -libbabeltrace2_python_plugin_provider_la_LIBADD = - -# Link the Python plugin provider library with libbabeltrace2 -# when it's not built-in the babeltrace2 executable. -if !ENABLE_BUILT_IN_PLUGINS -libbabeltrace2_python_plugin_provider_la_LIBADD += \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ - $(top_builddir)/lib/libbabeltrace2.la -endif -endif # ENABLE_PYTHON_PLUGINS diff --git a/python-plugin-provider/python-plugin-provider.c b/python-plugin-provider/python-plugin-provider.c deleted file mode 100644 index 8e5ee08e..00000000 --- a/python-plugin-provider/python-plugin-provider.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * python-plugin-provider.c - * - * Babeltrace Python plugin provider - * - * Copyright 2017 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#define BT_LOG_TAG "PLUGIN-PY" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PYTHON_PLUGIN_FILE_PREFIX "bt_plugin_" -#define PYTHON_PLUGIN_FILE_PREFIX_LEN (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1) -#define PYTHON_PLUGIN_FILE_EXT ".py" -#define PYTHON_PLUGIN_FILE_EXT_LEN (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1) - -enum python_state { - /* init_python() not called yet */ - PYTHON_STATE_NOT_INITED, - - /* init_python() called once with success */ - PYTHON_STATE_FULLY_INITIALIZED, - - /* init_python() called once without success */ - PYTHON_STATE_CANNOT_INITIALIZE, -} python_state = PYTHON_STATE_NOT_INITED; - -static PyObject *py_try_load_plugin_module_func = NULL; -static bool python_was_initialized_by_us; - -static -void print_python_traceback_warn(void) -{ - if (BT_LOG_ON_WARN && Py_IsInitialized() && PyErr_Occurred()) { - BT_LOGW_STR("Exception occured: traceback: "); - PyErr_Print(); - } -} - -static -void pyerr_clear(void) -{ - if (Py_IsInitialized()) { - PyErr_Clear(); - } -} - -static -void init_python(void) -{ - PyObject *py_bt2_py_plugin_mod = NULL; - const char *dis_python_env; -#ifndef __MINGW32__ - sighandler_t old_sigint = signal(SIGINT, SIG_DFL); -#endif - - if (python_state != PYTHON_STATE_NOT_INITED) { - goto end; - } - - /* - * User can disable Python plugin support with the - * BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable set to - * 1. - */ - dis_python_env = getenv("BABELTRACE_DISABLE_PYTHON_PLUGINS"); - if (dis_python_env && strcmp(dis_python_env, "1") == 0) { - BT_LOGI_STR("Python plugin support is disabled because `BABELTRACE_DISABLE_PYTHON_PLUGINS=1`."); - python_state = PYTHON_STATE_CANNOT_INITIALIZE; - goto end; - } - - if (!Py_IsInitialized()) { - BT_LOGI_STR("Python interpreter is not initialized: initializing Python interpreter."); - Py_InitializeEx(0); - python_was_initialized_by_us = true; - BT_LOGI("Initialized Python interpreter: version=\"%s\"", - Py_GetVersion()); - } else { - BT_LOGI("Python interpreter is already initialized: version=\"%s\"", - Py_GetVersion()); - } - - py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin"); - if (!py_bt2_py_plugin_mod) { - BT_LOGI_STR("Cannot import bt2.py_plugin Python module: Python plugin support is disabled."); - python_state = PYTHON_STATE_CANNOT_INITIALIZE; - goto end; - } - - py_try_load_plugin_module_func = - PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module"); - if (!py_try_load_plugin_module_func) { - BT_LOGI_STR("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module: Python plugin support is disabled."); - python_state = PYTHON_STATE_CANNOT_INITIALIZE; - goto end; - } - - python_state = PYTHON_STATE_FULLY_INITIALIZED; - -end: -#ifndef __MINGW32__ - if (old_sigint != SIG_ERR) { - (void) signal(SIGINT, old_sigint); - } -#endif - - print_python_traceback_warn(); - pyerr_clear(); - Py_XDECREF(py_bt2_py_plugin_mod); - return; -} - -__attribute__((destructor)) static -void fini_python(void) { - if (Py_IsInitialized() && python_was_initialized_by_us) { - if (py_try_load_plugin_module_func) { - Py_DECREF(py_try_load_plugin_module_func); - py_try_load_plugin_module_func = NULL; - } - - Py_Finalize(); - BT_LOGI_STR("Finalized Python interpreter."); - } - - python_state = PYTHON_STATE_NOT_INITED; -} - -static -bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info) -{ - bt_plugin *plugin = NULL; - PyObject *py_name = NULL; - PyObject *py_author = NULL; - PyObject *py_description = NULL; - PyObject *py_license = NULL; - PyObject *py_version = NULL; - PyObject *py_comp_class_addrs = NULL; - const char *name = NULL; - const char *author = NULL; - const char *description = NULL; - const char *license = NULL; - unsigned int major = 0, minor = 0, patch = 0; - const char *version_extra = NULL; - int ret; - - BT_ASSERT(plugin_info); - BT_ASSERT(python_state == PYTHON_STATE_FULLY_INITIALIZED); - py_name = PyObject_GetAttrString(plugin_info, "name"); - if (!py_name) { - BT_LOGW("Cannot find `name` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - py_author = PyObject_GetAttrString(plugin_info, "author"); - if (!py_author) { - BT_LOGW("Cannot find `author` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - py_description = PyObject_GetAttrString(plugin_info, "description"); - if (!py_description) { - BT_LOGW("Cannot find `desciption` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - py_license = PyObject_GetAttrString(plugin_info, "license"); - if (!py_license) { - BT_LOGW("Cannot find `license` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - py_version = PyObject_GetAttrString(plugin_info, "version"); - if (!py_version) { - BT_LOGW("Cannot find `version` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - py_comp_class_addrs = PyObject_GetAttrString(plugin_info, - "comp_class_addrs"); - if (!py_comp_class_addrs) { - BT_LOGW("Cannot find `comp_class_addrs` attribute in Python plugin info object: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - if (PyUnicode_Check(py_name)) { - name = PyUnicode_AsUTF8(py_name); - if (!name) { - BT_LOGW("Cannot decode Python plugin name string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } else { - /* Plugin name is mandatory */ - BT_LOGW("Plugin name is not a string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - - if (PyUnicode_Check(py_author)) { - author = PyUnicode_AsUTF8(py_author); - if (!author) { - BT_LOGW("Cannot decode Python plugin author string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } - - if (PyUnicode_Check(py_description)) { - description = PyUnicode_AsUTF8(py_description); - if (!description) { - BT_LOGW("Cannot decode Python plugin description string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } - - if (PyUnicode_Check(py_license)) { - license = PyUnicode_AsUTF8(py_license); - if (!license) { - BT_LOGW("Cannot decode Python plugin license string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } - - if (PyTuple_Check(py_version)) { - if (PyTuple_Size(py_version) >= 3) { - PyObject *py_major = PyTuple_GetItem(py_version, 0); - PyObject *py_minor = PyTuple_GetItem(py_version, 1); - PyObject *py_patch = PyTuple_GetItem(py_version, 2); - - BT_ASSERT(py_major); - BT_ASSERT(py_minor); - BT_ASSERT(py_patch); - - if (PyLong_Check(py_major)) { - major = PyLong_AsUnsignedLong(py_major); - } - - if (PyLong_Check(py_minor)) { - minor = PyLong_AsUnsignedLong(py_minor); - } - - if (PyLong_Check(py_patch)) { - patch = PyLong_AsUnsignedLong(py_patch); - } - - if (PyErr_Occurred()) { - /* Overflow error, most probably */ - BT_LOGW("Invalid Python plugin version format: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } - - if (PyTuple_Size(py_version) >= 4) { - PyObject *py_extra = PyTuple_GetItem(py_version, 3); - - BT_ASSERT(py_extra); - - if (PyUnicode_Check(py_extra)) { - version_extra = PyUnicode_AsUTF8(py_extra); - if (!version_extra) { - BT_LOGW("Cannot decode Python plugin version's extra string: " - "py-plugin-info-addr=%p", plugin_info); - goto error; - } - } - } - } - - plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON); - if (!plugin) { - BT_LOGE_STR("Cannot create empty plugin object."); - goto error; - } - - bt_plugin_set_name(plugin, name); - - if (description) { - bt_plugin_set_description(plugin, description); - } - - if (author) { - bt_plugin_set_author(plugin, author); - } - - if (license) { - bt_plugin_set_license(plugin, license); - } - - bt_plugin_set_version(plugin, major, minor, patch, version_extra); - - if (PyList_Check(py_comp_class_addrs)) { - size_t i; - - for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) { - bt_component_class *comp_class; - PyObject *py_comp_class_addr; - - py_comp_class_addr = - PyList_GetItem(py_comp_class_addrs, i); - BT_ASSERT(py_comp_class_addr); - if (PyLong_Check(py_comp_class_addr)) { - comp_class = PyLong_AsVoidPtr(py_comp_class_addr); - } else { - BT_LOGW("Component class address is not an integer in Python plugin info object: " - "py-plugin-info-addr=%p, index=%zu", - plugin_info, i); - continue; - } - - ret = bt_plugin_add_component_class(plugin, comp_class); - if (ret < 0) { - BT_LOGE("Cannot add component class to plugin: " - "py-plugin-info-addr=%p, " - "plugin-addr=%p, plugin-name=\"%s\", " - "comp-class-addr=%p, " - "comp-class-name=\"%s\", " - "comp-class-type=%s", - plugin_info, - plugin, bt_plugin_get_name(plugin), - comp_class, - bt_component_class_get_name(comp_class), - bt_component_class_type_string( - bt_component_class_get_type(comp_class))); - continue; - } - } - } - - goto end; - -error: - print_python_traceback_warn(); - pyerr_clear(); - BT_OBJECT_PUT_REF_AND_RESET(plugin); - -end: - Py_XDECREF(py_name); - Py_XDECREF(py_author); - Py_XDECREF(py_description); - Py_XDECREF(py_license); - Py_XDECREF(py_version); - Py_XDECREF(py_comp_class_addrs); - return plugin; -} - -G_MODULE_EXPORT -bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path) -{ - bt_plugin_set *plugin_set = NULL; - bt_plugin *plugin = NULL; - PyObject *py_plugin_info = NULL; - gchar *basename = NULL; - size_t path_len; - - BT_ASSERT(path); - - if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) { - /* - * We do not even care about the rest of the function - * here because we already know Python cannot be fully - * initialized. - */ - goto error; - } - - BT_LOGD("Creating all Python plugins from file: path=\"%s\"", path); - path_len = strlen(path); - - /* File name ends with `.py` */ - if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN, - PYTHON_PLUGIN_FILE_EXT, - PYTHON_PLUGIN_FILE_EXT_LEN) != 0) { - BT_LOGD("Skipping non-Python file: path=\"%s\"", path); - goto error; - } - - /* File name starts with `bt_plugin_` */ - basename = g_path_get_basename(path); - if (!basename) { - BT_LOGW("Cannot get path's basename: path=\"%s\"", path); - goto error; - } - - if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX, - PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) { - BT_LOGD("Skipping Python file not starting with `%s`: " - "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX, path); - goto error; - } - - /* - * Initialize Python now. - * - * This is not done in the library contructor because the - * interpreter is somewhat slow to initialize. If you don't - * have any potential Python plugins, you don't need to endure - * this waiting time everytime you load the library. - */ - init_python(); - if (python_state != PYTHON_STATE_FULLY_INITIALIZED) { - /* - * For some reason we cannot initialize Python, - * import the required modules, and get the required - * attributes from them. - */ - BT_LOGI("Failed to initialize Python interpreter."); - goto error; - } - - /* - * Call bt2.py_plugin._try_load_plugin_module() with this path - * to get plugin info if the plugin is loadable and complete. - * This function returns None when there's an error, but just in - * case we also manually clear the last Python error state. - */ - BT_LOGD_STR("Getting Python plugin info object from Python module."); - py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func, - "(s)", path); - if (!py_plugin_info || py_plugin_info == Py_None) { - BT_LOGW("Cannot load Python plugin: path=\"%s\"", path); - print_python_traceback_warn(); - PyErr_Clear(); - goto error; - } - - /* - * Get bt_plugin from plugin info object. - */ - plugin = bt_plugin_from_python_plugin_info(py_plugin_info); - if (!plugin) { - BT_LOGW("Cannot create plugin object from Python plugin info object: " - "path=\"%s\", py-plugin-info-addr=%p", - path, py_plugin_info); - goto error; - } - - bt_plugin_set_path(plugin, path); - plugin_set = bt_plugin_set_create(); - if (!plugin_set) { - BT_LOGE_STR("Cannot create empty plugin set."); - goto error; - } - - bt_plugin_set_add_plugin(plugin_set, plugin); - BT_LOGD("Created all Python plugins from file: path=\"%s\", " - "plugin-addr=%p, plugin-name=\"%s\"", - path, plugin, bt_plugin_get_name(plugin)); - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(plugin_set); - -end: - bt_plugin_put_ref(plugin); - Py_XDECREF(py_plugin_info); - g_free(basename); - return plugin_set; -} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..28b7bd4c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS = \ + common \ + ctfser \ + fd-cache \ + compat \ + logging \ + ctf-writer \ + lib \ + python-plugin-provider \ + plugins \ + cli \ + bindings + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = babeltrace2.pc babeltrace2-ctf-writer.pc diff --git a/src/babeltrace2-ctf-writer.pc.in b/src/babeltrace2-ctf-writer.pc.in new file mode 100644 index 00000000..504d8597 --- /dev/null +++ b/src/babeltrace2-ctf-writer.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Babeltrace CTF parser +Description: libbabeltrace2-ctf provides the specific bits necessary to write a Common Trace Format (CTF) trace. +Version: @PACKAGE_VERSION@ +Requires: +Requires.private: uuid popt +Libs: -L${libdir} -lbabeltrace2-ctf-writer +Cflags: -I${includedir} + diff --git a/src/babeltrace2.pc.in b/src/babeltrace2.pc.in new file mode 100644 index 00000000..646aeb47 --- /dev/null +++ b/src/babeltrace2.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Babeltrace +Description: libbabeltrace2 provides a reader for trace files, reading mainly the +Common Trace Format (CTF). +Version: @PACKAGE_VERSION@ +Requires: +Requires.private: uuid popt +Libs: -L${libdir} -lbabeltrace2 +Cflags: -I${includedir} + diff --git a/src/bindings/Makefile.am b/src/bindings/Makefile.am new file mode 100644 index 00000000..773e43ad --- /dev/null +++ b/src/bindings/Makefile.am @@ -0,0 +1,3 @@ +if ENABLE_PYTHON_BINDINGS +SUBDIRS = python +endif diff --git a/src/bindings/python/Makefile.am b/src/bindings/python/Makefile.am new file mode 100644 index 00000000..f3cec072 --- /dev/null +++ b/src/bindings/python/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = bt2 diff --git a/src/bindings/python/bt2/.gitignore b/src/bindings/python/bt2/.gitignore new file mode 100644 index 00000000..b893bced --- /dev/null +++ b/src/bindings/python/bt2/.gitignore @@ -0,0 +1,7 @@ +bt2/__init__.py +bt2/native_bt.py +bt2/native_bt_wrap.c +build +build-python-bindings.stamp +installed_files.txt +setup.py diff --git a/src/bindings/python/bt2/Makefile.am b/src/bindings/python/bt2/Makefile.am new file mode 100644 index 00000000..70da6698 --- /dev/null +++ b/src/bindings/python/bt2/Makefile.am @@ -0,0 +1,117 @@ +# Since the shared object used by the python bindings is not built with +# libtool, we need to add the directory containing libbabeltrace2 to the +# linker path. +AM_LDFLAGS=-L$(top_builddir)/src/lib/.libs + +INSTALLED_FILES=$(builddir)/installed_files.txt + +STATIC_BINDINGS_DEPS = \ + bt2/logging.c \ + bt2/logging.h \ + bt2/native_bt_clock_class.i \ + bt2/native_bt_clock_snapshot.i \ + bt2/native_bt_component_class.i \ + bt2/native_bt_component.i \ + bt2/native_bt_connection.i \ + bt2/native_bt_event_class.i \ + bt2/native_bt_event.i \ + bt2/native_bt_field_class.i \ + bt2/native_bt_field_path.i \ + bt2/native_bt_field.i \ + bt2/native_bt_graph.i \ + bt2/native_bt.i \ + bt2/native_bt_logging.i \ + bt2/native_bt_message.i \ + bt2/native_bt_notifier.i \ + bt2/native_bt_packet.i \ + bt2/native_bt_plugin.i \ + bt2/native_bt_port.i \ + bt2/native_bt_query_exec.i \ + bt2/native_bt_stream_class.i \ + bt2/native_bt_stream.i \ + bt2/native_bt_trace_class.i \ + bt2/native_bt_trace.i \ + bt2/native_bt_value.i \ + bt2/native_bt_version.i \ + bt2/clock_class.py \ + bt2/clock_snapshot.py \ + bt2/component.py \ + bt2/connection.py \ + bt2/event_class.py \ + bt2/event.py \ + bt2/field.py \ + bt2/field_class.py \ + bt2/field_path.py \ + bt2/graph.py \ + bt2/logging.py \ + bt2/message_iterator.py \ + bt2/message.py \ + bt2/object.py \ + bt2/packet.py \ + bt2/plugin.py \ + bt2/port.py \ + bt2/py_plugin.py \ + bt2/query_executor.py \ + bt2/stream_class.py \ + bt2/stream.py \ + bt2/trace.py \ + bt2/trace_class.py \ + bt2/trace_collection_message_iterator.py \ + bt2/utils.py \ + bt2/value.py + +GENERATED_BINDINGS_DEPS = \ + bt2/__init__.py \ + setup.py + +BUILD_FLAGS=CC="$(CC)" \ + CFLAGS="$(GLIB_CFLAGS) $(AM_CFLAGS) $(CFLAGS)" \ + CPPFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS)" \ + LDFLAGS="$(AM_LDFLAGS) $(LDFLAGS) $(GLIB_LIBS) $(LIBS)" + +all-local: build-python-bindings.stamp + +copy-static-deps.stamp: $(addprefix $(srcdir)/, $(STATIC_BINDINGS_DEPS)) + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for file in $(STATIC_BINDINGS_DEPS); do \ + cp -f $(srcdir)/$$file $(builddir)/$$file; \ + done; \ + fi + touch $@ + +build-python-bindings.stamp: copy-static-deps.stamp $(GENERATED_BINDINGS_DEPS) + $(BUILD_FLAGS) $(PYTHON) $(builddir)/setup.py build_ext --force --swig "$(SWIG)" + $(BUILD_FLAGS) $(PYTHON) $(builddir)/setup.py build --force + touch $@ + +install-exec-local: build-python-bindings.stamp + @opts="--prefix=$(prefix) --record $(INSTALLED_FILES) --verbose --no-compile $(DISTSETUPOPTS)"; \ + if [ "$(DESTDIR)" != "" ]; then \ + opts="$$opts --root=$(DESTDIR)"; \ + fi; \ + $(PYTHON) $(builddir)/setup.py install $$opts; + +clean-local: + rm -rf $(builddir)/build + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for file in $(STATIC_BINDINGS_DEPS); do \ + rm -f $(builddir)/$$file; \ + done; \ + fi + +# Distutils' setup.py does not include an uninstall target, we thus need to do +# it manually. We save the path of the files that were installed during the install target +# and delete them during the uninstallation. +uninstall-local: + if [ "$(DESTDIR)" != "" ]; then \ + $(SED) -i "s|^|$(DESTDIR)/|g" $(INSTALLED_FILES); \ + fi + cat $(INSTALLED_FILES) | xargs rm -rf || true + $(GREP) "__init__.py" $(INSTALLED_FILES) | xargs dirname | xargs rm -rf || true + rm -f $(INSTALLED_FILES) + +# distribute: extra Python modules and SWIG interface files +EXTRA_DIST = $(STATIC_BINDINGS_DEPS) + +# clean: generated C and Python files (by SWIG) +CLEANFILES = bt2/native_bt.py bt2/native_bt_wrap.c build-python-bindings.stamp copy-static-deps.stamp diff --git a/src/bindings/python/bt2/bt2/__init__.py.in b/src/bindings/python/bt2/bt2/__init__.py.in new file mode 100644 index 00000000..f80cf658 --- /dev/null +++ b/src/bindings/python/bt2/bt2/__init__.py.in @@ -0,0 +1,133 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +__version__ = '@PACKAGE_VERSION@' + + +from bt2.clock_class import * +from bt2.clock_snapshot import * +from bt2.component import * +from bt2.component import _FilterComponent +from bt2.component import _GenericFilterComponentClass +from bt2.component import _GenericSinkComponentClass +from bt2.component import _GenericSourceComponentClass +from bt2.component import _SinkComponent +from bt2.component import _SourceComponent +from bt2.component import _UserFilterComponent +from bt2.component import _UserSinkComponent +from bt2.component import _UserSourceComponent +from bt2.connection import * +from bt2.connection import _Connection +from bt2.event import _Event +from bt2.event_class import * +from bt2.field_class import * +from bt2.field_path import * +from bt2.field import * +from bt2.graph import * +from bt2.logging import * +from bt2.message import * +from bt2.message import _DiscardedEventsMessage +from bt2.message import _DiscardedPacketsMessage +from bt2.message_iterator import * +from bt2.message_iterator import _UserMessageIterator +from bt2.packet import _Packet +from bt2.plugin import * +from bt2.port import * +from bt2.py_plugin import * +from bt2.query_executor import * +from bt2.stream import _Stream +from bt2.stream_class import * +from bt2.trace import * +from bt2.trace_class import * +from bt2.trace_collection_message_iterator import * +from bt2.value import * +from bt2.value import _Value +from bt2.value import _IntegerValue + + +class Error(Exception): + pass + + +class CreationError(Error): + '''Raised when object creation fails due to memory issues.''' + + +class InvalidQueryObject(Error): + pass + + +class InvalidQueryParams(Error): + pass + + +class TryAgain(Exception): + pass + + +class Stop(StopIteration): + pass + + +class PortConnectionRefused(Exception): + pass + + +class IncompleteUserClass(Error): + pass + + +class GraphCanceled(Exception): + pass + + +class QueryExecutorCanceled(Exception): + pass + + +class NonexistentClockSnapshot(Error): + pass + + +class _ListenerHandle: + def __init__(self, listener_id, obj): + self._listener_id = listener_id + self._obj = obj + + +def _init_and_register_exit(): + import bt2.native_bt as _native_bt + import atexit + + atexit.register(_native_bt.py3_cc_exit_handler) + version = (_native_bt.version_get_major(), _native_bt.version_get_minor(), + _native_bt.version_get_patch(), _native_bt.version_get_extra()) + _native_bt.py3_cc_init_from_bt2() + + +_init_and_register_exit() + + +try: + del native_bt +except: + pass diff --git a/src/bindings/python/bt2/bt2/clock_class.py b/src/bindings/python/bt2/bt2/clock_class.py new file mode 100644 index 00000000..59a93cd7 --- /dev/null +++ b/src/bindings/python/bt2/bt2/clock_class.py @@ -0,0 +1,144 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2 +import uuid as uuidp + + +class ClockClassOffset: + def __init__(self, seconds=0, cycles=0): + utils._check_int64(seconds) + utils._check_int64(cycles) + self._seconds = seconds + self._cycles = cycles + + @property + def seconds(self): + return self._seconds + + @property + def cycles(self): + return self._cycles + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # not comparing apples to apples + return False + + return (self.seconds, self.cycles) == (other.seconds, other.cycles) + + +class _ClockClass(object._SharedObject): + _get_ref = staticmethod(native_bt.clock_class_get_ref) + _put_ref = staticmethod(native_bt.clock_class_put_ref) + + @property + def name(self): + return native_bt.clock_class_get_name(self._ptr) + + def _name(self, name): + utils._check_str(name) + ret = native_bt.clock_class_set_name(self._ptr, name) + utils._handle_ret(ret, "cannot set clock class object's name") + + _name = property(fset=_name) + + @property + def description(self): + return native_bt.clock_class_get_description(self._ptr) + + def _description(self, description): + utils._check_str(description) + ret = native_bt.clock_class_set_description(self._ptr, description) + utils._handle_ret(ret, "cannot set clock class object's description") + + _description = property(fset=_description) + + @property + def frequency(self): + return native_bt.clock_class_get_frequency(self._ptr) + + def _frequency(self, frequency): + utils._check_uint64(frequency) + native_bt.clock_class_set_frequency(self._ptr, frequency) + + _frequency = property(fset=_frequency) + + @property + def precision(self): + precision = native_bt.clock_class_get_precision(self._ptr) + return precision + + def _precision(self, precision): + utils._check_uint64(precision) + native_bt.clock_class_set_precision(self._ptr, precision) + + _precision = property(fset=_precision) + + @property + def offset(self): + offset_s, offset_cycles = native_bt.clock_class_get_offset(self._ptr) + return ClockClassOffset(offset_s, offset_cycles) + + def _offset(self, offset): + utils._check_type(offset, ClockClassOffset) + native_bt.clock_class_set_offset(self._ptr, offset.seconds, offset.cycles) + + _offset = property(fset=_offset) + + @property + def origin_is_unix_epoch(self): + return native_bt.clock_class_origin_is_unix_epoch(self._ptr) + + def _origin_is_unix_epoch(self, origin_is_unix_epoch): + utils._check_bool(origin_is_unix_epoch) + native_bt.clock_class_set_origin_is_unix_epoch(self._ptr, int(origin_is_unix_epoch)) + + _origin_is_unix_epoch = property(fset=_origin_is_unix_epoch) + + @property + def uuid(self): + uuid_bytes = native_bt.clock_class_get_uuid(self._ptr) + + if uuid_bytes is None: + return + + return uuidp.UUID(bytes=uuid_bytes) + + def _uuid(self, uuid): + utils._check_type(uuid, uuidp.UUID) + native_bt.clock_class_set_uuid(self._ptr, uuid.bytes) + + _uuid = property(fset=_uuid) + + def cycles_to_ns_from_origin(self, cycles): + utils._check_uint64(cycles) + ret, ns = native_bt.clock_class_cycles_to_ns_from_origin(self._ptr, cycles) + + error_msg = "cannot convert clock value to nanoseconds from origin for given clock class" + + if ret == native_bt.CLOCK_CLASS_STATUS_OVERFLOW: + raise OverflowError(error_msg) + + utils._handle_ret(ret, error_msg) + return ns diff --git a/src/bindings/python/bt2/bt2/clock_snapshot.py b/src/bindings/python/bt2/bt2/clock_snapshot.py new file mode 100644 index 00000000..4bf37d62 --- /dev/null +++ b/src/bindings/python/bt2/bt2/clock_snapshot.py @@ -0,0 +1,70 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import numbers +import bt2 +import functools + + +class _BaseClockSnapshot(object._UniqueObject): + @property + def clock_class(self): + cc_ptr = native_bt.clock_snapshot_borrow_clock_class_const(self._ptr) + assert cc_ptr is not None + return bt2.clock_class._ClockClass._create_from_ptr_and_get_ref(cc_ptr) + + +@functools.total_ordering +class _ClockSnapshot(_BaseClockSnapshot): + @property + def value(self): + return native_bt.clock_snapshot_get_value(self._ptr) + + @property + def ns_from_origin(self): + ret, ns = native_bt.clock_snapshot_get_ns_from_origin(self._ptr) + + if ret == native_bt.CLOCK_SNAPSHOT_STATUS_OVERFLOW: + raise OverflowError("cannot get clock snapshot's nanoseconds from origin") + + return ns + + def __eq__(self, other): + if not isinstance(other, numbers.Integral): + return NotImplemented + + return self.value == int(other) + + def __lt__(self, other): + if not isinstance(other, numbers.Integral): + return NotImplemented + + return self.value < int(other) + + +class _UnknownClockSnapshot(_BaseClockSnapshot): + pass + + +class _InfiniteClockSnapshot(_BaseClockSnapshot): + pass diff --git a/src/bindings/python/bt2/bt2/component.py b/src/bindings/python/bt2/bt2/component.py new file mode 100644 index 00000000..3480f657 --- /dev/null +++ b/src/bindings/python/bt2/bt2/component.py @@ -0,0 +1,785 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.message_iterator +import collections.abc +import bt2.value +import traceback +import bt2.port +import sys +import bt2 +import os + + +_env_var = os.environ.get('BABELTRACE_PYTHON_BT2_NO_TRACEBACK') +_NO_PRINT_TRACEBACK = _env_var == '1' + + +# This class wraps a component class pointer. This component class could +# have been created by Python code, but since we only have the pointer, +# we can only wrap it in a generic way and lose the original Python +# class. +# +# Subclasses must implement some methods that this base class uses: +# +# - _as_component_class_ptr: static method, convert the passed component class +# pointer to a 'bt_component_class *'. + +class _GenericComponentClass(object._SharedObject): + @property + def name(self): + ptr = self._as_component_class_ptr(self._ptr) + name = native_bt.component_class_get_name(ptr) + assert name is not None + return name + + @property + def description(self): + ptr = self._as_component_class_ptr(self._ptr) + return native_bt.component_class_get_description(ptr) + + @property + def help(self): + ptr = self._as_component_class_ptr(self._ptr) + return native_bt.component_class_get_help(ptr) + + def _component_class_ptr(self): + return self._as_component_class_ptr(self._ptr) + + def __eq__(self, other): + if not isinstance(other, _GenericComponentClass): + try: + if not issubclass(other, _UserComponent): + return False + except TypeError: + return False + + return self.addr == other.addr + + +class _GenericSourceComponentClass(_GenericComponentClass): + _get_ref = staticmethod(native_bt.component_class_source_get_ref) + _put_ref = staticmethod(native_bt.component_class_source_put_ref) + _as_component_class_ptr = staticmethod(native_bt.component_class_source_as_component_class) + + +class _GenericFilterComponentClass(_GenericComponentClass): + _get_ref = staticmethod(native_bt.component_class_filter_get_ref) + _put_ref = staticmethod(native_bt.component_class_filter_put_ref) + _as_component_class_ptr = staticmethod(native_bt.component_class_filter_as_component_class) + + +class _GenericSinkComponentClass(_GenericComponentClass): + _get_ref = staticmethod(native_bt.component_class_sink_get_ref) + _put_ref = staticmethod(native_bt.component_class_sink_put_ref) + _as_component_class_ptr = staticmethod(native_bt.component_class_sink_as_component_class) + + +def _handle_component_status(status, gen_error_msg): + if status == native_bt.SELF_COMPONENT_STATUS_END: + raise bt2.Stop + elif status == native_bt.SELF_COMPONENT_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION: + raise bt2.PortConnectionRefused + elif status < 0: + raise bt2.Error(gen_error_msg) + + +class _PortIterator(collections.abc.Iterator): + def __init__(self, comp_ports): + self._comp_ports = comp_ports + self._at = 0 + + def __next__(self): + if self._at == len(self._comp_ports): + raise StopIteration + + comp_ports = self._comp_ports + comp_ptr = comp_ports._component_ptr + + port_ptr = comp_ports._borrow_port_ptr_at_index(comp_ptr, self._at) + assert port_ptr is not None + + name = native_bt.port_get_name(comp_ports._port_pycls._as_port_ptr(port_ptr)) + assert name is not None + + self._at += 1 + return name + + +class _ComponentPorts(collections.abc.Mapping): + + # component_ptr is a bt_component_source *, bt_component_filter * or + # bt_component_sink *. Its type must match the type expected by the + # functions passed as arguments. + + def __init__(self, component_ptr, + borrow_port_ptr_by_name, + borrow_port_ptr_at_index, + get_port_count, + port_pycls): + self._component_ptr = component_ptr + self._borrow_port_ptr_by_name = borrow_port_ptr_by_name + self._borrow_port_ptr_at_index = borrow_port_ptr_at_index + self._get_port_count = get_port_count + self._port_pycls = port_pycls + + def __getitem__(self, key): + utils._check_str(key) + port_ptr = self._borrow_port_ptr_by_name(self._component_ptr, key) + + if port_ptr is None: + raise KeyError(key) + + return self._port_pycls._create_from_ptr_and_get_ref(port_ptr) + + def __len__(self): + count = self._get_port_count(self._component_ptr) + assert count >= 0 + return count + + def __iter__(self): + return _PortIterator(self) + + +# This class holds the methods which are common to both generic +# component objects and Python user component objects. +# +# Subclasses must provide these methods or property: +# +# - _borrow_component_class_ptr: static method, must return a pointer to the +# specialized component class (e.g. 'bt_component_class_sink *') of the +# passed specialized component pointer (e.g. 'bt_component_sink *'). +# - _comp_cls_type: property, one of the native_bt.COMPONENT_CLASS_TYPE_* +# constants. +# - _as_component_ptr: static method, must return the passed specialized +# component pointer (e.g. 'bt_component_sink *') as a 'bt_component *'. + +class _Component: + @property + def name(self): + ptr = self._as_component_ptr(self._ptr) + name = native_bt.component_get_name(ptr) + assert name is not None + return name + + @property + def cls(self): + cc_ptr = self._borrow_component_class_ptr(self._ptr) + assert cc_ptr is not None + return _create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) + + def __eq__(self, other): + if not hasattr(other, 'addr'): + return False + + return self.addr == other.addr + + +class _SourceComponent(_Component): + _borrow_component_class_ptr = staticmethod(native_bt.component_source_borrow_class_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE + _as_component_class_ptr = staticmethod(native_bt.component_class_source_as_component_class) + _as_component_ptr = staticmethod(native_bt.component_source_as_component_const) + + +class _FilterComponent(_Component): + _borrow_component_class_ptr = staticmethod(native_bt.component_filter_borrow_class_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_FILTER + _as_component_class_ptr = staticmethod(native_bt.component_class_filter_as_component_class) + _as_component_ptr = staticmethod(native_bt.component_filter_as_component_const) + + +class _SinkComponent(_Component): + _borrow_component_class_ptr = staticmethod(native_bt.component_sink_borrow_class_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SINK + _as_component_class_ptr = staticmethod(native_bt.component_class_sink_as_component_class) + _as_component_ptr = staticmethod(native_bt.component_sink_as_component_const) + + +# This is analogous to _GenericSourceComponentClass, but for source +# component objects. +class _GenericSourceComponent(object._SharedObject, _SourceComponent): + _get_ref = staticmethod(native_bt.component_source_get_ref) + _put_ref = staticmethod(native_bt.component_source_put_ref) + + @property + def output_ports(self): + return _ComponentPorts(self._ptr, + native_bt.component_source_borrow_output_port_by_name_const, + native_bt.component_source_borrow_output_port_by_index_const, + native_bt.component_source_get_output_port_count, + bt2.port._OutputPort) + + +# This is analogous to _GenericFilterComponentClass, but for filter +# component objects. +class _GenericFilterComponent(object._SharedObject, _FilterComponent): + _get_ref = staticmethod(native_bt.component_filter_get_ref) + _put_ref = staticmethod(native_bt.component_filter_put_ref) + + @property + def output_ports(self): + return _ComponentPorts(self._ptr, + native_bt.component_filter_borrow_output_port_by_name_const, + native_bt.component_filter_borrow_output_port_by_index_const, + native_bt.component_filter_get_output_port_count, + bt2.port._OutputPort) + + @property + def input_ports(self): + return _ComponentPorts(self._ptr, + native_bt.component_filter_borrow_input_port_by_name_const, + native_bt.component_filter_borrow_input_port_by_index_const, + native_bt.component_filter_get_input_port_count, + bt2.port._InputPort) + + +# This is analogous to _GenericSinkComponentClass, but for sink +# component objects. +class _GenericSinkComponent(object._SharedObject, _SinkComponent): + _get_ref = staticmethod(native_bt.component_sink_get_ref) + _put_ref = staticmethod(native_bt.component_sink_put_ref) + + @property + def input_ports(self): + return _ComponentPorts(self._ptr, + native_bt.component_sink_borrow_input_port_by_name_const, + native_bt.component_sink_borrow_input_port_by_index_const, + native_bt.component_sink_get_input_port_count, + bt2.port._InputPort) + + +_COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS = { + native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponent, + native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponent, + native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponent, +} + + +_COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS = { + native_bt.COMPONENT_CLASS_TYPE_SOURCE: _GenericSourceComponentClass, + native_bt.COMPONENT_CLASS_TYPE_FILTER: _GenericFilterComponentClass, + native_bt.COMPONENT_CLASS_TYPE_SINK: _GenericSinkComponentClass, +} + + +# Create a component Python object of type _GenericSourceComponent, +# _GenericFilterComponent or _GenericSinkComponent, depending on +# comp_cls_type. +# +# Steals the reference to ptr from the caller. + +def _create_component_from_ptr(ptr, comp_cls_type): + return _COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS[comp_cls_type]._create_from_ptr(ptr) + + +# Same as the above, but acquire a new reference instead of stealing the +# reference from the caller. + +def _create_component_from_ptr_and_get_ref(ptr, comp_cls_type): + return _COMP_CLS_TYPE_TO_GENERIC_COMP_PYCLS[comp_cls_type]._create_from_ptr_and_get_ref(ptr) + + +# Create a component class Python object of type +# _GenericSourceComponentClass, _GenericFilterComponentClass or +# _GenericSinkComponentClass, depending on comp_cls_type. +# +# Acquires a new reference to ptr. + +def _create_component_class_from_ptr_and_get_ref(ptr, comp_cls_type): + return _COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS[comp_cls_type]._create_from_ptr_and_get_ref(ptr) + + +def _trim_docstring(docstring): + lines = docstring.expandtabs().splitlines() + indent = sys.maxsize + + for line in lines[1:]: + stripped = line.lstrip() + + if stripped: + indent = min(indent, len(line) - len(stripped)) + + trimmed = [lines[0].strip()] + + if indent < sys.maxsize: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + + while trimmed and not trimmed[-1]: + trimmed.pop() + + while trimmed and not trimmed[0]: + trimmed.pop(0) + + return '\n'.join(trimmed) + + +# Metaclass for component classes defined by Python code. +# +# The Python user can create a standard Python class which inherits one +# of the three base classes (_UserSourceComponent, _UserFilterComponent, +# or _UserSinkComponent). Those base classes set this class +# (_UserComponentType) as their metaclass. +# +# Once the body of a user-defined component class is executed, this +# metaclass is used to create and initialize the class. The metaclass +# creates a native BT component class of the corresponding type and +# associates it with this user-defined class. The metaclass also defines +# class methods like the `name` and `description` properties to match +# the _GenericComponentClass interface. +# +# The component class name which is used is either: +# +# * The `name` parameter of the class: +# +# class MySink(bt2.SinkComponent, name='my-custom-sink'): +# ... +# +# * If the `name` class parameter is not used: the name of the class +# itself (`MySink` in the example above). +# +# The component class description which is used is the user-defined +# class's docstring: +# +# class MySink(bt2.SinkComponent): +# 'Description goes here' +# ... +# +# A user-defined Python component class can have an __init__() method +# which must at least accept the `params` and `name` arguments: +# +# def __init__(self, params, name, something_else): +# ... +# +# The user-defined component class can also have a _finalize() method +# (do NOT use __del__()) to be notified when the component object is +# finalized. +# +# User-defined source and filter component classes must use the +# `message_iterator_class` class parameter to specify the +# message iterator class to use for this component class: +# +# class MyMessageIterator(bt2._UserMessageIterator): +# ... +# +# class MySource(bt2._UserSourceComponent, +# message_iterator_class=MyMessageIterator): +# ... +# +# This message iterator class must inherit +# bt2._UserMessageIterator, and it must define the _get() and +# _next() methods. The message iterator class can also define an +# __init__() method: this method has access to the original Python +# component object which was used to create it as the `component` +# property. The message iterator class can also define a +# _finalize() method (again, do NOT use __del__()): this is called when +# the message iterator is (really) destroyed. +# +# When the user-defined class is destroyed, this metaclass's __del__() +# method is called: the native BT component class pointer is put (not +# needed anymore, at least not by any Python code since all references +# are dropped for __del__() to be called). +class _UserComponentType(type): + # __new__() is used to catch custom class parameters + def __new__(meta_cls, class_name, bases, attrs, **kwargs): + return super().__new__(meta_cls, class_name, bases, attrs) + + def __init__(cls, class_name, bases, namespace, **kwargs): + super().__init__(class_name, bases, namespace) + + # skip our own bases; they are never directly instantiated by the user + own_bases = ( + '_UserComponent', + '_UserFilterSinkComponent', + '_UserSourceComponent', + '_UserFilterComponent', + '_UserSinkComponent', + ) + + if class_name in own_bases: + return + + comp_cls_name = kwargs.get('name', class_name) + utils._check_str(comp_cls_name) + comp_cls_descr = None + comp_cls_help = None + + if hasattr(cls, '__doc__') and cls.__doc__ is not None: + utils._check_str(cls.__doc__) + docstring = _trim_docstring(cls.__doc__) + lines = docstring.splitlines() + + if len(lines) >= 1: + comp_cls_descr = lines[0] + + if len(lines) >= 3: + comp_cls_help = '\n'.join(lines[2:]) + + iter_cls = kwargs.get('message_iterator_class') + + if _UserSourceComponent in bases: + _UserComponentType._set_iterator_class(cls, iter_cls) + cc_ptr = native_bt.py3_component_class_source_create(cls, + comp_cls_name, + comp_cls_descr, + comp_cls_help) + elif _UserFilterComponent in bases: + _UserComponentType._set_iterator_class(cls, iter_cls) + cc_ptr = native_bt.py3_component_class_filter_create(cls, + comp_cls_name, + comp_cls_descr, + comp_cls_help) + elif _UserSinkComponent in bases: + if not hasattr(cls, '_consume'): + raise bt2.IncompleteUserClass("cannot create component class '{}': missing a _consume() method".format(class_name)) + + cc_ptr = native_bt.py3_component_class_sink_create(cls, + comp_cls_name, + comp_cls_descr, + comp_cls_help) + else: + raise bt2.IncompleteUserClass("cannot find a known component class base in the bases of '{}'".format(class_name)) + + if cc_ptr is None: + raise bt2.CreationError("cannot create component class '{}'".format(class_name)) + + cls._cc_ptr = cc_ptr + + def _init_from_native(cls, comp_ptr, params_ptr): + # create instance, not user-initialized yet + self = cls.__new__(cls) + + # pointer to native self component object (weak/borrowed) + self._ptr = comp_ptr + + # call user's __init__() method + if params_ptr is not None: + params = bt2.value._create_from_ptr_and_get_ref(params_ptr) + else: + params = None + + self.__init__(params) + return self + + def __call__(cls, *args, **kwargs): + raise bt2.Error('cannot directly instantiate a user component from a Python module') + + @staticmethod + def _set_iterator_class(cls, iter_cls): + if iter_cls is None: + raise bt2.IncompleteUserClass("cannot create component class '{}': missing message iterator class".format(cls.__name__)) + + if not issubclass(iter_cls, bt2.message_iterator._UserMessageIterator): + raise bt2.IncompleteUserClass("cannot create component class '{}': message iterator class does not inherit bt2._UserMessageIterator".format(cls.__name__)) + + if not hasattr(iter_cls, '__next__'): + raise bt2.IncompleteUserClass("cannot create component class '{}': message iterator class is missing a __next__() method".format(cls.__name__)) + + cls._iter_cls = iter_cls + + @property + def name(cls): + ptr = cls._as_component_class_ptr(cls._cc_ptr) + return native_bt.component_class_get_name(ptr) + + @property + def description(cls): + ptr = cls._as_component_class_ptr(cls._cc_ptr) + return native_bt.component_class_get_description(ptr) + + @property + def help(cls): + ptr = cls._as_component_class_ptr(cls._cc_ptr) + return native_bt.component_class_get_help(ptr) + + @property + def addr(cls): + return int(cls._cc_ptr) + + def _query_from_native(cls, query_exec_ptr, obj, params_ptr): + # this can raise, in which case the native call to + # bt_component_class_query() returns NULL + if params_ptr is not None: + params = bt2.value._create_from_ptr_and_get_ref(params_ptr) + else: + params = None + + query_exec = bt2.QueryExecutor._create_from_ptr_and_get_ref( + query_exec_ptr) + + # this can raise, but the native side checks the exception + results = cls._query(query_exec, obj, params) + + # this can raise, but the native side checks the exception + results = bt2.create_value(results) + + if results is None: + results_addr = int(native_bt.value_null) + else: + # return new reference + results_addr = int(results._release()) + + return results_addr + + def _query(cls, query_executor, obj, params): + raise NotImplementedError + + def _component_class_ptr(self): + return self._as_component_class_ptr(self._cc_ptr) + + def __del__(cls): + if hasattr(cls, '_cc_ptr'): + cc_ptr = cls._as_component_class_ptr(cls._cc_ptr) + native_bt.component_class_put_ref(cc_ptr) + +# Subclasses must provide these methods or property: +# +# - _as_not_self_specific_component_ptr: static method, must return the passed +# specialized self component pointer (e.g. 'bt_self_component_sink *') as a +# specialized non-self pointer (e.g. 'bt_component_sink *'). +# - _borrow_component_class_ptr: static method, must return a pointer to the +# specialized component class (e.g. 'bt_component_class_sink *') of the +# passed specialized component pointer (e.g. 'bt_component_sink *'). +# - _comp_cls_type: property, one of the native_bt.COMPONENT_CLASS_TYPE_* +# constants. + +class _UserComponent(metaclass=_UserComponentType): + @property + def name(self): + ptr = self._as_not_self_specific_component_ptr(self._ptr) + ptr = self._as_component_ptr(ptr) + name = native_bt.component_get_name(ptr) + assert name is not None + return name + + @property + def cls(self): + comp_ptr = self._as_not_self_specific_component_ptr(self._ptr) + cc_ptr = self._borrow_component_class_ptr(comp_ptr) + return _create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) + + @property + def addr(self): + return int(self._ptr) + + def __init__(self, params=None): + pass + + def _finalize(self): + pass + + def _accept_port_connection(self, port, other_port): + return True + + def _accept_port_connection_from_native(self, self_port_ptr, self_port_type, other_port_ptr): + port = bt2.port._create_self_from_ptr_and_get_ref( + self_port_ptr, self_port_type) + + if self_port_type == native_bt.PORT_TYPE_OUTPUT: + other_port_type = native_bt.PORT_TYPE_INPUT + else: + other_port_type = native_bt.PORT_TYPE_OUTPUT + + other_port = bt2.port._create_from_ptr_and_get_ref( + other_port_ptr, other_port_type) + res = self._accept_port_connection(port, other_port_ptr) + + if type(res) is not bool: + raise TypeError("'{}' is not a 'bool' object") + + return res + + def _port_connected(self, port, other_port): + pass + + def _port_connected_from_native(self, self_port_ptr, self_port_type, other_port_ptr): + port = bt2.port._create_self_from_ptr_and_get_ref( + self_port_ptr, self_port_type) + + if self_port_type == native_bt.PORT_TYPE_OUTPUT: + other_port_type = native_bt.PORT_TYPE_INPUT + else: + other_port_type = native_bt.PORT_TYPE_OUTPUT + + other_port = bt2.port._create_from_ptr_and_get_ref( + other_port_ptr, other_port_type) + self._port_connected(port, other_port) + + def _graph_is_configured_from_native(self): + self._graph_is_configured() + + def _create_trace_class(self, env=None, uuid=None, + assigns_automatic_stream_class_id=True): + ptr = self._as_self_component_ptr(self._ptr) + tc_ptr = native_bt.trace_class_create(ptr) + + if tc_ptr is None: + raise bt2.CreationError('could not create trace class') + + tc = bt2._TraceClass._create_from_ptr(tc_ptr) + + if env is not None: + for key, value in env.items(): + tc.env[key] = value + + if uuid is not None: + tc._uuid = uuid + + tc._assigns_automatic_stream_class_id = assigns_automatic_stream_class_id + + return tc + + def _create_clock_class(self, frequency=None, name=None, description=None, + precision=None, offset=None, origin_is_unix_epoch=True, + uuid=None): + ptr = self._as_self_component_ptr(self._ptr) + cc_ptr = native_bt.clock_class_create(ptr) + + if cc_ptr is None: + raise bt2.CreationError('could not create clock class') + + cc = bt2.clock_class._ClockClass._create_from_ptr(cc_ptr) + + if frequency is not None: + cc._frequency = frequency + + if name is not None: + cc._name = name + + if description is not None: + cc._description = description + + if precision is not None: + cc._precision = precision + + if offset is not None: + cc._offset = offset + + cc._origin_is_unix_epoch = origin_is_unix_epoch + + if uuid is not None: + cc._uuid = uuid + + return cc + + +class _UserSourceComponent(_UserComponent, _SourceComponent): + _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_source_as_component_source) + _as_self_component_ptr = staticmethod(native_bt.self_component_source_as_self_component) + + @property + def _output_ports(self): + def get_output_port_count(self_ptr): + ptr = self._as_not_self_specific_component_ptr(self_ptr) + return native_bt.component_source_get_output_port_count(ptr) + + return _ComponentPorts(self._ptr, + native_bt.self_component_source_borrow_output_port_by_name, + native_bt.self_component_source_borrow_output_port_by_index, + get_output_port_count, + bt2.port._UserComponentOutputPort) + + def _add_output_port(self, name, user_data=None): + utils._check_str(name) + fn = native_bt.self_component_source_add_output_port + comp_status, self_port_ptr = fn(self._ptr, name, user_data) + _handle_component_status(comp_status, + 'cannot add output port to source component object') + assert self_port_ptr is not None + return bt2.port._UserComponentOutputPort._create_from_ptr(self_port_ptr) + + +class _UserFilterComponent(_UserComponent, _FilterComponent): + _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_filter_as_component_filter) + _as_self_component_ptr = staticmethod(native_bt.self_component_filter_as_self_component) + + @property + def _output_ports(self): + def get_output_port_count(self_ptr): + ptr = self._as_not_self_specific_component_ptr(self_ptr) + return native_bt.component_filter_get_output_port_count(ptr) + + return _ComponentPorts(self._ptr, + native_bt.self_component_filter_borrow_output_port_by_name, + native_bt.self_component_filter_borrow_output_port_by_index, + get_output_port_count, + bt2.port._UserComponentOutputPort) + + @property + def _input_ports(self): + def get_input_port_count(self_ptr): + ptr = self._as_not_self_specific_component_ptr(self_ptr) + return native_bt.component_filter_get_input_port_count(ptr) + + return _ComponentPorts(self._ptr, + native_bt.self_component_filter_borrow_input_port_by_name, + native_bt.self_component_filter_borrow_input_port_by_index, + get_input_port_count, + bt2.port._UserComponentInputPort) + + def _add_output_port(self, name, user_data=None): + utils._check_str(name) + fn = native_bt.self_component_filter_add_output_port + comp_status, self_port_ptr = fn(self._ptr, name, user_data) + _handle_component_status(comp_status, + 'cannot add output port to filter component object') + assert self_port_ptr + return bt2.port._UserComponentOutputPort._create_from_ptr(self_port_ptr) + + def _add_input_port(self, name, user_data=None): + utils._check_str(name) + fn = native_bt.self_component_filter_add_input_port + comp_status, self_port_ptr = fn(self._ptr, name, user_data) + _handle_component_status(comp_status, + 'cannot add input port to filter component object') + assert self_port_ptr + return bt2.port._UserComponentInputPort._create_from_ptr(self_port_ptr) + + +class _UserSinkComponent(_UserComponent, _SinkComponent): + _as_not_self_specific_component_ptr = staticmethod(native_bt.self_component_sink_as_component_sink) + _as_self_component_ptr = staticmethod(native_bt.self_component_sink_as_self_component) + + @property + def _input_ports(self): + def get_input_port_count(self_ptr): + ptr = self._as_not_self_specific_component_ptr(self_ptr) + return native_bt.component_sink_get_input_port_count(ptr) + + return _ComponentPorts(self._ptr, + native_bt.self_component_sink_borrow_input_port_by_name, + native_bt.self_component_sink_borrow_input_port_by_index, + get_input_port_count, + bt2.port._UserComponentInputPort) + + def _add_input_port(self, name, user_data=None): + utils._check_str(name) + fn = native_bt.self_component_sink_add_input_port + comp_status, self_port_ptr = fn(self._ptr, name, user_data) + _handle_component_status(comp_status, + 'cannot add input port to sink component object') + assert self_port_ptr + return bt2.port._UserComponentInputPort._create_from_ptr(self_port_ptr) diff --git a/src/bindings/python/bt2/bt2/connection.py b/src/bindings/python/bt2/bt2/connection.py new file mode 100644 index 00000000..4c52a776 --- /dev/null +++ b/src/bindings/python/bt2/bt2/connection.py @@ -0,0 +1,43 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, utils +import bt2.message_iterator +import bt2.port +import bt2 + + +class _Connection(bt2.object._SharedObject): + _get_ref = staticmethod(native_bt.connection_get_ref) + _put_ref = staticmethod(native_bt.connection_put_ref) + + @property + def downstream_port(self): + port_ptr = native_bt.connection_borrow_downstream_port_const(self._ptr) + utils._handle_ptr(port_ptr, "cannot get connection object's downstream port object") + return bt2.port._create_from_ptr_and_get_ref(port_ptr, native_bt.PORT_TYPE_INPUT) + + @property + def upstream_port(self): + port_ptr = native_bt.connection_borrow_upstream_port_const(self._ptr) + utils._handle_ptr(port_ptr, "cannot get connection object's upstream port object") + return bt2.port._create_from_ptr_and_get_ref(port_ptr, native_bt.PORT_TYPE_OUTPUT) diff --git a/src/bindings/python/bt2/bt2/event.py b/src/bindings/python/bt2/bt2/event.py new file mode 100644 index 00000000..4ff398b7 --- /dev/null +++ b/src/bindings/python/bt2/bt2/event.py @@ -0,0 +1,115 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016-2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.clock_class +import bt2.event_class +import bt2.packet +import bt2.stream +import bt2.field +import bt2.clock_snapshot +import bt2 + + +class _Event(object._UniqueObject): + @property + def cls(self): + event_class_ptr = native_bt.event_borrow_class(self._ptr) + assert event_class_ptr is not None + return bt2.event_class._EventClass._create_from_ptr_and_get_ref(event_class_ptr) + + @property + def name(self): + return self.cls.name + + @property + def id(self): + return self.cls.id + + @property + def packet(self): + packet_ptr = native_bt.event_borrow_packet(self._ptr) + assert packet_ptr is not None + return bt2.packet._Packet._create_from_ptr_and_get_ref(packet_ptr) + + @property + def stream(self): + stream_ptr = native_bt.event_borrow_stream(self._ptr) + assert stream_ptr is not None + return bt2._Stream._create_from_ptr_and_get_ref(stream_ptr) + + @property + def common_context_field(self): + field_ptr = native_bt.event_borrow_common_context_field(self._ptr) + + if field_ptr is None: + return + + return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + @property + def specific_context_field(self): + field_ptr = native_bt.event_borrow_specific_context_field(self._ptr) + + if field_ptr is None: + return + + return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + @property + def payload_field(self): + field_ptr = native_bt.event_borrow_payload_field(self._ptr) + + if field_ptr is None: + return + + return bt2.field._create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + def __getitem__(self, key): + utils._check_str(key) + payload_field = self.payload_field + + if payload_field is not None and key in payload_field: + return payload_field[key] + + specific_context_field = self.specific_context_field + + if specific_context_field is not None and key in specific_context_field: + return specific_context_field[key] + + common_context_field = self.common_context_field + + if common_context_field is not None and key in common_context_field: + return common_context_field[key] + + packet_context_field = self.packet.context_field + + if packet_context_field is not None and key in packet_context_field: + return packet_context_field[key] + + raise KeyError(key) diff --git a/src/bindings/python/bt2/bt2/event_class.py b/src/bindings/python/bt2/bt2/event_class.py new file mode 100644 index 00000000..d3141b86 --- /dev/null +++ b/src/bindings/python/bt2/bt2/event_class.py @@ -0,0 +1,171 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.field_class +import bt2.value +import bt2.event +import bt2 + + +class EventClassLogLevel: + EMERGENCY = native_bt.EVENT_CLASS_LOG_LEVEL_EMERGENCY + ALERT = native_bt.EVENT_CLASS_LOG_LEVEL_ALERT + CRITICAL = native_bt.EVENT_CLASS_LOG_LEVEL_CRITICAL + ERROR = native_bt.EVENT_CLASS_LOG_LEVEL_ERROR + WARNING = native_bt.EVENT_CLASS_LOG_LEVEL_WARNING + NOTICE = native_bt.EVENT_CLASS_LOG_LEVEL_NOTICE + INFO = native_bt.EVENT_CLASS_LOG_LEVEL_INFO + DEBUG_SYSTEM = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM + DEBUG_PROGRAM = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM + DEBUG_PROCESS = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS + DEBUG_MODULE = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE + DEBUG_UNIT = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT + DEBUG_FUNCTION = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION + DEBUG_LINE = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_LINE + DEBUG = native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG + + +class _EventClass(object._SharedObject): + _get_ref = staticmethod(native_bt.event_class_get_ref) + _put_ref = staticmethod(native_bt.event_class_put_ref) + + @property + def stream_class(self): + sc_ptr = native_bt.event_class_borrow_stream_class(self._ptr) + + if sc_ptr is not None: + return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(sc_ptr) + + @property + def name(self): + return native_bt.event_class_get_name(self._ptr) + + def _name(self, name): + utils._check_str(name) + return native_bt.event_class_set_name(self._ptr, name) + + _name = property(fset=_name) + + @property + def id(self): + id = native_bt.event_class_get_id(self._ptr) + return id if id >= 0 else None + + @property + def log_level(self): + is_available, log_level = native_bt.event_class_get_log_level(self._ptr) + + if is_available != native_bt.PROPERTY_AVAILABILITY_AVAILABLE: + return None + + return _EVENT_CLASS_LOG_LEVEL_TO_OBJ[log_level] + + def _log_level(self, log_level): + log_levels = ( + EventClassLogLevel.EMERGENCY, + EventClassLogLevel.ALERT, + EventClassLogLevel.CRITICAL, + EventClassLogLevel.ERROR, + EventClassLogLevel.WARNING, + EventClassLogLevel.NOTICE, + EventClassLogLevel.INFO, + EventClassLogLevel.DEBUG_SYSTEM, + EventClassLogLevel.DEBUG_PROGRAM, + EventClassLogLevel.DEBUG_PROCESS, + EventClassLogLevel.DEBUG_MODULE, + EventClassLogLevel.DEBUG_UNIT, + EventClassLogLevel.DEBUG_FUNCTION, + EventClassLogLevel.DEBUG_LINE, + EventClassLogLevel.DEBUG, + ) + + if log_level not in log_levels: + raise ValueError("'{}' is not a valid log level".format(log_level)) + + native_bt.event_class_set_log_level(self._ptr, log_level) + + _log_level = property(fset=_log_level) + + @property + def emf_uri(self): + return native_bt.event_class_get_emf_uri(self._ptr) + + def _emf_uri(self, emf_uri): + utils._check_str(emf_uri) + ret = native_bt.event_class_set_emf_uri(self._ptr, emf_uri) + utils._handle_ret(ret, "cannot set event class object's EMF URI") + + _emf_uri = property(fset=_emf_uri) + + @property + def specific_context_field_class(self): + fc_ptr = native_bt.event_class_borrow_specific_context_field_class_const(self._ptr) + + if fc_ptr is None: + return + + return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) + + def _specific_context_field_class(self, context_field_class): + if context_field_class is not None: + utils._check_type(context_field_class, bt2.field_class._StructureFieldClass) + ret = native_bt.event_class_set_specific_context_field_class(self._ptr, context_field_class._ptr) + utils._handle_ret(ret, "cannot set event class object's context field class") + + _specific_context_field_class = property(fset=_specific_context_field_class) + + @property + def payload_field_class(self): + fc_ptr = native_bt.event_class_borrow_payload_field_class_const(self._ptr) + + if fc_ptr is None: + return + + return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) + + def _payload_field_class(self, payload_field_class): + if payload_field_class is not None: + utils._check_type(payload_field_class, bt2.field_class._StructureFieldClass) + ret = native_bt.event_class_set_payload_field_class(self._ptr, payload_field_class._ptr) + utils._handle_ret(ret, "cannot set event class object's payload field class") + + _payload_field_class = property(fset=_payload_field_class) + + +_EVENT_CLASS_LOG_LEVEL_TO_OBJ = { + native_bt.EVENT_CLASS_LOG_LEVEL_EMERGENCY: EventClassLogLevel.EMERGENCY, + native_bt.EVENT_CLASS_LOG_LEVEL_ALERT: EventClassLogLevel.ALERT, + native_bt.EVENT_CLASS_LOG_LEVEL_CRITICAL: EventClassLogLevel.CRITICAL, + native_bt.EVENT_CLASS_LOG_LEVEL_ERROR: EventClassLogLevel.ERROR, + native_bt.EVENT_CLASS_LOG_LEVEL_WARNING: EventClassLogLevel.WARNING, + native_bt.EVENT_CLASS_LOG_LEVEL_NOTICE: EventClassLogLevel.NOTICE, + native_bt.EVENT_CLASS_LOG_LEVEL_INFO: EventClassLogLevel.INFO, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: EventClassLogLevel.DEBUG_SYSTEM, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: EventClassLogLevel.DEBUG_PROGRAM, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: EventClassLogLevel.DEBUG_PROCESS, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: EventClassLogLevel.DEBUG_MODULE, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: EventClassLogLevel.DEBUG_UNIT, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: EventClassLogLevel.DEBUG_FUNCTION, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: EventClassLogLevel.DEBUG_LINE, + native_bt.EVENT_CLASS_LOG_LEVEL_DEBUG: EventClassLogLevel.DEBUG, +} diff --git a/src/bindings/python/bt2/bt2/field.py b/src/bindings/python/bt2/bt2/field.py new file mode 100644 index 00000000..62dac5ff --- /dev/null +++ b/src/bindings/python/bt2/bt2/field.py @@ -0,0 +1,648 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.field_class +import collections.abc +import functools +import numbers +import math +import bt2 + + +def _create_field_from_ptr(ptr, owner_ptr, owner_get_ref, owner_put_ref): + field_class_ptr = native_bt.field_borrow_class_const(ptr) + utils._handle_ptr(field_class_ptr, "cannot get field object's class") + typeid = native_bt.field_class_get_type(field_class_ptr) + field = _TYPE_ID_TO_OBJ[typeid]._create_from_ptr_and_get_ref( + ptr, owner_ptr, owner_get_ref, owner_put_ref) + return field + + +# Get the "effective" field of `field`. If `field` is a variant, return the +# currently selected field. If `field` is of any other type, return `field` +# directly. + +def _get_leaf_field(field): + if not isinstance(field, _VariantField): + return field + + return _get_leaf_field(field.selected_option) + + +class _Field(object._UniqueObject): + def __eq__(self, other): + other = _get_leaf_field(other) + return self._spec_eq(other) + + @property + def field_class(self): + field_class_ptr = native_bt.field_borrow_class_const(self._ptr) + assert field_class_ptr is not None + return bt2.field_class._create_field_class_from_ptr_and_get_ref(field_class_ptr) + + def _repr(self): + raise NotImplementedError + + def __repr__(self): + return self._repr() + + +@functools.total_ordering +class _NumericField(_Field): + @staticmethod + def _extract_value(other): + if other is True or other is False: + return other + + if isinstance(other, numbers.Integral): + return int(other) + + if isinstance(other, numbers.Real): + return float(other) + + if isinstance(other, numbers.Complex): + return complex(other) + + raise TypeError("'{}' object is not a number object".format(other.__class__.__name__)) + + def __int__(self): + return int(self._value) + + def __float__(self): + return float(self._value) + + def _repr(self): + return repr(self._value) + + def __lt__(self, other): + if not isinstance(other, numbers.Number): + raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__, + other.__class__.__name__)) + + return self._value < float(other) + + def __le__(self, other): + if not isinstance(other, numbers.Number): + raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__, + other.__class__.__name__)) + + return self._value <= float(other) + + def _spec_eq(self, other): + if not isinstance(other, numbers.Number): + return NotImplemented + + return self._value == complex(other) + + def __rmod__(self, other): + return self._extract_value(other) % self._value + + def __mod__(self, other): + return self._value % self._extract_value(other) + + def __rfloordiv__(self, other): + return self._extract_value(other) // self._value + + def __floordiv__(self, other): + return self._value // self._extract_value(other) + + def __round__(self, ndigits=None): + if ndigits is None: + return round(self._value) + else: + return round(self._value, ndigits) + + def __ceil__(self): + return math.ceil(self._value) + + def __floor__(self): + return math.floor(self._value) + + def __trunc__(self): + return int(self._value) + + def __abs__(self): + return abs(self._value) + + def __add__(self, other): + return self._value + self._extract_value(other) + + def __radd__(self, other): + return self.__add__(other) + + def __neg__(self): + return -self._value + + def __pos__(self): + return +self._value + + def __mul__(self, other): + return self._value * self._extract_value(other) + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + return self._value / self._extract_value(other) + + def __rtruediv__(self, other): + return self._extract_value(other) / self._value + + def __pow__(self, exponent): + return self._value ** self._extract_value(exponent) + + def __rpow__(self, base): + return self._extract_value(base) ** self._value + + def __iadd__(self, other): + self.value = self + other + return self + + def __isub__(self, other): + self.value = self - other + return self + + def __imul__(self, other): + self.value = self * other + return self + + def __itruediv__(self, other): + self.value = self / other + return self + + def __ifloordiv__(self, other): + self.value = self // other + return self + + def __imod__(self, other): + self.value = self % other + return self + + def __ipow__(self, other): + self.value = self ** other + return self + + +class _IntegralField(_NumericField, numbers.Integral): + def __lshift__(self, other): + return self._value << self._extract_value(other) + + def __rlshift__(self, other): + return self._extract_value(other) << self._value + + def __rshift__(self, other): + return self._value >> self._extract_value(other) + + def __rrshift__(self, other): + return self._extract_value(other) >> self._value + + def __and__(self, other): + return self._value & self._extract_value(other) + + def __rand__(self, other): + return self._extract_value(other) & self._value + + def __xor__(self, other): + return self._value ^ self._extract_value(other) + + def __rxor__(self, other): + return self._extract_value(other) ^ self._value + + def __or__(self, other): + return self._value | self._extract_value(other) + + def __ror__(self, other): + return self._extract_value(other) | self._value + + def __invert__(self): + return ~self._value + + def __ilshift__(self, other): + self.value = self << other + return self + + def __irshift__(self, other): + self.value = self >> other + return self + + def __iand__(self, other): + self.value = self & other + return self + + def __ixor__(self, other): + self.value = self ^ other + return self + + def __ior__(self, other): + self.value = self | other + return self + + +class _IntegerField(_IntegralField, _Field): + pass + + +class _UnsignedIntegerField(_IntegerField, _Field): + _NAME = 'Unsigned integer' + + def _value_to_int(self, value): + if not isinstance(value, numbers.Real): + raise TypeError('expecting a real number object') + + value = int(value) + utils._check_uint64(value) + + return value + + @property + def _value(self): + return native_bt.field_unsigned_integer_get_value(self._ptr) + + def _set_value(self, value): + value = self._value_to_int(value) + native_bt.field_unsigned_integer_set_value(self._ptr, value) + + value = property(fset=_set_value) + + +class _SignedIntegerField(_IntegerField, _Field): + _NAME = 'Signed integer' + + def _value_to_int(self, value): + if not isinstance(value, numbers.Real): + raise TypeError('expecting a real number object') + + value = int(value) + utils._check_int64(value) + + return value + + @property + def _value(self): + return native_bt.field_signed_integer_get_value(self._ptr) + + def _set_value(self, value): + value = self._value_to_int(value) + native_bt.field_signed_integer_set_value(self._ptr, value) + + value = property(fset=_set_value) + + +class _RealField(_NumericField, numbers.Real): + _NAME = 'Real' + + def _value_to_float(self, value): + if not isinstance(value, numbers.Real): + raise TypeError("expecting a real number object") + + return float(value) + + @property + def _value(self): + return native_bt.field_real_get_value(self._ptr) + + def _set_value(self, value): + value = self._value_to_float(value) + native_bt.field_real_set_value(self._ptr, value) + + value = property(fset=_set_value) + + +class _EnumerationField(_IntegerField): + def _repr(self): + return '{} ({})'.format(self._value, ', '.join(self.labels)) + + @property + def labels(self): + ret, labels = self._get_mapping_labels(self._ptr) + utils._handle_ret(ret, "cannot get label for enumeration field") + + assert labels is not None + return labels + + +class _UnsignedEnumerationField(_EnumerationField, _UnsignedIntegerField): + _NAME = 'Unsigned Enumeration' + _get_mapping_labels = staticmethod(native_bt.field_unsigned_enumeration_get_mapping_labels) + + +class _SignedEnumerationField(_EnumerationField, _SignedIntegerField): + _NAME = 'Signed Enumeration' + _get_mapping_labels = staticmethod(native_bt.field_signed_enumeration_get_mapping_labels) + + +@functools.total_ordering +class _StringField(_Field): + _NAME = 'String' + + def _value_to_str(self, value): + if isinstance(value, self.__class__): + value = value._value + + if not isinstance(value, str): + raise TypeError("expecting a 'str' object") + + return value + + @property + def _value(self): + return native_bt.field_string_get_value(self._ptr) + + def _set_value(self, value): + value = self._value_to_str(value) + native_bt.field_string_set_value(self._ptr, value) + + value = property(fset=_set_value) + + def _spec_eq(self, other): + try: + other = self._value_to_str(other) + except Exception: + return False + + return self._value == other + + def __le__(self, other): + return self._value <= self._value_to_str(other) + + def __lt__(self, other): + return self._value < self._value_to_str(other) + + def __bool__(self): + return bool(self._value) + + def _repr(self): + return repr(self._value) + + def __str__(self): + return str(self._value) + + def __getitem__(self, index): + return self._value[index] + + def __len__(self): + return native_bt.field_string_get_length(self._ptr) + + def __iadd__(self, value): + value = self._value_to_str(value) + ret = native_bt.field_string_append(self._ptr, value) + utils._handle_ret(ret, "cannot append to string field object's value") + return self + + +class _ContainerField(_Field): + def __bool__(self): + return len(self) != 0 + + def __len__(self): + count = self._count() + assert count >= 0 + return count + + def __delitem__(self, index): + raise NotImplementedError + + +class _StructureField(_ContainerField, collections.abc.MutableMapping): + _NAME = 'Structure' + + def _count(self): + return len(self.field_class) + + def __setitem__(self, key, value): + # raises if key is somehow invalid + field = self[key] + + # the field's property does the appropriate conversion or raises + # the appropriate exception + field.value = value + + def __iter__(self): + # same name iterator + return iter(self.field_class) + + def _spec_eq(self, other): + try: + if len(self) != len(other): + return False + + for self_key, self_value in self.items(): + if self_key not in other: + return False + + other_value = other[self_key] + + if self_value != other_value: + return False + + return True + except Exception: + return False + + def _set_value(self, values): + try: + for key, value in values.items(): + self[key].value = value + except Exception: + raise + + value = property(fset=_set_value) + + def _repr(self): + items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] + return '{{{}}}'.format(', '.join(items)) + + def __getitem__(self, key): + utils._check_str(key) + field_ptr = native_bt.field_structure_borrow_member_field_by_name(self._ptr, key) + + if field_ptr is None: + raise KeyError(key) + + return _create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + def member_at_index(self, index): + utils._check_uint64(index) + + if index >= len(self): + raise IndexError + + field_ptr = native_bt.field_structure_borrow_member_field_by_index(self._ptr, index) + assert field_ptr is not None + return _create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + +class _VariantField(_ContainerField, _Field): + _NAME = 'Variant' + + @property + def selected_option_index(self): + return native_bt.field_variant_get_selected_option_field_index(self._ptr) + + @selected_option_index.setter + def selected_option_index(self, index): + native_bt.field_variant_select_option_field(self._ptr, index) + + @property + def selected_option(self): + field_ptr = native_bt.field_variant_borrow_selected_option_field(self._ptr) + utils._handle_ptr(field_ptr, "cannot get variant field's selected option") + + return _create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + def _spec_eq(self, other): + new_self = _get_leaf_field(self) + return new_self == other + + def __bool__(self): + raise NotImplementedError + + def __str__(self): + return str(self.selected_option) + + def _repr(self): + return repr(self.selected_option) + + def _set_value(self, value): + self.selected_option.value = value + + value = property(fset=_set_value) + + +class _ArrayField(_ContainerField, _Field): + + def _get_length(self): + return native_bt.field_array_get_length(self._ptr) + + length = property(fget=_get_length) + + def __getitem__(self, index): + if not isinstance(index, numbers.Integral): + raise TypeError("'{}' is not an integral number object: invalid index".format(index.__class__.__name__)) + + index = int(index) + + if index < 0 or index >= len(self): + raise IndexError('{} field object index is out of range'.format(self._NAME)) + + field_ptr = native_bt.field_array_borrow_element_field_by_index(self._ptr, index) + assert(field_ptr) + return _create_field_from_ptr(field_ptr, self._owner_ptr, + self._owner_get_ref, + self._owner_put_ref) + + def __setitem__(self, index, value): + # we can only set numbers and strings + if not isinstance(value, (numbers.Number, _StringField, str)): + raise TypeError('expecting number or string object') + + # raises if index is somehow invalid + field = self[index] + + if not isinstance(field, (_NumericField, _StringField)): + raise TypeError('can only set the value of a number or string field') + + # the field's property does the appropriate conversion or raises + # the appropriate exception + field.value = value + + def insert(self, index, value): + raise NotImplementedError + + def _spec_eq(self, other): + try: + if len(self) != len(other): + return False + + for self_field, other_field in zip(self, other): + if self_field != other_field: + return False + + return True + except Exception: + return False + + def _repr(self): + return '[{}]'.format(', '.join([repr(v) for v in self])) + + +class _StaticArrayField(_ArrayField, _Field): + _NAME = 'Static array' + + def _count(self): + return native_bt.field_array_get_length(self._ptr) + + def _set_value(self, values): + if len(self) != len(values): + raise ValueError( + 'expected length of value and array field to match') + + for index, value in enumerate(values): + if value is not None: + self[index].value = value + + value = property(fset=_set_value) + + +class _DynamicArrayField(_ArrayField, _Field): + _NAME = 'Dynamic array' + + def _count(self): + return self.length + + def _set_length(self, length): + utils._check_uint64(length) + ret = native_bt.field_dynamic_array_set_length(self._ptr, length) + utils._handle_ret(ret, "cannot set dynamic array length") + + length = property(fget=_ArrayField._get_length, fset=_set_length) + + def _set_value(self, values): + if len(values) != self.length: + self.length = len(values) + + for index, value in enumerate(values): + if value is not None: + self[index].value = value + + value = property(fset=_set_value) + + +_TYPE_ID_TO_OBJ = { + native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerField, + native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerField, + native_bt.FIELD_CLASS_TYPE_REAL: _RealField, + native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationField, + native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationField, + native_bt.FIELD_CLASS_TYPE_STRING: _StringField, + native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureField, + native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayField, + native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayField, + native_bt.FIELD_CLASS_TYPE_VARIANT: _VariantField, +} diff --git a/src/bindings/python/bt2/bt2/field_class.py b/src/bindings/python/bt2/bt2/field_class.py new file mode 100644 index 00000000..27a37d96 --- /dev/null +++ b/src/bindings/python/bt2/bt2/field_class.py @@ -0,0 +1,397 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import collections.abc +import bt2.field +import bt2.field_path +import bt2 + + +def _create_field_class_from_ptr_and_get_ref(ptr): + typeid = native_bt.field_class_get_type(ptr) + return _FIELD_CLASS_TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) + + +class IntegerDisplayBase: + BINARY = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY + OCTAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL + DECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL + HEXADECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL + + +class _FieldClass(object._SharedObject): + _get_ref = staticmethod(native_bt.field_class_get_ref) + _put_ref = staticmethod(native_bt.field_class_put_ref) + + def _check_create_status(self, ptr): + if ptr is None: + raise bt2.CreationError('cannot create {} field class object'.format(self._NAME.lower())) + + +class _IntegerFieldClass(_FieldClass): + @property + def field_value_range(self): + size = native_bt.field_class_integer_get_field_value_range(self._ptr) + assert(size >= 1) + return size + + def _field_value_range(self, size): + if size < 1 or size > 64: + raise ValueError("Value is outside valid range [1, 64] ({})".format(size)) + native_bt.field_class_integer_set_field_value_range(self._ptr, size) + + _field_value_range = property(fset=_field_value_range) + + @property + def preferred_display_base(self): + base = native_bt.field_class_integer_get_preferred_display_base( + self._ptr) + assert(base >= 0) + return base + + def _preferred_display_base(self, base): + utils._check_uint64(base) + + if base not in (IntegerDisplayBase.BINARY, + IntegerDisplayBase.OCTAL, + IntegerDisplayBase.DECIMAL, + IntegerDisplayBase.HEXADECIMAL): + raise ValueError("Display base is not a valid IntegerDisplayBase value") + + native_bt.field_class_integer_set_preferred_display_base( + self._ptr, base) + + _preferred_display_base = property(fset=_preferred_display_base) + + +class _UnsignedIntegerFieldClass(_IntegerFieldClass): + _NAME = 'Unsigned integer' + + +class _SignedIntegerFieldClass(_IntegerFieldClass): + _NAME = 'Signed integer' + + +class _RealFieldClass(_FieldClass): + _NAME = 'Real' + + @property + def is_single_precision(self): + return native_bt.field_class_real_is_single_precision(self._ptr) + + def _is_single_precision(self, is_single_precision): + utils._check_bool(is_single_precision) + native_bt.field_class_real_set_is_single_precision( + self._ptr, is_single_precision) + + _is_single_precision = property(fset=_is_single_precision) + + +class _EnumerationFieldClassMappingRange: + def __init__(self, lower, upper): + self._lower = lower + self._upper = upper + + @property + def lower(self): + return self._lower + + @property + def upper(self): + return self._upper + + def __eq__(self, other): + return self.lower == other.lower and self.upper == other.upper + + +class _EnumerationFieldClassMapping(collections.abc.Set): + def __init__(self, mapping_ptr): + self._mapping_ptr = mapping_ptr + + @property + def label(self): + mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr) + label = native_bt.field_class_enumeration_mapping_get_label(mapping_ptr) + assert label is not None + return label + + def __len__(self): + mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr) + return native_bt.field_class_enumeration_mapping_get_range_count(mapping_ptr) + + def __contains__(self, other_range): + for curr_range in self: + if curr_range == other_range: + return True + return False + + def __iter__(self): + for idx in range(len(self)): + lower, upper = self._get_range_by_index(self._mapping_ptr, idx) + yield _EnumerationFieldClassMappingRange(lower, upper) + + +class _UnsignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping): + _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_as_mapping_const) + _get_range_by_index = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_get_range_by_index) + + +class _SignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping): + _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_signed_enumeration_mapping_as_mapping_const) + _get_range_by_index = staticmethod(native_bt.field_class_signed_enumeration_mapping_get_range_by_index) + + +class _EnumerationFieldClass(_IntegerFieldClass, collections.abc.Mapping): + def __len__(self): + count = native_bt.field_class_enumeration_get_mapping_count(self._ptr) + assert(count >= 0) + return count + + def map_range(self, label, lower, upper=None): + utils._check_str(label) + + if upper is None: + upper = lower + + ret = self._map_range(self._ptr, label, lower, upper) + utils._handle_ret(ret, "cannot add mapping to enumeration field class object") + + def labels_by_value(self, value): + ret, labels = self._get_mapping_labels_by_value(self._ptr, value) + utils._handle_ret(ret, "cannot get mapping labels") + return labels + + def __iter__(self): + for idx in range(len(self)): + mapping = self._get_mapping_by_index(self._ptr, idx) + yield mapping.label + + def __getitem__(self, key): + utils._check_str(key) + for idx in range(len(self)): + mapping = self._get_mapping_by_index(self._ptr, idx) + if mapping.label == key: + return mapping + + raise KeyError(key) + + def __iadd__(self, mappings): + for mapping in mappings.values(): + for range in mapping: + self.map_range(mapping.label, range.lower, range.upper) + + return self + + +class _UnsignedEnumerationFieldClass(_EnumerationFieldClass, _UnsignedIntegerFieldClass): + _NAME = 'Unsigned enumeration' + + @staticmethod + def _get_mapping_by_index(enum_ptr, index): + mapping_ptr = native_bt.field_class_unsigned_enumeration_borrow_mapping_by_index_const(enum_ptr, index) + assert mapping_ptr is not None + return _UnsignedEnumerationFieldClassMapping(mapping_ptr) + + @staticmethod + def _map_range(enum_ptr, label, lower, upper): + utils._check_uint64(lower) + utils._check_uint64(upper) + return native_bt.field_class_unsigned_enumeration_map_range(enum_ptr, label, lower, upper) + + @staticmethod + def _get_mapping_labels_by_value(enum_ptr, value): + utils._check_uint64(value) + return native_bt.field_class_unsigned_enumeration_get_mapping_labels_by_value(enum_ptr, value) + + +class _SignedEnumerationFieldClass(_EnumerationFieldClass, _SignedIntegerFieldClass): + _NAME = 'Signed enumeration' + + @staticmethod + def _get_mapping_by_index(enum_ptr, index): + mapping_ptr = native_bt.field_class_signed_enumeration_borrow_mapping_by_index_const(enum_ptr, index) + assert mapping_ptr is not None + return _SignedEnumerationFieldClassMapping(mapping_ptr) + + @staticmethod + def _map_range(enum_ptr, label, lower, upper): + utils._check_int64(lower) + utils._check_int64(upper) + return native_bt.field_class_signed_enumeration_map_range(enum_ptr, label, lower, upper) + + @staticmethod + def _get_mapping_labels_by_value(enum_ptr, value): + utils._check_int64(value) + return native_bt.field_class_signed_enumeration_get_mapping_labels_by_value(enum_ptr, value) + + +class _StringFieldClass(_FieldClass): + _NAME = 'String' + + +class _FieldContainer(collections.abc.Mapping): + def __len__(self): + count = self._get_element_count(self._ptr) + assert count >= 0 + return count + + def __getitem__(self, key): + if not isinstance(key, str): + raise TypeError("key should be a 'str' object, got {}".format(key.__class__.__name__)) + + ptr = self._borrow_field_class_ptr_by_name(key) + + if ptr is None: + raise KeyError(key) + + return _create_field_class_from_ptr_and_get_ref(ptr) + + def _borrow_field_class_ptr_by_name(self, key): + element_ptr = self._borrow_element_by_name(self._ptr, key) + if element_ptr is None: + return + + return self._element_borrow_field_class(element_ptr) + + def __iter__(self): + for idx in range(len(self)): + element_ptr = self._borrow_element_by_index(self._ptr, idx) + assert element_ptr is not None + + yield self._element_get_name(element_ptr) + + def _append_element_common(self, name, field_class): + utils._check_str(name) + utils._check_type(field_class, _FieldClass) + ret = self._append_element(self._ptr, name, field_class._ptr) + utils._handle_ret(ret, "cannot add field to {} field class object".format(self._NAME.lower())) + + def __iadd__(self, fields): + for name, field_class in fields.items(): + self._append_element_common(name, field_class) + + return self + + def _at_index(self, index): + utils._check_uint64(index) + + if index < 0 or index >= len(self): + raise IndexError + + element_ptr = self._borrow_element_by_index(self._ptr, index) + assert element_ptr is not None + + field_class_ptr = self._element_borrow_field_class(element_ptr) + + return _create_field_class_from_ptr_and_get_ref(field_class_ptr) + + +class _StructureFieldClass(_FieldClass, _FieldContainer): + _NAME = 'Structure' + _borrow_element_by_index = staticmethod(native_bt.field_class_structure_borrow_member_by_index_const) + _borrow_element_by_name = staticmethod(native_bt.field_class_structure_borrow_member_by_name_const) + _element_get_name = staticmethod(native_bt.field_class_structure_member_get_name) + _element_borrow_field_class = staticmethod(native_bt.field_class_structure_member_borrow_field_class_const) + _get_element_count = staticmethod(native_bt.field_class_structure_get_member_count) + _append_element = staticmethod(native_bt.field_class_structure_append_member) + + def append_member(self, name, field_class): + return self._append_element_common(name, field_class) + + def member_at_index(self, index): + return self._at_index(index) + + +class _VariantFieldClass(_FieldClass, _FieldContainer): + _NAME = 'Variant' + _borrow_element_by_index = staticmethod(native_bt.field_class_variant_borrow_option_by_index_const) + _borrow_element_by_name = staticmethod(native_bt.field_class_variant_borrow_option_by_name_const) + _element_get_name = staticmethod(native_bt.field_class_variant_option_get_name) + _element_borrow_field_class = staticmethod(native_bt.field_class_variant_option_borrow_field_class_const) + _get_element_count = staticmethod(native_bt.field_class_variant_get_option_count) + _append_element = staticmethod(native_bt.field_class_variant_append_option) + + def append_option(self, name, field_class): + return self._append_element_common(name, field_class) + + def option_at_index(self, index): + return self._at_index(index) + + @property + def selector_field_path(self): + ptr = native_bt.field_class_variant_borrow_selector_field_path_const(self._ptr) + if ptr is None: + return + + return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr) + + def _set_selector_field_class(self, selector_fc): + utils._check_type(selector_fc, bt2.field_class._EnumerationFieldClass) + ret = native_bt.field_class_variant_set_selector_field_class(self._ptr, selector_fc._ptr) + utils._handle_ret(ret, "cannot set variant selector field type") + + _selector_field_class = property(fset=_set_selector_field_class) + + +class _ArrayFieldClass(_FieldClass): + @property + def element_field_class(self): + elem_fc_ptr = native_bt.field_class_array_borrow_element_field_class_const(self._ptr) + return _create_field_class_from_ptr_and_get_ref(elem_fc_ptr) + + +class _StaticArrayFieldClass(_ArrayFieldClass): + @property + def length(self): + return native_bt.field_class_static_array_get_length(self._ptr) + + +class _DynamicArrayFieldClass(_ArrayFieldClass): + @property + def length_field_path(self): + ptr = native_bt.field_class_dynamic_array_borrow_length_field_path_const(self._ptr) + if ptr is None: + return + + return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr) + + def _set_length_field_class(self, length_fc): + utils._check_type(length_fc, _UnsignedIntegerFieldClass) + ret = native_bt.field_class_dynamic_array_set_length_field_class(self._ptr, length_fc._ptr) + utils._handle_ret(ret, "cannot set dynamic array length field type") + + _length_field_class = property(fset=_set_length_field_class) + + +_FIELD_CLASS_TYPE_TO_OBJ = { + native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerFieldClass, + native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerFieldClass, + native_bt.FIELD_CLASS_TYPE_REAL: _RealFieldClass, + native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationFieldClass, + native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationFieldClass, + native_bt.FIELD_CLASS_TYPE_STRING: _StringFieldClass, + native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureFieldClass, + native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayFieldClass, + native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayFieldClass, + native_bt.FIELD_CLASS_TYPE_VARIANT: _VariantFieldClass, +} diff --git a/src/bindings/python/bt2/bt2/field_path.py b/src/bindings/python/bt2/bt2/field_path.py new file mode 100644 index 00000000..2191171e --- /dev/null +++ b/src/bindings/python/bt2/bt2/field_path.py @@ -0,0 +1,82 @@ +# The MIT License (MIT) +# +# Copyright (c) 2018 Francis Deslauriers +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import collections +from bt2 import native_bt, object + + +class Scope: + PACKET_CONTEXT = native_bt.SCOPE_PACKET_CONTEXT + EVENT_COMMON_CONTEXT = native_bt.SCOPE_EVENT_COMMON_CONTEXT + EVENT_SPECIFIC_CONTEXT = native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT + EVENT_PAYLOAD = native_bt.SCOPE_EVENT_PAYLOAD + + +class _FieldPathItem: + pass + + +class _IndexFieldPathItem(_FieldPathItem): + def __init__(self, index): + self._index = index + + @property + def index(self): + return self._index + + +class _CurrentArrayElementFieldPathItem(_FieldPathItem): + pass + + +class _FieldPath(object._SharedObject, collections.abc.Iterable): + _get_ref = staticmethod(native_bt.field_path_get_ref) + _put_ref = staticmethod(native_bt.field_path_put_ref) + + @property + def root_scope(self): + scope = native_bt.field_path_get_root_scope(self._ptr) + return _SCOPE_TO_OBJ[scope] + + def __len__(self): + return native_bt.field_path_get_item_count(self._ptr) + + def __iter__(self): + for idx in range(len(self)): + item_ptr = native_bt.field_path_borrow_item_by_index_const(self._ptr, idx) + assert item_ptr is not None + item_type = native_bt.field_path_item_get_type(item_ptr) + if item_type == native_bt.FIELD_PATH_ITEM_TYPE_INDEX: + idx = native_bt.field_path_item_index_get_index(item_ptr) + yield _IndexFieldPathItem(idx) + elif item_type == native_bt.FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: + yield _CurrentArrayElementFieldPathItem() + else: + assert False + + +_SCOPE_TO_OBJ = { + native_bt.SCOPE_PACKET_CONTEXT: Scope.PACKET_CONTEXT, + native_bt.SCOPE_EVENT_COMMON_CONTEXT: Scope.EVENT_COMMON_CONTEXT, + native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT: Scope.EVENT_SPECIFIC_CONTEXT, + native_bt.SCOPE_EVENT_PAYLOAD: Scope.EVENT_PAYLOAD +} diff --git a/src/bindings/python/bt2/bt2/graph.py b/src/bindings/python/bt2/bt2/graph.py new file mode 100644 index 00000000..998d310b --- /dev/null +++ b/src/bindings/python/bt2/bt2/graph.py @@ -0,0 +1,179 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.connection +import bt2.component +import functools +import bt2.port +import bt2 + + +def _graph_port_added_listener_from_native(user_listener, + component_ptr, component_type, + port_ptr, port_type): + component = bt2.component._create_component_from_ptr_and_get_ref(component_ptr, component_type) + port = bt2.port._create_from_ptr_and_get_ref(port_ptr, port_type) + user_listener(component, port) + + +def _graph_ports_connected_listener_from_native(user_listener, + upstream_component_ptr, upstream_component_type, + upstream_port_ptr, + downstream_component_ptr, downstream_component_type, + downstream_port_ptr): + upstream_component = bt2.component._create_component_from_ptr_and_get_ref( + upstream_component_ptr, upstream_component_type) + upstream_port = bt2.port._create_from_ptr_and_get_ref( + upstream_port_ptr, native_bt.PORT_TYPE_OUTPUT) + downstream_component = bt2.component._create_component_from_ptr_and_get_ref( + downstream_component_ptr, downstream_component_type) + downstream_port = bt2.port._create_from_ptr_and_get_ref( + downstream_port_ptr, native_bt.PORT_TYPE_INPUT) + user_listener(upstream_component, upstream_port, downstream_component, downstream_port) + + +class Graph(object._SharedObject): + _get_ref = staticmethod(native_bt.graph_get_ref) + _put_ref = staticmethod(native_bt.graph_put_ref) + + def __init__(self): + ptr = native_bt.graph_create() + + if ptr is None: + raise bt2.CreationError('cannot create graph object') + + super().__init__(ptr) + + def _handle_status(self, status, gen_error_msg): + if status == native_bt.GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + raise bt2.PortConnectionRefused + elif status == native_bt.GRAPH_STATUS_CANCELED: + raise bt2.GraphCanceled + elif status == native_bt.GRAPH_STATUS_END: + raise bt2.Stop + elif status == native_bt.GRAPH_STATUS_AGAIN: + raise bt2.TryAgain + elif status < 0: + raise bt2.Error(gen_error_msg) + + def add_component(self, component_class, name, params=None): + if isinstance(component_class, bt2.component._GenericSourceComponentClass): + cc_ptr = component_class._ptr + add_fn = native_bt.graph_add_source_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE + elif isinstance(component_class, bt2.component._GenericFilterComponentClass): + cc_ptr = component_class._ptr + add_fn = native_bt.graph_add_filter_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_FILTER + elif isinstance(component_class, bt2.component._GenericSinkComponentClass): + cc_ptr = component_class._ptr + add_fn = native_bt.graph_add_sink_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_SINK + elif issubclass(component_class, bt2.component._UserSourceComponent): + cc_ptr = component_class._cc_ptr + add_fn = native_bt.graph_add_source_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE + elif issubclass(component_class, bt2.component._UserSinkComponent): + cc_ptr = component_class._cc_ptr + add_fn = native_bt.graph_add_sink_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_SINK + elif issubclass(component_class, bt2.component._UserFilterComponent): + cc_ptr = component_class._cc_ptr + add_fn = native_bt.graph_add_filter_component + cc_type = native_bt.COMPONENT_CLASS_TYPE_FILTER + else: + raise TypeError("'{}' is not a component class".format( + component_class.__class__.__name__)) + + utils._check_str(name) + params = bt2.create_value(params) + + params_ptr = params._ptr if params is not None else None + + status, comp_ptr = add_fn(self._ptr, cc_ptr, name, params_ptr) + self._handle_status(status, 'cannot add component to graph') + assert comp_ptr + return bt2.component._create_component_from_ptr(comp_ptr, cc_type) + + def connect_ports(self, upstream_port, downstream_port): + utils._check_type(upstream_port, bt2.port._OutputPort) + utils._check_type(downstream_port, bt2.port._InputPort) + status, conn_ptr = native_bt.graph_connect_ports(self._ptr, + upstream_port._ptr, + downstream_port._ptr) + self._handle_status(status, 'cannot connect component ports within graph') + assert(conn_ptr) + return bt2.connection._Connection._create_from_ptr(conn_ptr) + + def add_port_added_listener(self, listener): + if not callable(listener): + raise TypeError("'listener' parameter is not callable") + + fn = native_bt.py3_graph_add_port_added_listener + listener_from_native = functools.partial(_graph_port_added_listener_from_native, + listener) + + listener_ids = fn(self._ptr, listener_from_native) + if listener_ids is None: + utils._raise_bt2_error('cannot add listener to graph object') + return bt2._ListenerHandle(listener_ids, self) + + def add_ports_connected_listener(self, listener): + if not callable(listener): + raise TypeError("'listener' parameter is not callable") + + fn = native_bt.py3_graph_add_ports_connected_listener + listener_from_native = functools.partial(_graph_ports_connected_listener_from_native, + listener) + + listener_ids = fn(self._ptr, listener_from_native) + if listener_ids is None: + utils._raise_bt2_error('cannot add listener to graph object') + return bt2._ListenerHandle(listener_ids, self) + + def run(self): + status = native_bt.graph_run(self._ptr) + + if status == native_bt.GRAPH_STATUS_END: + return + + self._handle_status(status, 'graph object stopped running because of an unexpected error') + + def cancel(self): + status = native_bt.graph_cancel(self._ptr) + self._handle_status(status, 'cannot cancel graph object') + + @property + def is_canceled(self): + is_canceled = native_bt.graph_is_canceled(self._ptr) + assert(is_canceled >= 0) + return is_canceled > 0 + + def create_output_port_message_iterator(self, output_port): + utils._check_type(output_port, bt2.port._OutputPort) + msg_iter_ptr = native_bt.port_output_message_iterator_create(self._ptr, output_port._ptr) + + if msg_iter_ptr is None: + raise bt2.CreationError('cannot create output port message iterator') + + return bt2.message_iterator._OutputPortMessageIterator(msg_iter_ptr) diff --git a/src/bindings/python/bt2/bt2/logging.c b/src/bindings/python/bt2/bt2/logging.c new file mode 100644 index 00000000..a9738039 --- /dev/null +++ b/src/bindings/python/bt2/bt2/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_python_bindings_bt2_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_python_bindings_bt2_log_level, + "BABELTRACE_PYTHON_BT2_LOG_LEVEL"); diff --git a/src/bindings/python/bt2/bt2/logging.h b/src/bindings/python/bt2/bt2/logging.h new file mode 100644 index 00000000..5f98158d --- /dev/null +++ b/src/bindings/python/bt2/bt2/logging.h @@ -0,0 +1,31 @@ +#ifndef BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H +#define BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_python_bindings_bt2_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_python_bindings_bt2_log_level); + +#endif /* BABELTRACE_BINDINGS_PYTHON_BT2_LOGGING_H */ diff --git a/src/bindings/python/bt2/bt2/logging.py b/src/bindings/python/bt2/bt2/logging.py new file mode 100644 index 00000000..51d898cc --- /dev/null +++ b/src/bindings/python/bt2/bt2/logging.py @@ -0,0 +1,59 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2 + + +class LoggingLevel: + VERBOSE = native_bt.LOGGING_LEVEL_VERBOSE + DEBUG = native_bt.LOGGING_LEVEL_DEBUG + INFO = native_bt.LOGGING_LEVEL_INFO + WARN = native_bt.LOGGING_LEVEL_WARN + ERROR = native_bt.LOGGING_LEVEL_ERROR + FATAL = native_bt.LOGGING_LEVEL_FATAL + NONE = native_bt.LOGGING_LEVEL_NONE + + +def get_minimal_logging_level(): + return native_bt.logging_get_minimal_level() + + +def get_global_logging_level(): + return native_bt.logging_get_global_level() + + +def set_global_logging_level(level): + levels = ( + LoggingLevel.VERBOSE, + LoggingLevel.DEBUG, + LoggingLevel.INFO, + LoggingLevel.WARN, + LoggingLevel.ERROR, + LoggingLevel.FATAL, + LoggingLevel.NONE, + ) + + if level not in levels: + raise TypeError("'{}' is not a valid logging level".format(level)) + + return native_bt.logging_set_global_level(level) diff --git a/src/bindings/python/bt2/bt2/message.py b/src/bindings/python/bt2/bt2/message.py new file mode 100644 index 00000000..56b545da --- /dev/null +++ b/src/bindings/python/bt2/bt2/message.py @@ -0,0 +1,236 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.clock_snapshot +import bt2.packet +import bt2.stream +import bt2.event +import bt2 + + +def _create_from_ptr(ptr): + msg_type = native_bt.message_get_type(ptr) + + if msg_type not in _MESSAGE_TYPE_TO_CLS: + raise bt2.Error('unknown message type: {}'.format(msg_type)) + + return _MESSAGE_TYPE_TO_CLS[msg_type]._create_from_ptr(ptr) + + +class _Message(object._SharedObject): + _get_ref = staticmethod(native_bt.message_get_ref) + _put_ref = staticmethod(native_bt.message_put_ref) + + @staticmethod + def _check_has_default_clock_class(clock_class): + if clock_class is None: + raise bt2.NonexistentClockSnapshot('cannot get default clock snapshot: stream class has no default clock class') + + +class _MessageWithDefaultClockSnapshot: + def _get_default_clock_snapshot(self, borrow_clock_snapshot_ptr): + snapshot_ptr = borrow_clock_snapshot_ptr(self._ptr) + + return bt2.clock_snapshot._ClockSnapshot._create_from_ptr_and_get_ref( + snapshot_ptr, self._ptr, self._get_ref, self._put_ref) + + +class _EventMessage(_Message, _MessageWithDefaultClockSnapshot): + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_event_borrow_default_clock_snapshot_const) + + @property + def default_clock_snapshot(self): + self._check_has_default_clock_class(self.event.packet.stream.cls.default_clock_class) + return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) + + @property + def event(self): + event_ptr = native_bt.message_event_borrow_event(self._ptr) + assert event_ptr is not None + return bt2.event._Event._create_from_ptr_and_get_ref( + event_ptr, self._ptr, self._get_ref, self._put_ref) + + +class _PacketMessage(_Message, _MessageWithDefaultClockSnapshot): + @property + def default_clock_snapshot(self): + self._check_has_default_clock_class(self.packet.stream.cls.default_clock_class) + return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) + + @property + def packet(self): + packet_ptr = self._borrow_packet_ptr(self._ptr) + assert packet_ptr is not None + return bt2.packet._Packet._create_from_ptr_and_get_ref(packet_ptr) + + +class _PacketBeginningMessage(_PacketMessage): + _borrow_packet_ptr = staticmethod(native_bt.message_packet_beginning_borrow_packet) + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_packet_beginning_borrow_default_clock_snapshot_const) + + +class _PacketEndMessage(_PacketMessage): + _borrow_packet_ptr = staticmethod(native_bt.message_packet_end_borrow_packet) + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_packet_end_borrow_default_clock_snapshot_const) + + +class _StreamMessage(_Message): + @property + def stream(self): + stream_ptr = self._borrow_stream_ptr(self._ptr) + assert stream_ptr + return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) + + +class _StreamBeginningMessage(_StreamMessage): + _borrow_stream_ptr = staticmethod(native_bt.message_stream_beginning_borrow_stream) + + +class _StreamEndMessage(_StreamMessage): + _borrow_stream_ptr = staticmethod(native_bt.message_stream_end_borrow_stream) + + +class _StreamActivityMessage(_Message): + @property + def default_clock_snapshot(self): + self._check_has_default_clock_class(self.stream.cls.default_clock_class) + status, snapshot_ptr = self._borrow_default_clock_snapshot_ptr(self._ptr) + + if status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: + snapshot_type = bt2.clock_snapshot._ClockSnapshot + elif status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: + snapshot_type = bt2.clock_snapshot._UnknownClockSnapshot + elif status == native_bt.MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: + snapshot_type = bt2.clock_snapshot._InfiniteClockSnapshot + else: + raise bt2.Error('cannot borrow default clock snapshot from message') + + assert snapshot_ptr is not None + + return snapshot_type._create_from_ptr_and_get_ref( + snapshot_ptr, self._ptr, self._get_ref, self._put_ref) + + def _default_clock_snapshot(self, value): + self._set_default_clock_snapshot_ptr(self._ptr, value) + + _default_clock_snapshot = property(fset=_default_clock_snapshot) + + @property + def stream(self): + stream_ptr = self._borrow_stream_ptr(self._ptr) + assert stream_ptr + return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) + + +class _StreamActivityBeginningMessage(_StreamActivityMessage): + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_beginning_borrow_default_clock_snapshot_const) + _set_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_beginning_set_default_clock_snapshot) + _borrow_stream_ptr = staticmethod(native_bt.message_stream_activity_beginning_borrow_stream) + + +class _StreamActivityEndMessage(_StreamActivityMessage): + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_end_borrow_default_clock_snapshot_const) + _set_default_clock_snapshot_ptr = staticmethod(native_bt.message_stream_activity_end_set_default_clock_snapshot) + _borrow_stream_ptr = staticmethod(native_bt.message_stream_activity_end_borrow_stream) + + +class _MessageIteratorInactivityMessage(_Message, _MessageWithDefaultClockSnapshot): + _borrow_default_clock_snapshot_ptr = staticmethod(native_bt.message_message_iterator_inactivity_borrow_default_clock_snapshot_const) + + @property + def default_clock_snapshot(self): + # This kind of message always has a default clock class: no + # need to call self._check_has_default_clock_class() here. + return self._get_default_clock_snapshot(self._borrow_default_clock_snapshot_ptr) + + +class _DiscardedMessage(_Message, _MessageWithDefaultClockSnapshot): + @property + def stream(self): + stream_ptr = self._borrow_stream_ptr(self._ptr) + assert stream_ptr + return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) + + @property + def count(self): + avail, count = self._get_count(self._ptr) + if avail is native_bt.PROPERTY_AVAILABILITY_AVAILABLE: + return count + + def _set_count(self, count): + utils._check_uint64(count) + self._set_count(self._ptr, count) + + _count = property(fset=_set_count) + + def _check_has_default_clock_snapshots(self): + if not self._has_default_clock_snapshots: + raise bt2.NonexistentClockSnapshot('cannot get default clock snapshot: such a message has no clock snapshots for this stream class') + + @property + def beginning_default_clock_snapshot(self): + self._check_has_default_clock_snapshots() + return self._get_default_clock_snapshot(self._borrow_beginning_clock_snapshot_ptr) + + @property + def end_default_clock_snapshot(self): + self._check_has_default_clock_snapshots() + return self._get_default_clock_snapshot(self._borrow_end_clock_snapshot_ptr) + + +class _DiscardedEventsMessage(_DiscardedMessage): + _borrow_stream_ptr = staticmethod(native_bt.message_discarded_events_borrow_stream_const) + _get_count = staticmethod(native_bt.message_discarded_events_get_count) + _set_count = staticmethod(native_bt.message_discarded_events_set_count) + _borrow_beginning_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_events_borrow_beginning_default_clock_snapshot_const) + _borrow_end_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_events_borrow_end_default_clock_snapshot_const) + + @property + def _has_default_clock_snapshots(self): + return self.stream.cls.discarded_events_have_default_clock_snapshots + + +class _DiscardedPacketsMessage(_DiscardedMessage): + _borrow_stream_ptr = staticmethod(native_bt.message_discarded_packets_borrow_stream_const) + _get_count = staticmethod(native_bt.message_discarded_packets_get_count) + _set_count = staticmethod(native_bt.message_discarded_packets_set_count) + _borrow_beginning_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_packets_borrow_beginning_default_clock_snapshot_const) + _borrow_end_clock_snapshot_ptr = staticmethod(native_bt.message_discarded_packets_borrow_end_default_clock_snapshot_const) + + @property + def _has_default_clock_snapshots(self): + return self.stream.cls.discarded_packets_have_default_clock_snapshots + + +_MESSAGE_TYPE_TO_CLS = { + native_bt.MESSAGE_TYPE_EVENT: _EventMessage, + native_bt.MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: _MessageIteratorInactivityMessage, + native_bt.MESSAGE_TYPE_STREAM_BEGINNING: _StreamBeginningMessage, + native_bt.MESSAGE_TYPE_STREAM_END: _StreamEndMessage, + native_bt.MESSAGE_TYPE_PACKET_BEGINNING: _PacketBeginningMessage, + native_bt.MESSAGE_TYPE_PACKET_END: _PacketEndMessage, + native_bt.MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: _StreamActivityBeginningMessage, + native_bt.MESSAGE_TYPE_STREAM_ACTIVITY_END: _StreamActivityEndMessage, + native_bt.MESSAGE_TYPE_DISCARDED_EVENTS: _DiscardedEventsMessage, + native_bt.MESSAGE_TYPE_DISCARDED_PACKETS: _DiscardedPacketsMessage, +} diff --git a/src/bindings/python/bt2/bt2/message_iterator.py b/src/bindings/python/bt2/bt2/message_iterator.py new file mode 100644 index 00000000..7f996b00 --- /dev/null +++ b/src/bindings/python/bt2/bt2/message_iterator.py @@ -0,0 +1,337 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.message +import collections.abc +import bt2.component +import bt2 + + +class _MessageIterator(collections.abc.Iterator): + def _handle_status(self, status, gen_error_msg): + if status == native_bt.MESSAGE_ITERATOR_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.MESSAGE_ITERATOR_STATUS_END: + raise bt2.Stop + elif status < 0: + raise bt2.Error(gen_error_msg) + + def __next__(self): + raise NotImplementedError + + +class _GenericMessageIterator(object._SharedObject, _MessageIterator): + def __init__(self, ptr): + self._current_msgs = [] + self._at = 0 + super().__init__(ptr) + + def __next__(self): + if len(self._current_msgs) == self._at: + status, msgs = self._get_msg_range(self._ptr) + self._handle_status(status, + 'unexpected error: cannot advance the message iterator') + self._current_msgs = msgs + self._at = 0 + + msg_ptr = self._current_msgs[self._at] + self._at += 1 + + return bt2.message._create_from_ptr(msg_ptr) + + +# This is created when a component wants to iterate on one of its input ports. +class _UserComponentInputPortMessageIterator(_GenericMessageIterator): + _get_msg_range = staticmethod(native_bt.py3_self_component_port_input_get_msg_range) + _get_ref = staticmethod(native_bt.self_component_port_input_message_iterator_get_ref) + _put_ref = staticmethod(native_bt.self_component_port_input_message_iterator_put_ref) + + +# This is created when the user wants to iterate on a component's output port, +# from outside the graph. +class _OutputPortMessageIterator(_GenericMessageIterator): + _get_msg_range = staticmethod(native_bt.py3_port_output_get_msg_range) + _get_ref = staticmethod(native_bt.port_output_message_iterator_get_ref) + _put_ref = staticmethod(native_bt.port_output_message_iterator_put_ref) + + +# This is extended by the user to implement component classes in Python. It +# is created for a given output port when an input port message iterator is +# created on the input port on the other side of the connection. It is also +# created when an output port message iterator is created on this output port. +# +# Its purpose is to feed the messages that should go out through this output +# port. +class _UserMessageIterator(_MessageIterator): + def __new__(cls, ptr): + # User iterator objects are always created by the native side, + # that is, never instantiated directly by Python code. + # + # The native code calls this, then manually calls + # self.__init__() without the `ptr` argument. The user has + # access to self.component during this call, thanks to this + # self._ptr argument being set. + # + # self._ptr is NOT owned by this object here, so there's nothing + # to do in __del__(). + self = super().__new__(cls) + self._ptr = ptr + return self + + def _init_from_native(self, self_output_port_ptr): + self_output_port = bt2.port._create_self_from_ptr_and_get_ref( + self_output_port_ptr, native_bt.PORT_TYPE_OUTPUT) + self.__init__(self_output_port) + + def __init__(self, output_port): + pass + + @property + def _component(self): + return native_bt.py3_get_user_component_from_user_msg_iter(self._ptr) + + @property + def addr(self): + return int(self._ptr) + + def _finalize(self): + pass + + def __next__(self): + raise bt2.Stop + + def _next_from_native(self): + # this can raise anything: it's catched by the native part + try: + msg = next(self) + except StopIteration: + raise bt2.Stop + except: + raise + + utils._check_type(msg, bt2.message._Message) + + # Release the reference to the native part. + ptr = msg._release() + return int(ptr) + + # Validate that the presence or lack of presence of a + # `default_clock_snapshot` value is valid in the context of `stream_class`. + @staticmethod + def _validate_default_clock_snapshot(stream_class, default_clock_snapshot): + stream_class_has_default_clock_class = stream_class.default_clock_class is not None + + if stream_class_has_default_clock_class and default_clock_snapshot is None: + raise bt2.Error( + 'stream class has a default clock class, default_clock_snapshot should not be None') + + if not stream_class_has_default_clock_class and default_clock_snapshot is not None: + raise bt2.Error( + 'stream class has no default clock class, default_clock_snapshot should be None') + + def _create_event_message(self, event_class, packet, default_clock_snapshot=None): + utils._check_type(event_class, bt2.event_class._EventClass) + utils._check_type(packet, bt2.packet._Packet) + self._validate_default_clock_snapshot(packet.stream.cls, default_clock_snapshot) + + if default_clock_snapshot is not None: + utils._check_uint64(default_clock_snapshot) + ptr = native_bt.message_event_create_with_default_clock_snapshot( + self._ptr, event_class._ptr, packet._ptr, default_clock_snapshot) + else: + ptr = native_bt.message_event_create( + self._ptr, event_class._ptr, packet._ptr) + + if ptr is None: + raise bt2.CreationError('cannot create event message object') + + return bt2.message._EventMessage(ptr) + + def _create_message_iterator_inactivity_message(self, clock_class, clock_snapshot): + utils._check_type(clock_class, bt2.clock_class._ClockClass) + ptr = native_bt.message_message_iterator_inactivity_create( + self._ptr, clock_class._ptr, clock_snapshot) + + if ptr is None: + raise bt2.CreationError('cannot create inactivity message object') + + return bt2.message._MessageIteratorInactivityMessage(ptr) + + def _create_stream_beginning_message(self, stream): + utils._check_type(stream, bt2.stream._Stream) + + ptr = native_bt.message_stream_beginning_create(self._ptr, stream._ptr) + if ptr is None: + raise bt2.CreationError('cannot create stream beginning message object') + + return bt2.message._StreamBeginningMessage(ptr) + + def _create_stream_activity_beginning_message(self, stream, default_clock_snapshot=None): + utils._check_type(stream, bt2.stream._Stream) + self._validate_default_clock_snapshot(stream.cls, default_clock_snapshot) + + ptr = native_bt.message_stream_activity_beginning_create(self._ptr, stream._ptr) + + if ptr is None: + raise bt2.CreationError( + 'cannot create stream activity beginning message object') + + msg = bt2.message._StreamActivityBeginningMessage(ptr) + + if default_clock_snapshot is not None: + msg._default_clock_snapshot = default_clock_snapshot + + return msg + + def _create_stream_activity_end_message(self, stream, default_clock_snapshot=None): + utils._check_type(stream, bt2.stream._Stream) + self._validate_default_clock_snapshot(stream.cls, default_clock_snapshot) + + ptr = native_bt.message_stream_activity_end_create(self._ptr, stream._ptr) + + if ptr is None: + raise bt2.CreationError( + 'cannot create stream activity end message object') + + msg = bt2.message._StreamActivityEndMessage(ptr) + + if default_clock_snapshot is not None: + msg._default_clock_snapshot = default_clock_snapshot + + return msg + + def _create_stream_end_message(self, stream): + utils._check_type(stream, bt2.stream._Stream) + + ptr = native_bt.message_stream_end_create(self._ptr, stream._ptr) + if ptr is None: + raise bt2.CreationError('cannot create stream end message object') + + return bt2.message._StreamEndMessage(ptr) + + def _create_packet_beginning_message(self, packet, default_clock_snapshot=None): + utils._check_type(packet, bt2.packet._Packet) + + if packet.stream.cls.packets_have_beginning_default_clock_snapshot: + if default_clock_snapshot is None: + raise ValueError("packet beginning messages in this stream must have a default clock snapshots") + + utils._check_uint64(default_clock_snapshot) + ptr = native_bt.message_packet_beginning_create_with_default_clock_snapshot( + self._ptr, packet._ptr, default_clock_snapshot) + else: + if default_clock_snapshot is not None: + raise ValueError("packet beginning messages in this stream must not have a default clock snapshots") + + ptr = native_bt.message_packet_beginning_create(self._ptr, packet._ptr) + + if ptr is None: + raise bt2.CreationError('cannot create packet beginning message object') + + return bt2.message._PacketBeginningMessage(ptr) + + def _create_packet_end_message(self, packet, default_clock_snapshot=None): + utils._check_type(packet, bt2.packet._Packet) + + if packet.stream.cls.packets_have_end_default_clock_snapshot: + if default_clock_snapshot is None: + raise ValueError("packet end messages in this stream must have a default clock snapshots") + + utils._check_uint64(default_clock_snapshot) + ptr = native_bt.message_packet_end_create_with_default_clock_snapshot( + self._ptr, packet._ptr, default_clock_snapshot) + else: + if default_clock_snapshot is not None: + raise ValueError("packet end messages in this stream must not have a default clock snapshots") + + ptr = native_bt.message_packet_end_create(self._ptr, packet._ptr) + + if ptr is None: + raise bt2.CreationError('cannot create packet end message object') + + return bt2.message._PacketEndMessage(ptr) + + def _create_discarded_events_message(self, stream, count=None, + beg_clock_snapshot=None, + end_clock_snapshot=None): + utils._check_type(stream, bt2.stream._Stream) + + if not stream.cls.supports_discarded_events: + raise ValueError('stream class does not support discarded events') + + if stream.cls.discarded_events_have_default_clock_snapshots: + if beg_clock_snapshot is None or end_clock_snapshot is None: + raise ValueError('discarded events have default clock snapshots for this stream class') + + utils._check_uint64(beg_clock_snapshot) + utils._check_uint64(end_clock_snapshot) + ptr = native_bt.message_discarded_events_create_with_default_clock_snapshots( + self._ptr, stream._ptr, beg_clock_snapshot, end_clock_snapshot) + else: + if beg_clock_snapshot is not None or end_clock_snapshot is not None: + raise ValueError('discarded events have no default clock snapshots for this stream class') + + ptr = native_bt.message_discarded_events_create( + self._ptr, stream._ptr) + + if ptr is None: + raise bt2.CreationError('cannot discarded events message object') + + msg = bt2.message._DiscardedEventsMessage(ptr) + + if count is not None: + msg._count = count + + return msg + + def _create_discarded_packets_message(self, stream, count=None, beg_clock_snapshot=None, end_clock_snapshot=None): + utils._check_type(stream, bt2.stream._Stream) + + if not stream.cls.supports_discarded_packets: + raise ValueError('stream class does not support discarded packets') + + if stream.cls.discarded_packets_have_default_clock_snapshots: + if beg_clock_snapshot is None or end_clock_snapshot is None: + raise ValueError('discarded packets have default clock snapshots for this stream class') + + utils._check_uint64(beg_clock_snapshot) + utils._check_uint64(end_clock_snapshot) + ptr = native_bt.message_discarded_packets_create_with_default_clock_snapshots( + self._ptr, stream._ptr, beg_clock_snapshot, end_clock_snapshot) + else: + if beg_clock_snapshot is not None or end_clock_snapshot is not None: + raise ValueError('discarded packets have no default clock snapshots for this stream class') + + ptr = native_bt.message_discarded_packets_create( + self._ptr, stream._ptr) + + if ptr is None: + raise bt2.CreationError('cannot discarded packets message object') + + msg = bt2.message._DiscardedPacketsMessage(ptr) + + if count is not None: + msg._count = count + + return msg + diff --git a/src/bindings/python/bt2/bt2/native_bt.i b/src/bindings/python/bt2/bt2/native_bt.i new file mode 100644 index 00000000..bf20edc7 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt.i @@ -0,0 +1,213 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SWIGPYTHON +# error Unsupported output language +#endif + +%module native_bt + +%{ +#define BT_LOG_TAG "PY-NATIVE" +#include "logging.h" + +#include +#include +#include "common/assert.h" + +typedef const uint8_t *bt_uuid; +%} + +typedef int bt_bool; + +/* For uint*_t/int*_t */ +%include "stdint.i" + +/* + * Remove `bt_` and `BT_` prefixes from function names, global variables and + * enumeration items + */ +%rename("%(strip:[bt_])s", %$isfunction) ""; +%rename("%(strip:[bt_])s", %$isvariable) ""; +%rename("%(strip:[BT_])s", %$isenumitem) ""; + +/* + * Output argument typemap for string output (always appends) + * + * We initialize the output parameter `temp_value` to an invalid but non-zero + * pointer value. This is to make sure we don't rely on its initial value in + * the epilogue (where we call SWIG_Python_str_FromChar). When they fail, + * functions on which we apply this typemap don't guarantee that the value of + * `temp_value` will be unchanged or valid. + */ +%typemap(in, numinputs=0) (const char **OUT) (char *temp_value = (void *) 1) { + $1 = &temp_value; +} + +%typemap(argout) (const char **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, SWIG_Python_str_FromChar(*$1)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for value output (always appends) */ +%typemap(in, numinputs=0) (bt_value **OUT) (struct bt_value *temp_value = NULL) { + $1 = &temp_value; +} + +%typemap(argout) (bt_value **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_value, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for initialized uint64_t output parameter (always appends) */ +%typemap(in, numinputs=0) (uint64_t *OUT) (uint64_t temp) { + $1 = &temp; +} + +%typemap(argout) uint64_t *OUT { + $result = SWIG_Python_AppendOutput(resultobj, + SWIG_From_unsigned_SS_long_SS_long((*$1))); +} + +/* Output argument typemap for initialized int64_t output parameter (always appends) */ +%typemap(in, numinputs=0) (int64_t *OUT) (int64_t temp) { + $1 = &temp; +} + +%typemap(argout) (int64_t *OUT) { + $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_long_SS_long((*$1))); +} + +/* Output argument typemap for initialized unsigned int output parameter (always appends) */ +%typemap(in, numinputs=0) (unsigned int *OUT) (unsigned int temp) { + $1 = &temp; +} + +%typemap(argout) (unsigned int *OUT) { + $result = SWIG_Python_AppendOutput(resultobj, + SWIG_From_unsigned_SS_long_SS_long((uint64_t) (*$1))); +} +/* Output argument typemap for initialized double output parameter (always appends) */ +%typemap(in, numinputs=0) (double *OUT) (double temp) { + $1 = &temp; +} + +%typemap(argout) (double *OUT) { + $result = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*$1))); +} + +/* Input argument typemap for UUID bytes */ +%typemap(in) bt_uuid { + $1 = (unsigned char *) PyBytes_AsString($input); +} + +/* Output argument typemap for UUID bytes */ +%typemap(out) bt_uuid { + if (!$1) { + Py_INCREF(Py_None); + $result = Py_None; + } else { + $result = PyBytes_FromStringAndSize((const char *) $1, 16); + } +} + +/* Input argument typemap for bt_bool */ +%typemap(in) bt_bool { + $1 = PyObject_IsTrue($input); +} + +/* Output argument typemap for bt_bool */ +%typemap(out) bt_bool { + if ($1 > 0) { + $result = Py_True; + } else { + $result = Py_False; + } + Py_INCREF($result); + return $result; +} + +/* + * Input and output argument typemaps for raw Python objects (direct). + * + * Those typemaps honor the convention of Python C function calls with + * respect to reference counting: parameters are passed as borrowed + * references, and objects are returned as new references. The wrapped + * C function must ensure that the return value is always a new + * reference, and never steal parameter references. + */ +%typemap(in) PyObject * { + $1 = $input; +} + +%typemap(out) PyObject * { + $result = $1; +} + +/* From property.h */ + +typedef enum bt_property_availability { + BT_PROPERTY_AVAILABILITY_AVAILABLE, + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, +} bt_property_availability; + +/* Per-module interface files */ +%include "native_bt_clock_class.i" +%include "native_bt_clock_snapshot.i" +%include "native_bt_component.i" +%include "native_bt_component_class.i" +%include "native_bt_connection.i" +%include "native_bt_event.i" +%include "native_bt_event_class.i" +%include "native_bt_field.i" +%include "native_bt_field_class.i" +%include "native_bt_field_path.i" +%include "native_bt_graph.i" +%include "native_bt_logging.i" +%include "native_bt_message.i" +%include "native_bt_notifier.i" +%include "native_bt_packet.i" +%include "native_bt_plugin.i" +%include "native_bt_port.i" +%include "native_bt_query_exec.i" +%include "native_bt_stream.i" +%include "native_bt_stream_class.i" +%include "native_bt_trace.i" +%include "native_bt_trace_class.i" +%include "native_bt_value.i" +%include "native_bt_version.i" diff --git a/src/bindings/python/bt2/bt2/native_bt_clock_class.i b/src/bindings/python/bt2/bt2/native_bt_clock_class.i new file mode 100644 index 00000000..bf8ea437 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_clock_class.i @@ -0,0 +1,85 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From clock-class-const.h */ + +typedef enum bt_clock_class_status { + BT_CLOCK_CLASS_STATUS_OK = 0, + BT_CLOCK_CLASS_STATUS_NOMEM = -12, + BT_CLOCK_CLASS_STATUS_OVERFLOW = -75, +} bt_clock_class_status; + +extern const char *bt_clock_class_get_name( + const bt_clock_class *clock_class); + +extern const char *bt_clock_class_get_description( + const bt_clock_class *clock_class); + +extern uint64_t bt_clock_class_get_frequency( + const bt_clock_class *clock_class); + +extern uint64_t bt_clock_class_get_precision( + const bt_clock_class *clock_class); + +extern void bt_clock_class_get_offset(const bt_clock_class *clock_class, + int64_t *OUT, uint64_t *OUT); + +extern bt_bool bt_clock_class_origin_is_unix_epoch( + const bt_clock_class *clock_class); + +extern bt_uuid bt_clock_class_get_uuid( + const bt_clock_class *clock_class); + +extern bt_clock_class_status bt_clock_class_cycles_to_ns_from_origin( + const bt_clock_class *clock_class, + uint64_t cycles, int64_t *OUT); + +extern void bt_clock_class_get_ref(const bt_clock_class *clock_class); + +extern void bt_clock_class_put_ref(const bt_clock_class *clock_class); + +/* From clock-class.h */ + +extern bt_clock_class *bt_clock_class_create(bt_self_component *self_comp); + +extern bt_clock_class_status bt_clock_class_set_name( + bt_clock_class *clock_class, const char *name); + +extern bt_clock_class_status bt_clock_class_set_description( + bt_clock_class *clock_class, const char *description); + +extern void bt_clock_class_set_frequency(bt_clock_class *clock_class, + uint64_t freq); + +extern void bt_clock_class_set_precision(bt_clock_class *clock_class, + uint64_t precision); + +extern void bt_clock_class_set_offset(bt_clock_class *clock_class, + int64_t seconds, uint64_t cycles); + +extern void bt_clock_class_set_origin_is_unix_epoch(bt_clock_class *clock_class, + bt_bool origin_is_unix_epoch); + +extern void bt_clock_class_set_uuid(bt_clock_class *clock_class, + bt_uuid uuid); diff --git a/src/bindings/python/bt2/bt2/native_bt_clock_snapshot.i b/src/bindings/python/bt2/bt2/native_bt_clock_snapshot.i new file mode 100644 index 00000000..a2ca8e86 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_clock_snapshot.i @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From clock-snapshot-const.h */ + +typedef enum bt_clock_snapshot_status { + BT_CLOCK_SNAPSHOT_STATUS_OK = 0, + BT_CLOCK_SNAPSHOT_STATUS_OVERFLOW = -75, +} bt_clock_snapshot_status; + +extern const bt_clock_class *bt_clock_snapshot_borrow_clock_class_const( + const bt_clock_snapshot *clock_snapshot); + +extern uint64_t bt_clock_snapshot_get_value( + const bt_clock_snapshot *clock_snapshot); + +extern bt_clock_snapshot_status bt_clock_snapshot_get_ns_from_origin( + const bt_clock_snapshot *clock_snapshot, + int64_t *OUT); diff --git a/src/bindings/python/bt2/bt2/native_bt_component.i b/src/bindings/python/bt2/bt2/native_bt_component.i new file mode 100644 index 00000000..f484263e --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_component.i @@ -0,0 +1,305 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Output argument typemap for self port output (always appends) */ +%typemap(in, numinputs=0) + (bt_self_component_port_input **OUT) + (bt_self_component_port_input *temp_self_port = NULL) { + $1 = &temp_self_port; +} + +%typemap(argout) bt_self_component_port_input **OUT { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_self_component_port_input, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for self port output (always appends) */ +%typemap(in, numinputs=0) + (bt_self_component_port_output **OUT) + (bt_self_component_port_output *temp_self_port = NULL) { + $1 = &temp_self_port; +} + +%typemap(argout) (bt_self_component_port_output **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_self_component_port_output, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Typemaps used for user data attached to self component ports. */ + +/* + * The user data Python object is kept as the user data of the port, we pass + * the PyObject pointer directly to the port creation function. + */ +%typemap(in) void *PY_SELF_PORT_USER_DATA { + $1 = $input; +} + +/* + * The port, if created successfully, now owns a reference to the Python object, + * we reflect that here. + */ +%typemap(argout) void *PY_SELF_PORT_USER_DATA { + if (PyLong_AsLong($result) == BT_SELF_COMPONENT_STATUS_OK) { + Py_INCREF($1); + } +} + +/* From component-const.h */ + +extern const char *bt_component_get_name(const bt_component *component); + +extern const bt_component_class *bt_component_borrow_class_const( + const bt_component *component); + +extern bt_component_class_type bt_component_get_class_type( + const bt_component *component); + +bt_bool bt_component_is_source(const bt_component *component); + +bt_bool bt_component_is_filter(const bt_component *component); + +bt_bool bt_component_is_sink(const bt_component *component); + +extern bt_bool bt_component_graph_is_canceled( + const bt_component *component); + +extern void bt_component_get_ref(const bt_component *component); + +extern void bt_component_put_ref(const bt_component *component); + +/* From component-source-const.h */ + +const bt_component *bt_component_source_as_component_const( + const bt_component_source *component); + +extern const bt_component_class_source * +bt_component_source_borrow_class_const( + const bt_component_source *component); + +extern uint64_t bt_component_source_get_output_port_count( + const bt_component_source *component); + +extern const bt_port_output * +bt_component_source_borrow_output_port_by_name_const( + const bt_component_source *component, const char *name); + +extern const bt_port_output * +bt_component_source_borrow_output_port_by_index_const( + const bt_component_source *component, uint64_t index); + +extern void bt_component_source_get_ref( + const bt_component_source *component_source); + +extern void bt_component_source_put_ref( + const bt_component_source *component_source); + +/* From component-filter-const.h */ + +const bt_component *bt_component_filter_as_component_const( + const bt_component_filter *component); + +extern const bt_component_class_filter * +bt_component_filter_borrow_class_const( + const bt_component_filter *component); + +extern uint64_t bt_component_filter_get_input_port_count( + const bt_component_filter *component); + +extern const bt_port_input * +bt_component_filter_borrow_input_port_by_name_const( + const bt_component_filter *component, const char *name); + +extern const bt_port_input * +bt_component_filter_borrow_input_port_by_index_const( + const bt_component_filter *component, uint64_t index); + +extern uint64_t bt_component_filter_get_output_port_count( + const bt_component_filter *component); + +extern const bt_port_output * +bt_component_filter_borrow_output_port_by_name_const( + const bt_component_filter *component, const char *name); + +extern const bt_port_output * +bt_component_filter_borrow_output_port_by_index_const( + const bt_component_filter *component, uint64_t index); + +extern void bt_component_filter_get_ref( + const bt_component_filter *component_filter); + +extern void bt_component_filter_put_ref( + const bt_component_filter *component_filter); + +/* From component-sink-const.h */ + +const bt_component *bt_component_sink_as_component_const( + const bt_component_sink *component); + +extern const bt_component_class_sink * +bt_component_sink_borrow_class_const( + const bt_component_sink *component); + +extern uint64_t bt_component_sink_get_input_port_count( + const bt_component_sink *component); + +extern const bt_port_input * +bt_component_sink_borrow_input_port_by_name_const( + const bt_component_sink *component, const char *name); + +extern const bt_port_input * +bt_component_sink_borrow_input_port_by_index_const( + const bt_component_sink *component, uint64_t index); + +extern void bt_component_sink_get_ref( + const bt_component_sink *component_sink); + +extern void bt_component_sink_put_ref( + const bt_component_sink *component_sink); + +/* From self-component.h */ + +typedef enum bt_self_component_status { + BT_SELF_COMPONENT_STATUS_OK = 0, + BT_SELF_COMPONENT_STATUS_END = 1, + BT_SELF_COMPONENT_STATUS_AGAIN = 11, + BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION = 111, + BT_SELF_COMPONENT_STATUS_ERROR = -1, + BT_SELF_COMPONENT_STATUS_NOMEM = -12, +} bt_self_component_status; + +const bt_component *bt_self_component_as_component( + bt_self_component *self_component); + +extern void *bt_self_component_get_data( + const bt_self_component *self_component); + +extern void bt_self_component_set_data( + bt_self_component *self_component, void *data); + +/* From self-component-source.h */ + +bt_self_component *bt_self_component_source_as_self_component( + bt_self_component_source *self_comp_source); + +const bt_component_source * +bt_self_component_source_as_component_source( + bt_self_component_source *self_comp_source); + +extern bt_self_component_port_output * +bt_self_component_source_borrow_output_port_by_name( + bt_self_component_source *self_component, + const char *name); + +extern bt_self_component_port_output * +bt_self_component_source_borrow_output_port_by_index( + bt_self_component_source *self_component, + uint64_t index); + +extern bt_self_component_status +bt_self_component_source_add_output_port( + bt_self_component_source *self_component, + const char *name, void *PY_SELF_PORT_USER_DATA, + bt_self_component_port_output **OUT); + +/* From self-component-filter.h */ + +bt_self_component *bt_self_component_filter_as_self_component( + bt_self_component_filter *self_comp_filter); + +const bt_component_filter * +bt_self_component_filter_as_component_filter( + bt_self_component_filter *self_comp_filter); + +extern bt_self_component_port_output * +bt_self_component_filter_borrow_output_port_by_name( + bt_self_component_filter *self_component, + const char *name); + +extern bt_self_component_port_output * +bt_self_component_filter_borrow_output_port_by_index( + bt_self_component_filter *self_component, + uint64_t index); + +extern bt_self_component_status +bt_self_component_filter_add_output_port( + bt_self_component_filter *self_component, + const char *name, void *PY_SELF_PORT_USER_DATA, + bt_self_component_port_output **OUT); + +extern bt_self_component_port_input * +bt_self_component_filter_borrow_input_port_by_name( + bt_self_component_filter *self_component, + const char *name); + +extern bt_self_component_port_input * +bt_self_component_filter_borrow_input_port_by_index( + bt_self_component_filter *self_component, + uint64_t index); + +extern bt_self_component_status +bt_self_component_filter_add_input_port( + bt_self_component_filter *self_component, + const char *name, void *PY_SELF_PORT_USER_DATA, + bt_self_component_port_input **OUT); + +/* From self-component-sink.h */ + +bt_self_component *bt_self_component_sink_as_self_component( + bt_self_component_sink *self_comp_sink); + +const bt_component_sink * +bt_self_component_sink_as_component_sink( + bt_self_component_sink *self_comp_sink); + +extern bt_self_component_port_input * +bt_self_component_sink_borrow_input_port_by_name( + bt_self_component_sink *self_component, + const char *name); + +extern bt_self_component_port_input * +bt_self_component_sink_borrow_input_port_by_index( + bt_self_component_sink *self_component, uint64_t index); + +extern bt_self_component_status +bt_self_component_sink_add_input_port( + bt_self_component_sink *self_component, + const char *name, void *PY_SELF_PORT_USER_DATA, + bt_self_component_port_input **OUT); diff --git a/src/bindings/python/bt2/bt2/native_bt_component_class.i b/src/bindings/python/bt2/bt2/native_bt_component_class.i new file mode 100644 index 00000000..1dfa0110 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_component_class.i @@ -0,0 +1,1761 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From component-class-const.h */ + +typedef enum bt_component_class_status { + BT_COMPONENT_CLASS_STATUS_OK = 0, + BT_COMPONENT_CLASS_STATUS_NOMEM = -12, +} bt_component_class_status; + +typedef enum bt_component_class_type { + BT_COMPONENT_CLASS_TYPE_SOURCE = 0, + BT_COMPONENT_CLASS_TYPE_FILTER = 1, + BT_COMPONENT_CLASS_TYPE_SINK = 2, +} bt_component_class_type; + +extern const char *bt_component_class_get_name( + const bt_component_class *component_class); + +extern const char *bt_component_class_get_description( + const bt_component_class *component_class); + +extern const char *bt_component_class_get_help( + const bt_component_class *component_class); + +extern bt_component_class_type bt_component_class_get_type( + const bt_component_class *component_class); + +bt_bool bt_component_class_is_source( + const bt_component_class *component_class); + +bt_bool bt_component_class_is_filter( + const bt_component_class *component_class); + +bt_bool bt_component_class_is_sink( + const bt_component_class *component_class); + +extern void bt_component_class_get_ref( + const bt_component_class *component_class); + +extern void bt_component_class_put_ref( + const bt_component_class *component_class); + +/* From component-class-source-const.h */ + +const bt_component_class * +bt_component_class_source_as_component_class_const( + const bt_component_class_source *comp_cls_source); + +extern void bt_component_class_source_get_ref( + const bt_component_class_source *component_class_source); + +extern void bt_component_class_source_put_ref( + const bt_component_class_source *component_class_source); + +/* From component-class-source.h */ + +typedef bt_self_component_status +(*bt_component_class_source_init_method)( + bt_self_component_source *self_component, + const bt_value *params, void *init_method_data); + +typedef void (*bt_component_class_source_finalize_method)( + bt_self_component_source *self_component); + +typedef bt_self_message_iterator_status +(*bt_component_class_source_message_iterator_init_method)( + bt_self_message_iterator *message_iterator, + bt_self_component_source *self_component, + bt_self_component_port_output *port); + +typedef void +(*bt_component_class_source_message_iterator_finalize_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_self_message_iterator_status +(*bt_component_class_source_message_iterator_next_method)( + bt_self_message_iterator *message_iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +typedef bt_self_message_iterator_status +(*bt_component_class_source_message_iterator_seek_ns_from_origin_method)( + bt_self_message_iterator *message_iterator, + int64_t ns_from_origin); + +typedef bt_self_message_iterator_status +(*bt_component_class_source_message_iterator_seek_beginning_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_bool +(*bt_component_class_source_message_iterator_can_seek_ns_from_origin_method)( + bt_self_message_iterator *message_iterator, + int64_t ns_from_origin); + +typedef bt_bool +(*bt_component_class_source_message_iterator_can_seek_beginning_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_query_status (*bt_component_class_source_query_method)( + bt_self_component_class_source *comp_class, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result); + +typedef bt_self_component_status +(*bt_component_class_source_accept_output_port_connection_method)( + bt_self_component_source *self_component, + bt_self_component_port_output *self_port, + const bt_port_input *other_port); + +typedef bt_self_component_status +(*bt_component_class_source_output_port_connected_method)( + bt_self_component_source *self_component, + bt_self_component_port_output *self_port, + const bt_port_input *other_port); + +bt_component_class *bt_component_class_source_as_component_class( + bt_component_class_source *comp_cls_source); + +extern +bt_component_class_source *bt_component_class_source_create( + const char *name, + bt_component_class_source_message_iterator_next_method method); + +extern bt_component_class_status +bt_component_class_source_set_init_method( + bt_component_class_source *comp_class, + bt_component_class_source_init_method method); + +extern bt_component_class_status +bt_component_class_source_set_finalize_method( + bt_component_class_source *comp_class, + bt_component_class_source_finalize_method method); + +extern bt_component_class_status +bt_component_class_source_set_accept_output_port_connection_method( + bt_component_class_source *comp_class, + bt_component_class_source_accept_output_port_connection_method method); + +extern bt_component_class_status +bt_component_class_source_set_output_port_connected_method( + bt_component_class_source *comp_class, + bt_component_class_source_output_port_connected_method method); + +extern bt_component_class_status +bt_component_class_source_set_query_method( + bt_component_class_source *comp_class, + bt_component_class_source_query_method method); + +extern bt_component_class_status +bt_component_class_source_set_message_iterator_init_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_init_method method); + +extern bt_component_class_status +bt_component_class_source_set_message_iterator_finalize_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_finalize_method method); + +extern bt_component_class_status +bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_seek_ns_from_origin_method method); + +extern bt_component_class_status +bt_component_class_source_set_message_iterator_seek_beginning_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_seek_beginning_method method); + +extern bt_bool +bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_can_seek_ns_from_origin_method method); + +extern bt_bool +bt_component_class_source_set_message_iterator_can_seek_beginning_method( + bt_component_class_source *comp_class, + bt_component_class_source_message_iterator_can_seek_beginning_method method); + +/* From component-class-filter-const.h */ + +const bt_component_class * +bt_component_class_filter_as_component_class_const( + const bt_component_class_filter *comp_cls_filter); + +extern void bt_component_class_filter_get_ref( + const bt_component_class_filter *component_class_filter); + +extern void bt_component_class_filter_put_ref( + const bt_component_class_filter *component_class_filter); + +/* From component-class-filter.h */ + +typedef bt_self_component_status +(*bt_component_class_filter_init_method)( + bt_self_component_filter *self_component, + const bt_value *params, void *init_method_data); + +typedef void (*bt_component_class_filter_finalize_method)( + bt_self_component_filter *self_component); + +typedef bt_self_message_iterator_status +(*bt_component_class_filter_message_iterator_init_method)( + bt_self_message_iterator *message_iterator, + bt_self_component_filter *self_component, + bt_self_component_port_output *port); + +typedef void +(*bt_component_class_filter_message_iterator_finalize_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_self_message_iterator_status +(*bt_component_class_filter_message_iterator_next_method)( + bt_self_message_iterator *message_iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +typedef bt_self_message_iterator_status +(*bt_component_class_filter_message_iterator_seek_ns_from_origin_method)( + bt_self_message_iterator *message_iterator, + int64_t ns_from_origin); + +typedef bt_self_message_iterator_status +(*bt_component_class_filter_message_iterator_seek_beginning_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_bool +(*bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method)( + bt_self_message_iterator *message_iterator, + int64_t ns_from_origin); + +typedef bt_bool +(*bt_component_class_filter_message_iterator_can_seek_beginning_method)( + bt_self_message_iterator *message_iterator); + +typedef bt_query_status +(*bt_component_class_filter_query_method)( + bt_self_component_class_filter *comp_class, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result); + +typedef bt_self_component_status +(*bt_component_class_filter_accept_input_port_connection_method)( + bt_self_component_filter *self_component, + bt_self_component_port_input *self_port, + const bt_port_output *other_port); + +typedef bt_self_component_status +(*bt_component_class_filter_accept_output_port_connection_method)( + bt_self_component_filter *self_component, + bt_self_component_port_output *self_port, + const bt_port_input *other_port); + +typedef bt_self_component_status +(*bt_component_class_filter_input_port_connected_method)( + bt_self_component_filter *self_component, + bt_self_component_port_input *self_port, + const bt_port_output *other_port); + +typedef bt_self_component_status +(*bt_component_class_filter_output_port_connected_method)( + bt_self_component_filter *self_component, + bt_self_component_port_output *self_port, + const bt_port_input *other_port); + +bt_component_class *bt_component_class_filter_as_component_class( + bt_component_class_filter *comp_cls_filter); + +extern +bt_component_class_filter *bt_component_class_filter_create( + const char *name, + bt_component_class_filter_message_iterator_next_method method); + +extern bt_component_class_status +bt_component_class_filter_set_init_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_init_method method); + +extern bt_component_class_status +bt_component_class_filter_set_finalize_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_finalize_method method); + +extern bt_component_class_status +bt_component_class_filter_set_accept_input_port_connection_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_accept_input_port_connection_method method); + +extern bt_component_class_status +bt_component_class_filter_set_accept_output_port_connection_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_accept_output_port_connection_method method); + +extern bt_component_class_status +bt_component_class_filter_set_input_port_connected_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_input_port_connected_method method); + +extern bt_component_class_status +bt_component_class_filter_set_output_port_connected_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_output_port_connected_method method); + +extern bt_component_class_status +bt_component_class_filter_set_query_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_query_method method); + +extern bt_component_class_status +bt_component_class_filter_set_message_iterator_init_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_init_method method); + +extern bt_component_class_status +bt_component_class_filter_set_message_iterator_finalize_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_finalize_method method); + +extern bt_component_class_status +bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_seek_ns_from_origin_method method); + +extern bt_component_class_status +bt_component_class_filter_set_message_iterator_seek_beginning_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_seek_beginning_method method); + +extern bt_bool +bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method method); + +extern bt_bool +bt_component_class_filter_set_message_iterator_can_seek_beginning_method( + bt_component_class_filter *comp_class, + bt_component_class_filter_message_iterator_can_seek_beginning_method method); + +/* From component-class-sink-const.h */ + +const bt_component_class * +bt_component_class_sink_as_component_class_const( + const bt_component_class_sink *comp_cls_sink); + +extern void bt_component_class_sink_get_ref( + const bt_component_class_sink *component_class_sink); + +extern void bt_component_class_sink_put_ref( + const bt_component_class_sink *component_class_sink); + +/* From component-class-sink.h */ + +typedef bt_self_component_status (*bt_component_class_sink_init_method)( + bt_self_component_sink *self_component, + const bt_value *params, void *init_method_data); + +typedef void (*bt_component_class_sink_finalize_method)( + bt_self_component_sink *self_component); + +typedef bt_query_status +(*bt_component_class_sink_query_method)( + bt_self_component_class_sink *comp_class, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result); + +typedef bt_self_component_status +(*bt_component_class_sink_accept_input_port_connection_method)( + bt_self_component_sink *self_component, + bt_self_component_port_input *self_port, + const bt_port_output *other_port); + +typedef bt_self_component_status +(*bt_component_class_sink_input_port_connected_method)( + bt_self_component_sink *self_component, + bt_self_component_port_input *self_port, + const bt_port_output *other_port); + +typedef bt_self_component_status +(*bt_component_class_sink_graph_is_configured_method)( + bt_self_component_sink *self_component); + +typedef bt_self_component_status (*bt_component_class_sink_consume_method)( + bt_self_component_sink *self_component); + +bt_component_class *bt_component_class_sink_as_component_class( + bt_component_class_sink *comp_cls_sink); + +extern +bt_component_class_sink *bt_component_class_sink_create( + const char *name, + bt_component_class_sink_consume_method method); + +extern bt_component_class_status bt_component_class_sink_set_init_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_init_method method); + +extern bt_component_class_status bt_component_class_sink_set_finalize_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_finalize_method method); + +extern bt_component_class_status +bt_component_class_sink_set_accept_input_port_connection_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_accept_input_port_connection_method method); + +extern bt_component_class_status +bt_component_class_sink_set_input_port_connected_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_input_port_connected_method method); + +extern bt_component_class_status +bt_component_class_sink_set_graph_is_configured_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_graph_is_configured_method method); + +extern bt_component_class_status bt_component_class_sink_set_query_method( + bt_component_class_sink *comp_class, + bt_component_class_sink_query_method method); + +/* From self-component-class-source.h */ + +const bt_component_class_source * +bt_self_component_class_source_as_component_class_source( + bt_self_component_class_source *self_comp_cls_source); + +/* From self-component-class-filter.h */ + +const bt_component_class_filter * +bt_self_component_class_filter_as_component_class_filter( + bt_self_component_class_filter *self_comp_cls_filter); + +/* From self-component-class-sink.h */ + +const bt_component_class_sink * +bt_self_component_class_sink_as_component_class_sink( + bt_self_component_class_sink *self_comp_cls_sink); + +%{ +/* + * This hash table associates a BT component class object address to a + * user-defined Python class (PyObject *). The keys and values are NOT + * owned by this hash table. The Python class objects are owned by the + * Python module, which should not be unloaded until it is not possible + * to create a user Python component anyway. + * + * This hash table is written to when a user-defined Python component + * class is created by one of the bt_py3_component_class_*_create() + * functions. + * + * This function is read from when a user calls bt_component_create() + * with a component class pointer created by one of the functions above. + * In this case, the original Python class needs to be found to + * instantiate it and associate the created Python component object with + * a BT component object instance. + */ + +static GHashTable *bt_cc_ptr_to_py_cls; + +static void register_cc_ptr_to_py_cls(struct bt_component_class *bt_cc, + PyObject *py_cls) +{ + if (!bt_cc_ptr_to_py_cls) { + /* + * Lazy-initializing this GHashTable because GLib + * might not be initialized yet and it needs to be + * before we call g_hash_table_new() + */ + BT_LOGD_STR("Creating native component class to Python component class hash table."); + bt_cc_ptr_to_py_cls = g_hash_table_new(g_direct_hash, g_direct_equal); + BT_ASSERT(bt_cc_ptr_to_py_cls); + } + + g_hash_table_insert(bt_cc_ptr_to_py_cls, (gpointer) bt_cc, + (gpointer) py_cls); +} + +static PyObject *lookup_cc_ptr_to_py_cls(const bt_component_class *bt_cc) +{ + if (!bt_cc_ptr_to_py_cls) { + BT_LOGW("Cannot look up Python component class because hash table is NULL: " + "comp-cls-addr=%p", bt_cc); + return NULL; + } + + return (PyObject *) g_hash_table_lookup(bt_cc_ptr_to_py_cls, + (gconstpointer) bt_cc); +} + + +/* + * Useful Python objects. + */ + +static PyObject *py_mod_bt2 = NULL; +static PyObject *py_mod_bt2_exc_error_type = NULL; +static PyObject *py_mod_bt2_exc_try_again_type = NULL; +static PyObject *py_mod_bt2_exc_stop_type = NULL; +static PyObject *py_mod_bt2_exc_port_connection_refused_type = NULL; +static PyObject *py_mod_bt2_exc_msg_iter_canceled_type = NULL; +static PyObject *py_mod_bt2_exc_invalid_query_object_type = NULL; +static PyObject *py_mod_bt2_exc_invalid_query_params_type = NULL; + +static void bt_py3_cc_init_from_bt2(void) +{ + /* + * This is called once the bt2 package is loaded. + * + * Those modules and functions are needed while the package is + * used. Loading them here is safe because we know the bt2 + * package is imported, and we know that the user cannot use the + * code here without importing bt2 first. + */ + py_mod_bt2 = PyImport_ImportModule("bt2"); + BT_ASSERT(py_mod_bt2); + py_mod_bt2_exc_error_type = + PyObject_GetAttrString(py_mod_bt2, "Error"); + BT_ASSERT(py_mod_bt2_exc_error_type); + py_mod_bt2_exc_try_again_type = + PyObject_GetAttrString(py_mod_bt2, "TryAgain"); + BT_ASSERT(py_mod_bt2_exc_try_again_type); + py_mod_bt2_exc_stop_type = + PyObject_GetAttrString(py_mod_bt2, "Stop"); + BT_ASSERT(py_mod_bt2_exc_stop_type); + py_mod_bt2_exc_port_connection_refused_type = + PyObject_GetAttrString(py_mod_bt2, "PortConnectionRefused"); + BT_ASSERT(py_mod_bt2_exc_port_connection_refused_type); + py_mod_bt2_exc_invalid_query_object_type = + PyObject_GetAttrString(py_mod_bt2, "InvalidQueryObject"); + BT_ASSERT(py_mod_bt2_exc_invalid_query_object_type); + py_mod_bt2_exc_invalid_query_params_type = + PyObject_GetAttrString(py_mod_bt2, "InvalidQueryParams"); + BT_ASSERT(py_mod_bt2_exc_invalid_query_params_type); +} + +static void bt_py3_cc_exit_handler(void) +{ + /* + * This is an exit handler (set by the bt2 package). + * + * We only give back the references that we took in + * bt_py3_cc_init_from_bt2() here. The global variables continue + * to exist for the code of this file, but they are now borrowed + * references. If this code is executed, it means that somehow + * the modules are still loaded, so it should be safe to use + * them even without a strong reference. + * + * We cannot do this in the library's destructor because it + * gets executed once Python is already finalized. + */ + Py_XDECREF(py_mod_bt2); + Py_XDECREF(py_mod_bt2_exc_error_type); + Py_XDECREF(py_mod_bt2_exc_try_again_type); + Py_XDECREF(py_mod_bt2_exc_stop_type); + Py_XDECREF(py_mod_bt2_exc_port_connection_refused_type); + Py_XDECREF(py_mod_bt2_exc_msg_iter_canceled_type); + Py_XDECREF(py_mod_bt2_exc_invalid_query_object_type); + Py_XDECREF(py_mod_bt2_exc_invalid_query_params_type); +} + + +/* Library destructor */ + +__attribute__((destructor)) +static void bt_py3_native_comp_class_dtor(void) { + /* Destroy component class association hash table */ + if (bt_cc_ptr_to_py_cls) { + BT_LOGD_STR("Destroying native component class to Python component class hash table."); + g_hash_table_destroy(bt_cc_ptr_to_py_cls); + } +} + + +// TODO: maybe we can wrap code in the Python methods (e.g. _query_from_native) +// in a try catch and print the error there instead, it would be simpler. +static +void bt2_py_loge_exception(void) +{ + PyObject *type = NULL; + PyObject *value = NULL; + PyObject *traceback = NULL; + PyObject *traceback_module = NULL; + PyObject *format_exception_func = NULL; + PyObject *exc_str_list = NULL; + GString *msg_buf = NULL; + Py_ssize_t i; + + BT_ASSERT(PyErr_Occurred() != NULL); + + PyErr_Fetch(&type, &value, &traceback); + + BT_ASSERT(type != NULL); + + /* + * traceback can be NULL, when we fail to call a Python function from the + * native code (there is not Python stack at that point). E.g.: + * + * TypeError: _accept_port_connection_from_native() takes 3 positional arguments but 4 were given + */ + + + /* Make sure `value` is what we expected - an instance of `type`. */ + PyErr_NormalizeException(&type, &value, &traceback); + + traceback_module = PyImport_ImportModule("traceback"); + if (!traceback_module) { + BT_LOGE_STR("Failed to log Python exception (could not import traceback module)."); + goto end; + } + + format_exception_func = PyObject_GetAttrString(traceback_module, + traceback ? "format_exception" : "format_exception_only"); + if (!format_exception_func) { + BT_LOGE_STR("Failed to log Python exception (could not find format_exception)."); + goto end; + } + + if (!PyCallable_Check(format_exception_func)) { + BT_LOGE_STR("Failed to log Python exception (format_exception is not callable)."); + goto end; + } + + exc_str_list = PyObject_CallFunctionObjArgs(format_exception_func, type, value, traceback, NULL); + if (!exc_str_list) { + PyErr_Print(); + BT_LOGE_STR("Failed to log Python exception (call to format_exception failed)."); + goto end; + } + + msg_buf = g_string_new(NULL); + + for (i = 0; i < PyList_Size(exc_str_list); i++) { + PyObject *exc_str = PyList_GetItem(exc_str_list, i); + const char *str = PyUnicode_AsUTF8(exc_str); + if (!str) { + BT_LOGE_STR("Failed to log Python exception (failed to convert exception to string)."); + goto end; + } + + g_string_append(msg_buf, str); + } + + BT_LOGE_STR(msg_buf->str); + +end: + if (msg_buf) { + g_string_free(msg_buf, TRUE); + } + Py_XDECREF(exc_str_list); + Py_XDECREF(format_exception_func); + Py_XDECREF(traceback_module); + + /* PyErr_Restore takes our references. */ + PyErr_Restore(type, value, traceback); +} + +static bt_self_component_status bt_py3_exc_to_self_component_status(void) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + PyObject *exc = PyErr_Occurred(); + + if (!exc) { + goto end; + } + + if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_try_again_type)) { + status = BT_SELF_COMPONENT_STATUS_AGAIN; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_stop_type)) { + status = BT_SELF_COMPONENT_STATUS_END; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_port_connection_refused_type)) { + status = BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; + } else { + bt2_py_loge_exception(); + status = BT_SELF_COMPONENT_STATUS_ERROR; + } + +end: + PyErr_Clear(); + return status; +} + +/* Component class proxy methods (delegate to the attached Python object) */ + +static bt_self_message_iterator_status +bt_py3_exc_to_self_message_iterator_status(void) +{ + enum bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + PyObject *exc = PyErr_Occurred(); + + if (!exc) { + goto end; + } + + if (PyErr_GivenExceptionMatches(exc, py_mod_bt2_exc_stop_type)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + } else if (PyErr_GivenExceptionMatches(exc, py_mod_bt2_exc_try_again_type)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; + } else { + bt2_py_loge_exception(); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } + +end: + PyErr_Clear(); + return status; +} + +static enum bt_query_status bt_py3_exc_to_query_status(void) +{ + enum bt_query_status status = BT_QUERY_STATUS_OK; + PyObject *exc = PyErr_Occurred(); + + if (!exc) { + goto end; + } + + if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_invalid_query_object_type)) { + status = BT_QUERY_STATUS_INVALID_OBJECT; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_invalid_query_params_type)) { + status = BT_QUERY_STATUS_INVALID_PARAMS; + } else if (PyErr_GivenExceptionMatches(exc, + py_mod_bt2_exc_try_again_type)) { + status = BT_QUERY_STATUS_AGAIN; + } else { + bt2_py_loge_exception(); + status = BT_QUERY_STATUS_ERROR; + } + +end: + PyErr_Clear(); + return status; +} + +static bt_self_component_status +bt_py3_component_class_init( + bt_self_component *self_component, + void *self_component_v, + swig_type_info *self_comp_cls_type_swig_type, + const bt_value *params, + void *init_method_data) +{ + const bt_component *component = bt_self_component_as_component(self_component); + const bt_component_class *component_class = bt_component_borrow_class_const(component); + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + PyObject *py_cls = NULL; + PyObject *py_comp = NULL; + PyObject *py_params_ptr = NULL; + PyObject *py_comp_ptr = NULL; + + (void) init_method_data; + + BT_ASSERT(self_component); + BT_ASSERT(self_component_v); + BT_ASSERT(self_comp_cls_type_swig_type); + + /* + * Get the user-defined Python class which created this + * component's class in the first place (borrowed + * reference). + */ + py_cls = lookup_cc_ptr_to_py_cls(component_class); + if (!py_cls) { + BT_LOGE("Cannot find Python class associated to native component class: " + "comp-cls-addr=%p", component_class); + goto error; + } + + /* Parameters pointer -> SWIG pointer Python object */ + py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), + SWIGTYPE_p_bt_value, 0); + if (!py_params_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_comp_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_v), + self_comp_cls_type_swig_type, 0); + if (!py_comp_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + /* + * Do the equivalent of this: + * + * py_comp = py_cls._init_from_native(py_comp_ptr, py_params_ptr) + * + * _UserComponentType._init_from_native() calls the Python + * component object's __init__() function. + */ + py_comp = PyObject_CallMethod(py_cls, + "_init_from_native", "(OO)", py_comp_ptr, py_params_ptr); + if (!py_comp) { + bt2_py_loge_exception(); + BT_LOGE("Failed to call Python class's _init_from_native() method: " + "py-cls-addr=%p", py_cls); + + goto error; + } + + /* + * Our user Python component object is now fully created and + * initialized by the user. Since we just created it, this + * native component is its only (persistent) owner. + */ + bt_self_component_set_data(self_component, py_comp); + py_comp = NULL; + goto end; + +error: + status = BT_SELF_COMPONENT_STATUS_ERROR; + + /* + * Clear any exception: we're returning a bad status anyway. If + * this call originated from Python (creation from a plugin's + * component class, for example), then the user gets an + * appropriate creation error. + */ + PyErr_Clear(); + +end: + Py_XDECREF(py_comp); + Py_XDECREF(py_params_ptr); + Py_XDECREF(py_comp_ptr); + return status; +} + +/* + * Method of bt_component_class_source to initialize a bt_self_component_source + * of that class. + */ + +static bt_self_component_status +bt_py3_component_class_source_init(bt_self_component_source *self_component_source, + const bt_value *params, void *init_method_data) +{ + bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); + return bt_py3_component_class_init( + self_component, + self_component_source, + SWIGTYPE_p_bt_self_component_source, + params, init_method_data); +} + +static bt_self_component_status +bt_py3_component_class_filter_init(bt_self_component_filter *self_component_filter, + const bt_value *params, void *init_method_data) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + return bt_py3_component_class_init( + self_component, + self_component_filter, + SWIGTYPE_p_bt_self_component_filter, + params, init_method_data); +} + +static bt_self_component_status +bt_py3_component_class_sink_init(bt_self_component_sink *self_component_sink, + const bt_value *params, void *init_method_data) +{ + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + return bt_py3_component_class_init( + self_component, + self_component_sink, + SWIGTYPE_p_bt_self_component_sink, + params, init_method_data); +} + +static void bt_py3_component_class_finalize(bt_self_component *self_component) +{ + PyObject *py_comp = bt_self_component_get_data(self_component); + BT_ASSERT(py_comp); + + /* Call user's _finalize() method */ + PyObject *py_method_result = PyObject_CallMethod(py_comp, + "_finalize", NULL); + + if (PyErr_Occurred()) { + BT_LOGW("User's _finalize() method raised an exception: ignoring."); + } + + /* + * Ignore any exception raised by the _finalize() method because + * it won't change anything at this point: the component is + * being destroyed anyway. + */ + PyErr_Clear(); + Py_XDECREF(py_method_result); + Py_DECREF(py_comp); +} + +static void +bt_py3_component_class_source_finalize(bt_self_component_source *self_component_source) +{ + bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); + bt_py3_component_class_finalize(self_component); +} + +static void +bt_py3_component_class_filter_finalize(bt_self_component_filter *self_component_filter) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + bt_py3_component_class_finalize(self_component); +} + +static void +bt_py3_component_class_sink_finalize(bt_self_component_sink *self_component_sink) +{ + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + bt_py3_component_class_finalize(self_component); +} + +static bt_self_component_status +bt_py3_component_class_accept_port_connection( + bt_self_component *self_component, + bt_self_component_port *self_component_port, + bt_port_type self_component_port_type, + const bt_port *other_port) +{ + enum bt_self_component_status status; + PyObject *py_comp = NULL; + PyObject *py_self_port_ptr = NULL; + PyObject *py_other_port_ptr = NULL; + PyObject *py_method_result = NULL; + + py_comp = bt_self_component_get_data(self_component); + BT_ASSERT(py_comp); + + swig_type_info *self_component_port_swig_type = NULL; + swig_type_info *other_port_swig_type = NULL; + switch (self_component_port_type) { + case BT_PORT_TYPE_INPUT: + self_component_port_swig_type = SWIGTYPE_p_bt_self_component_port_input; + other_port_swig_type = SWIGTYPE_p_bt_port_output; + break; + case BT_PORT_TYPE_OUTPUT: + self_component_port_swig_type = SWIGTYPE_p_bt_self_component_port_output; + other_port_swig_type = SWIGTYPE_p_bt_port_input; + break; + } + BT_ASSERT(self_component_port_swig_type != NULL); + BT_ASSERT(other_port_swig_type != NULL); + + py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port), + self_component_port_swig_type, 0); + if (!py_self_port_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), + other_port_swig_type, 0); + if (!py_other_port_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_method_result = PyObject_CallMethod(py_comp, + "_accept_port_connection_from_native", "(OiO)", py_self_port_ptr, + self_component_port_type, py_other_port_ptr); + + status = bt_py3_exc_to_self_component_status(); + if (!py_method_result && status == BT_SELF_COMPONENT_STATUS_OK) { + /* Pretty sure this should never happen, but just in case */ + BT_LOGE("User's _accept_port_connection() method failed without raising an exception: " + "status=%d", status); + goto error; + } + + if (status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { + /* + * Looks like the user method raised + * PortConnectionRefused: accept this like if it + * returned False. + */ + goto end; + } else if (status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGE("User's _accept_port_connection() raised an unexpected exception: " + "status=%d", status); + goto error; + } + + BT_ASSERT(PyBool_Check(py_method_result)); + + if (py_method_result == Py_True) { + status = BT_SELF_COMPONENT_STATUS_OK; + } else { + status = BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION; + } + + goto end; + +error: + status = BT_SELF_COMPONENT_STATUS_ERROR; + + /* + * Clear any exception: we're returning a bad status anyway. If + * this call originated from Python, then the user gets an + * appropriate error. + */ + PyErr_Clear(); + +end: + Py_XDECREF(py_self_port_ptr); + Py_XDECREF(py_other_port_ptr); + Py_XDECREF(py_method_result); + return status; +} + +static bt_self_component_status +bt_py3_component_class_source_accept_output_port_connection(bt_self_component_source *self_component_source, + bt_self_component_port_output *self_component_port_output, + const bt_port_input *other_port_input) +{ + bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); + bt_self_component_port *self_component_port = bt_self_component_port_output_as_self_component_port(self_component_port_output); + const bt_port *other_port = bt_port_input_as_port_const(other_port_input); + return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_OUTPUT, other_port); +} + +static bt_self_component_status +bt_py3_component_class_filter_accept_input_port_connection(bt_self_component_filter *self_component_filter, + bt_self_component_port_input *self_component_port_input, + const bt_port_output *other_port_output) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + bt_self_component_port *self_component_port = bt_self_component_port_input_as_self_component_port(self_component_port_input); + const bt_port *other_port = bt_port_output_as_port_const(other_port_output); + return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_INPUT, other_port); +} + +static bt_self_component_status +bt_py3_component_class_filter_accept_output_port_connection(bt_self_component_filter *self_component_filter, + bt_self_component_port_output *self_component_port_output, + const bt_port_input *other_port_input) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + bt_self_component_port *self_component_port = bt_self_component_port_output_as_self_component_port(self_component_port_output); + const bt_port *other_port = bt_port_input_as_port_const(other_port_input); + return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_OUTPUT, other_port); +} + +static bt_self_component_status +bt_py3_component_class_sink_accept_input_port_connection(bt_self_component_sink *self_component_sink, + bt_self_component_port_input *self_component_port_input, + const bt_port_output *other_port_output) +{ + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + bt_self_component_port *self_component_port = bt_self_component_port_input_as_self_component_port(self_component_port_input); + const bt_port *other_port = bt_port_output_as_port_const(other_port_output); + return bt_py3_component_class_accept_port_connection(self_component, self_component_port, BT_PORT_TYPE_INPUT, other_port); +} + +static bt_self_component_status +bt_py3_component_class_port_connected( + bt_self_component *self_component, + void *self_component_port, + swig_type_info *self_component_port_swig_type, + bt_port_type self_component_port_type, + const void *other_port, + swig_type_info *other_port_swig_type) +{ + bt_self_component_status status; + PyObject *py_comp = NULL; + PyObject *py_self_port_ptr = NULL; + PyObject *py_other_port_ptr = NULL; + PyObject *py_method_result = NULL; + + py_comp = bt_self_component_get_data(self_component); + BT_ASSERT(py_comp); + + py_self_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port), + self_component_port_swig_type, 0); + if (!py_self_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + py_other_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(other_port), + other_port_swig_type, 0); + if (!py_other_port_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; } + + py_method_result = PyObject_CallMethod(py_comp, + "_port_connected_from_native", "(OiO)", py_self_port_ptr, + self_component_port_type, py_other_port_ptr); + + BT_ASSERT(!py_method_result || py_method_result == Py_None); + + status = bt_py3_exc_to_self_component_status(); + +end: + Py_XDECREF(py_self_port_ptr); + Py_XDECREF(py_other_port_ptr); + Py_XDECREF(py_method_result); + + return status; +} + +static bt_self_component_status +bt_py3_component_class_source_output_port_connected( + bt_self_component_source *self_component_source, + bt_self_component_port_output *self_component_port_output, + const bt_port_input *other_port_input) +{ + bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); + + return bt_py3_component_class_port_connected( + self_component, + self_component_port_output, + SWIGTYPE_p_bt_self_component_port_output, + BT_PORT_TYPE_OUTPUT, + other_port_input, + SWIGTYPE_p_bt_port_input); +} + +static bt_self_component_status +bt_py3_component_class_filter_input_port_connected( + bt_self_component_filter *self_component_filter, + bt_self_component_port_input *self_component_port_input, + const bt_port_output *other_port_output) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + + return bt_py3_component_class_port_connected( + self_component, + self_component_port_input, + SWIGTYPE_p_bt_self_component_port_input, + BT_PORT_TYPE_INPUT, + other_port_output, + SWIGTYPE_p_bt_port_output); +} + +static bt_self_component_status +bt_py3_component_class_filter_output_port_connected( + bt_self_component_filter *self_component_filter, + bt_self_component_port_output *self_component_port_output, + const bt_port_input *other_port_input) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + + return bt_py3_component_class_port_connected( + self_component, + self_component_port_output, + SWIGTYPE_p_bt_self_component_port_output, + BT_PORT_TYPE_OUTPUT, + other_port_input, + SWIGTYPE_p_bt_port_input); +} + +static bt_self_component_status +bt_py3_component_class_sink_input_port_connected( + bt_self_component_sink *self_component_sink, + bt_self_component_port_input *self_component_port_input, + const bt_port_output *other_port_output) +{ + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + + return bt_py3_component_class_port_connected( + self_component, + self_component_port_input, + SWIGTYPE_p_bt_self_component_port_input, + BT_PORT_TYPE_INPUT, + other_port_output, + SWIGTYPE_p_bt_port_output); +} + +static bt_self_component_status +bt_py3_component_class_sink_graph_is_configured(bt_self_component_sink *self_component_sink) +{ + PyObject *py_comp = NULL; + PyObject *py_method_result = NULL; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + + py_comp = bt_self_component_get_data(self_component); + py_method_result = PyObject_CallMethod(py_comp, + "_graph_is_configured_from_native", NULL); + + BT_ASSERT(!py_method_result || py_method_result == Py_None); + + status = bt_py3_exc_to_self_component_status(); + + Py_XDECREF(py_method_result); + + return status; +} + +static bt_query_status +bt_py3_component_class_query( + const bt_component_class *component_class, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result) +{ + PyObject *py_cls = NULL; + PyObject *py_params_ptr = NULL; + PyObject *py_query_exec_ptr = NULL; + PyObject *py_query_func = NULL; + PyObject *py_object = NULL; + PyObject *py_results_addr = NULL; + bt_query_status status = BT_QUERY_STATUS_OK; + + py_cls = lookup_cc_ptr_to_py_cls(component_class); + if (!py_cls) { + BT_LOGE("Cannot find Python class associated to native component class: " + "comp-cls-addr=%p", component_class); + goto error; + } + + py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params), + SWIGTYPE_p_bt_value, 0); + if (!py_params_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_query_exec_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(query_executor), + SWIGTYPE_p_bt_query_executor, 0); + if (!py_query_exec_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_object = SWIG_FromCharPtr(object); + if (!py_object) { + BT_LOGE_STR("Failed to create a Python string."); + goto error; + } + + py_results_addr = PyObject_CallMethod(py_cls, + "_query_from_native", "(OOO)", py_query_exec_ptr, + py_object, py_params_ptr); + + if (!py_results_addr) { + BT_LOGE("Failed to call Python class's _query_from_native() method: " + "py-cls-addr=%p", py_cls); + status = bt_py3_exc_to_query_status(); + goto end; + } + + /* + * The returned object, on success, is an integer object + * (PyLong) containing the address of a BT value object (new + * reference). + */ + *result = PyLong_AsVoidPtr(py_results_addr); + BT_ASSERT(!PyErr_Occurred()); + BT_ASSERT(*result); + goto end; + +error: + PyErr_Clear(); + status = BT_QUERY_STATUS_ERROR; + +end: + Py_XDECREF(py_params_ptr); + Py_XDECREF(py_query_exec_ptr); + Py_XDECREF(py_query_func); + Py_XDECREF(py_object); + Py_XDECREF(py_results_addr); + return status; +} + +static bt_query_status +bt_py3_component_class_source_query( + bt_self_component_class_source *self_component_class_source, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result) +{ + const bt_component_class_source *component_class_source = bt_self_component_class_source_as_component_class_source(self_component_class_source); + const bt_component_class *component_class = bt_component_class_source_as_component_class_const(component_class_source); + return bt_py3_component_class_query(component_class, query_executor, object, params, result); +} + +static bt_query_status +bt_py3_component_class_filter_query( + bt_self_component_class_filter *self_component_class_filter, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result) +{ + const bt_component_class_filter *component_class_filter = bt_self_component_class_filter_as_component_class_filter(self_component_class_filter); + const bt_component_class *component_class = bt_component_class_filter_as_component_class_const(component_class_filter); + return bt_py3_component_class_query(component_class, query_executor, object, params, result); +} + +static bt_query_status +bt_py3_component_class_sink_query( + bt_self_component_class_sink *self_component_class_sink, + const bt_query_executor *query_executor, + const char *object, const bt_value *params, + const bt_value **result) +{ + const bt_component_class_sink *component_class_sink = bt_self_component_class_sink_as_component_class_sink(self_component_class_sink); + const bt_component_class *component_class = bt_component_class_sink_as_component_class_const(component_class_sink); + return bt_py3_component_class_query(component_class, query_executor, object, params, result); +} + +static bt_self_message_iterator_status +bt_py3_component_class_message_iterator_init( + bt_self_message_iterator *self_message_iterator, + bt_self_component *self_component, + bt_self_component_port_output *self_component_port_output) +{ + bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + PyObject *py_comp_cls = NULL; + PyObject *py_iter_cls = NULL; + PyObject *py_iter_ptr = NULL; + PyObject *py_component_port_output_ptr = NULL; + PyObject *py_init_method_result = NULL; + PyObject *py_iter = NULL; + PyObject *py_comp; + + py_comp = bt_self_component_get_data(self_component); + + /* Find user's Python message iterator class */ + py_comp_cls = PyObject_GetAttrString(py_comp, "__class__"); + if (!py_comp_cls) { + BT_LOGE_STR("Cannot get Python object's `__class__` attribute."); + goto error; + } + + py_iter_cls = PyObject_GetAttrString(py_comp_cls, "_iter_cls"); + if (!py_iter_cls) { + BT_LOGE_STR("Cannot get Python class's `_iter_cls` attribute."); + goto error; + } + + py_iter_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_message_iterator), + SWIGTYPE_p_bt_self_message_iterator, 0); + if (!py_iter_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + /* + * Create object with borrowed native message iterator + * reference: + * + * py_iter = py_iter_cls.__new__(py_iter_cls, py_iter_ptr) + */ + py_iter = PyObject_CallMethod(py_iter_cls, "__new__", + "(OO)", py_iter_cls, py_iter_ptr); + if (!py_iter) { + BT_LOGE("Failed to call Python class's __new__() method: " + "py-cls-addr=%p", py_iter_cls); + bt2_py_loge_exception(); + goto error; + } + + /* + * Initialize object: + * + * py_iter.__init__(self_output_port) + * + * through the _init_for_native helper static method. + * + * At this point, py_iter._ptr is set, so this initialization + * function has access to self._component (which gives it the + * user Python component object from which the iterator was + * created). + */ + py_component_port_output_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(self_component_port_output), + SWIGTYPE_p_bt_self_component_port_output, 0); + if (!py_component_port_output_ptr) { + BT_LOGE_STR("Failed to create a SWIG pointer object."); + goto error; + } + + py_init_method_result = PyObject_CallMethod(py_iter, "_init_from_native", "O", py_component_port_output_ptr); + if (!py_init_method_result) { + BT_LOGE_STR("User's __init__() method failed."); + bt2_py_loge_exception(); + goto error; + } + + /* + * Since the Python code can never instantiate a user-defined + * message iterator class, the native message iterator + * object does NOT belong to a user Python message iterator + * object (borrowed reference). However this Python object is + * owned by this native message iterator object. + * + * In the Python world, the lifetime of the native message + * iterator is managed by a _GenericMessageIterator + * instance: + * + * _GenericMessageIterator instance: + * owns a native bt_message_iterator object (iter) + * owns a _UserMessageIterator instance (py_iter) + * self._ptr is a borrowed reference to the + * native bt_private_connection_private_message_iterator + * object (iter) + */ + bt_self_message_iterator_set_data(self_message_iterator, py_iter); + py_iter = NULL; + goto end; + +error: + status = bt_py3_exc_to_self_message_iterator_status(); + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + /* + * Looks like there wasn't any exception from the Python + * side, but we're still in an error state here. + */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } + + /* + * Clear any exception: we're returning a bad status anyway. If + * this call originated from Python, then the user gets an + * appropriate creation error. + */ + PyErr_Clear(); + +end: + Py_XDECREF(py_comp_cls); + Py_XDECREF(py_iter_cls); + Py_XDECREF(py_iter_ptr); + Py_XDECREF(py_component_port_output_ptr); + Py_XDECREF(py_init_method_result); + Py_XDECREF(py_iter); + return status; +} + +static bt_self_message_iterator_status +bt_py3_component_class_source_message_iterator_init( + bt_self_message_iterator *self_message_iterator, + bt_self_component_source *self_component_source, + bt_self_component_port_output *self_component_port_output) +{ + bt_self_component *self_component = bt_self_component_source_as_self_component(self_component_source); + return bt_py3_component_class_message_iterator_init(self_message_iterator, self_component, self_component_port_output); +} + +static bt_self_message_iterator_status +bt_py3_component_class_filter_message_iterator_init( + bt_self_message_iterator *self_message_iterator, + bt_self_component_filter *self_component_filter, + bt_self_component_port_output *self_component_port_output) +{ + bt_self_component *self_component = bt_self_component_filter_as_self_component(self_component_filter); + return bt_py3_component_class_message_iterator_init(self_message_iterator, self_component, self_component_port_output); +} + +static void +bt_py3_component_class_message_iterator_finalize( + bt_self_message_iterator *message_iterator) +{ + PyObject *py_message_iter = bt_self_message_iterator_get_data(message_iterator); + PyObject *py_method_result = NULL; + + BT_ASSERT(py_message_iter); + + /* Call user's _finalize() method */ + py_method_result = PyObject_CallMethod(py_message_iter, + "_finalize", NULL); + + if (PyErr_Occurred()) { + BT_LOGW("User's _finalize() method raised an exception: ignoring."); + } + + /* + * Ignore any exception raised by the _finalize() method because + * it won't change anything at this point: the component is + * being destroyed anyway. + */ + PyErr_Clear(); + Py_XDECREF(py_method_result); + Py_DECREF(py_message_iter); +} + +/* Valid for both sources and filters. */ + +static bt_self_message_iterator_status +bt_py3_component_class_message_iterator_next( + bt_self_message_iterator *message_iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + PyObject *py_message_iter = bt_self_message_iterator_get_data(message_iterator); + PyObject *py_method_result = NULL; + + BT_ASSERT(py_message_iter); + py_method_result = PyObject_CallMethod(py_message_iter, + "_next_from_native", NULL); + if (!py_method_result) { + status = bt_py3_exc_to_self_message_iterator_status(); + BT_ASSERT(status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK); + goto end; + } + + /* + * The returned object, on success, is an integer object + * (PyLong) containing the address of a native message + * object (which is now ours). + */ + msgs[0] = PyLong_AsVoidPtr(py_method_result); + *count = 1; + + /* Clear potential overflow error; should never happen */ + BT_ASSERT(!PyErr_Occurred()); + goto end; + +end: + Py_XDECREF(py_method_result); + return status; +} + +static bt_self_component_status +bt_py3_component_class_sink_consume(bt_self_component_sink *self_component_sink) +{ + bt_self_component *self_component = bt_self_component_sink_as_self_component(self_component_sink); + PyObject *py_comp = bt_self_component_get_data(self_component); + PyObject *py_method_result = NULL; + bt_self_component_status status; + + BT_ASSERT(py_comp); + py_method_result = PyObject_CallMethod(py_comp, + "_consume", NULL); + + status = bt_py3_exc_to_self_component_status(); + if (!py_method_result && status == BT_SELF_COMPONENT_STATUS_OK) { + /* Pretty sure this should never happen, but just in case */ + BT_LOGE("User's _consume() method failed without raising an exception: " + "status=%d", status); + status = BT_SELF_COMPONENT_STATUS_ERROR; + } + + Py_XDECREF(py_method_result); + return status; +} + +static +int bt_py3_component_class_set_help_and_desc( + bt_component_class *component_class, + const char *description, const char *help) +{ + int ret; + + if (description) { + ret = bt_component_class_set_description(component_class, description); + if (ret) { + BT_LOGE("Cannot set component class's description: " + "comp-cls-addr=%p", component_class); + goto end; + } + } + + if (help) { + ret = bt_component_class_set_help(component_class, help); + if (ret) { + BT_LOGE("Cannot set component class's help text: " + "comp-cls-addr=%p", component_class); + goto end; + } + } + + ret = 0; + +end: + return ret; +} + +static +bt_component_class_source *bt_py3_component_class_source_create( + PyObject *py_cls, const char *name, const char *description, + const char *help) +{ + bt_component_class_source *component_class_source; + bt_component_class *component_class; + int ret; + + BT_ASSERT(py_cls); + + component_class_source = bt_component_class_source_create(name, + bt_py3_component_class_message_iterator_next); + if (!component_class_source) { + BT_LOGE_STR("Cannot create source component class."); + goto end; + } + + component_class = bt_component_class_source_as_component_class(component_class_source); + + if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { + goto end; + } + + ret = bt_component_class_source_set_init_method(component_class_source, bt_py3_component_class_source_init); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_finalize_method (component_class_source, bt_py3_component_class_source_finalize); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_accept_output_port_connection_method(component_class_source, + bt_py3_component_class_source_accept_output_port_connection); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_output_port_connected_method(component_class_source, + bt_py3_component_class_source_output_port_connected); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_query_method(component_class_source, bt_py3_component_class_source_query); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_message_iterator_init_method( + component_class_source, bt_py3_component_class_source_message_iterator_init); + BT_ASSERT(ret == 0); + ret = bt_component_class_source_set_message_iterator_finalize_method( + component_class_source, bt_py3_component_class_message_iterator_finalize); + BT_ASSERT(ret == 0); + + register_cc_ptr_to_py_cls(component_class, py_cls); + +end: + return component_class_source; +} + +static +bt_component_class_filter *bt_py3_component_class_filter_create( + PyObject *py_cls, const char *name, const char *description, + const char *help) +{ + bt_component_class *component_class; + bt_component_class_filter *component_class_filter; + int ret; + + BT_ASSERT(py_cls); + + component_class_filter = bt_component_class_filter_create(name, + bt_py3_component_class_message_iterator_next); + if (!component_class_filter) { + BT_LOGE_STR("Cannot create filter component class."); + goto end; + } + + component_class = bt_component_class_filter_as_component_class(component_class_filter); + + if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { + goto end; + } + + ret = bt_component_class_filter_set_init_method(component_class_filter, bt_py3_component_class_filter_init); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_finalize_method (component_class_filter, bt_py3_component_class_filter_finalize); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_accept_input_port_connection_method(component_class_filter, + bt_py3_component_class_filter_accept_input_port_connection); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_accept_output_port_connection_method(component_class_filter, + bt_py3_component_class_filter_accept_output_port_connection); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_input_port_connected_method(component_class_filter, + bt_py3_component_class_filter_input_port_connected); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_output_port_connected_method(component_class_filter, + bt_py3_component_class_filter_output_port_connected); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_query_method(component_class_filter, bt_py3_component_class_filter_query); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_message_iterator_init_method( + component_class_filter, bt_py3_component_class_filter_message_iterator_init); + BT_ASSERT(ret == 0); + ret = bt_component_class_filter_set_message_iterator_finalize_method( + component_class_filter, bt_py3_component_class_message_iterator_finalize); + BT_ASSERT(ret == 0); + + register_cc_ptr_to_py_cls(component_class, py_cls); + +end: + return component_class_filter; +} + +static +bt_component_class_sink *bt_py3_component_class_sink_create( + PyObject *py_cls, const char *name, const char *description, + const char *help) +{ + bt_component_class_sink *component_class_sink; + bt_component_class *component_class; + int ret; + + BT_ASSERT(py_cls); + + component_class_sink = bt_component_class_sink_create(name, bt_py3_component_class_sink_consume); + + if (!component_class_sink) { + BT_LOGE_STR("Cannot create sink component class."); + goto end; + } + + component_class = bt_component_class_sink_as_component_class(component_class_sink); + + if (bt_py3_component_class_set_help_and_desc(component_class, description, help)) { + goto end; + } + + ret = bt_component_class_sink_set_init_method(component_class_sink, bt_py3_component_class_sink_init); + BT_ASSERT(ret == 0); + ret = bt_component_class_sink_set_finalize_method(component_class_sink, bt_py3_component_class_sink_finalize); + BT_ASSERT(ret == 0); + ret = bt_component_class_sink_set_accept_input_port_connection_method(component_class_sink, + bt_py3_component_class_sink_accept_input_port_connection); + BT_ASSERT(ret == 0); + ret = bt_component_class_sink_set_input_port_connected_method(component_class_sink, + bt_py3_component_class_sink_input_port_connected); + BT_ASSERT(ret == 0); + ret = bt_component_class_sink_set_graph_is_configured_method(component_class_sink, + bt_py3_component_class_sink_graph_is_configured); + BT_ASSERT(ret == 0); + ret = bt_component_class_sink_set_query_method(component_class_sink, bt_py3_component_class_sink_query); + BT_ASSERT(ret == 0); + + register_cc_ptr_to_py_cls(component_class, py_cls); + +end: + return component_class_sink; +} +%} + +struct bt_component_class_source *bt_py3_component_class_source_create( + PyObject *py_cls, const char *name, const char *description, + const char *help); +struct bt_component_class_filter *bt_py3_component_class_filter_create( + PyObject *py_cls, const char *name, const char *description, + const char *help); +struct bt_component_class_sink *bt_py3_component_class_sink_create( + PyObject *py_cls, const char *name, const char *description, + const char *help); +void bt_py3_cc_init_from_bt2(void); +void bt_py3_cc_exit_handler(void); diff --git a/src/bindings/python/bt2/bt2/native_bt_connection.i b/src/bindings/python/bt2/bt2/native_bt_connection.i new file mode 100644 index 00000000..dbdb04a5 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_connection.i @@ -0,0 +1,35 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From connection-const.h */ + +extern const bt_port_input *bt_connection_borrow_downstream_port_const( + const bt_connection *connection); + +extern const bt_port_output *bt_connection_borrow_upstream_port_const( + const bt_connection *connection); + +extern void bt_connection_get_ref(const bt_connection *connection); + +extern void bt_connection_put_ref(const bt_connection *connection); diff --git a/src/bindings/python/bt2/bt2/native_bt_event.i b/src/bindings/python/bt2/bt2/native_bt_event.i new file mode 100644 index 00000000..82ddbfae --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_event.i @@ -0,0 +1,64 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From event-const.h */ + +typedef enum bt_event_status { + BT_EVENT_STATUS_OK = 0, + BT_EVENT_STATUS_NOMEM = -12, +} bt_event_status; + +extern const bt_event_class *bt_event_borrow_class_const( + const bt_event *event); + +extern const bt_packet *bt_event_borrow_packet_const( + const bt_event *event); + +extern const bt_stream *bt_event_borrow_stream_const( + const bt_event *event); + +extern const bt_field *bt_event_borrow_common_context_field_const( + const bt_event *event); + +extern const bt_field *bt_event_borrow_specific_context_field_const( + const bt_event *event); + +extern const bt_field *bt_event_borrow_payload_field_const( + const bt_event *event); + +/* From event.h */ + +extern bt_event_class *bt_event_borrow_class(bt_event *event); + +extern bt_packet *bt_event_borrow_packet(bt_event *event); + +extern bt_stream *bt_event_borrow_stream(bt_event *event); + +extern bt_field * +bt_event_borrow_common_context_field(bt_event *event); + +extern bt_field * +bt_event_borrow_specific_context_field(bt_event *event); + +extern bt_field *bt_event_borrow_payload_field(bt_event *event); diff --git a/src/bindings/python/bt2/bt2/native_bt_event_class.i b/src/bindings/python/bt2/bt2/native_bt_event_class.i new file mode 100644 index 00000000..cac0c411 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_event_class.i @@ -0,0 +1,121 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Output argument typemap for initialized event class log level output + * parameter (always appends). + */ +%typemap(in, numinputs=0) + (bt_event_class_log_level *OUT) + (bt_event_class_log_level temp = -1) { + $1 = &temp; +} + +%typemap(argout) bt_event_class_log_level *OUT { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, SWIG_From_int(*$1)); +} + +/* From event-class-const.h */ + +typedef enum bt_event_class_status { + BT_EVENT_CLASS_STATUS_OK = 0, + BT_EVENT_CLASS_STATUS_NOMEM = -12, +} bt_event_class_status; + +typedef enum bt_event_class_log_level { + BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY, + BT_EVENT_CLASS_LOG_LEVEL_ALERT, + BT_EVENT_CLASS_LOG_LEVEL_CRITICAL, + BT_EVENT_CLASS_LOG_LEVEL_ERROR, + BT_EVENT_CLASS_LOG_LEVEL_WARNING, + BT_EVENT_CLASS_LOG_LEVEL_NOTICE, + BT_EVENT_CLASS_LOG_LEVEL_INFO, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE, + BT_EVENT_CLASS_LOG_LEVEL_DEBUG, +} bt_event_class_log_level; + +extern const bt_stream_class *bt_event_class_borrow_stream_class_const( + const bt_event_class *event_class); + +extern const char *bt_event_class_get_name(const bt_event_class *event_class); + +extern uint64_t bt_event_class_get_id(const bt_event_class *event_class); + +extern bt_property_availability bt_event_class_get_log_level( + const bt_event_class *event_class, + bt_event_class_log_level *OUT); + +extern const char *bt_event_class_get_emf_uri( + const bt_event_class *event_class); + +extern const bt_field_class * +bt_event_class_borrow_specific_context_field_class_const( + const bt_event_class *event_class); + +extern const bt_field_class *bt_event_class_borrow_payload_field_class_const( + const bt_event_class *event_class); + +extern void bt_event_class_get_ref(const bt_event_class *event_class); + +extern void bt_event_class_put_ref(const bt_event_class *event_class); + +/* From event-class.h */ + +extern bt_event_class *bt_event_class_create( + bt_stream_class *stream_class); + +extern bt_event_class *bt_event_class_create_with_id( + bt_stream_class *stream_class, uint64_t id); + +extern bt_stream_class *bt_event_class_borrow_stream_class( + bt_event_class *event_class); + +extern bt_event_class_status bt_event_class_set_name( + bt_event_class *event_class, const char *name); + +extern void bt_event_class_set_log_level(bt_event_class *event_class, + bt_event_class_log_level log_level); + +extern bt_event_class_status bt_event_class_set_emf_uri( + bt_event_class *event_class, const char *emf_uri); + +extern bt_event_class_status +bt_event_class_set_specific_context_field_class(bt_event_class *event_class, + bt_field_class *field_class); + +extern bt_field_class * +bt_event_class_borrow_specific_context_field_class(bt_event_class *event_class); + +extern bt_event_class_status bt_event_class_set_payload_field_class( + bt_event_class *event_class, + bt_field_class *field_class); + +extern bt_field_class *bt_event_class_borrow_payload_field_class( + bt_event_class *event_class); diff --git a/src/bindings/python/bt2/bt2/native_bt_field.i b/src/bindings/python/bt2/bt2/native_bt_field.i new file mode 100644 index 00000000..f4511f45 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_field.i @@ -0,0 +1,120 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* For label type mappings. */ +%include "native_bt_field_class.i" + +/* From field-const.h */ + +typedef enum bt_field_status { + BT_FIELD_STATUS_OK = 0, + BT_FIELD_STATUS_NOMEM = -12, +} bt_field_status; + +extern const bt_field_class *bt_field_borrow_class_const( + const bt_field *field); + +extern bt_field_class_type bt_field_get_class_type( + const bt_field *field); + +extern int64_t bt_field_signed_integer_get_value(const bt_field *field); + +extern uint64_t bt_field_unsigned_integer_get_value( + const bt_field *field); + +extern double bt_field_real_get_value(const bt_field *field); + +extern bt_field_status bt_field_unsigned_enumeration_get_mapping_labels( + const bt_field *field, + bt_field_class_enumeration_mapping_label_array *LABELARRAY, + uint64_t *LABELCOUNT); + +extern bt_field_status bt_field_signed_enumeration_get_mapping_labels( + const bt_field *field, + bt_field_class_enumeration_mapping_label_array *LABELARRAY, + uint64_t *LABELCOUNT); + +extern const char *bt_field_string_get_value(const bt_field *field); + +extern uint64_t bt_field_string_get_length(const bt_field *field); + +extern const bt_field * +bt_field_structure_borrow_member_field_by_index_const( + const bt_field *field, uint64_t index); + +extern const bt_field * +bt_field_structure_borrow_member_field_by_name_const( + const bt_field *field, const char *name); + +extern uint64_t bt_field_array_get_length(const bt_field *field); + +extern const bt_field * +bt_field_array_borrow_element_field_by_index_const( + const bt_field *field, uint64_t index); + +extern uint64_t bt_field_variant_get_selected_option_field_index( + const bt_field *field); + +extern const bt_field * +bt_field_variant_borrow_selected_option_field_const( + const bt_field *field); + +/* From field.h */ + +extern void bt_field_signed_integer_set_value(bt_field *field, + int64_t value); + +extern void bt_field_unsigned_integer_set_value(bt_field *field, + uint64_t value); + +extern void bt_field_real_set_value(bt_field *field, double value); + +extern bt_field_status bt_field_string_set_value(bt_field *field, + const char *value); + +extern bt_field_status bt_field_string_append(bt_field *field, + const char *value); + +extern bt_field_status bt_field_string_append_with_length(bt_field *field, + const char *value, uint64_t length); + +extern bt_field_status bt_field_string_clear(bt_field *field); + +extern bt_field *bt_field_structure_borrow_member_field_by_index( + bt_field *field, uint64_t index); + +extern bt_field *bt_field_structure_borrow_member_field_by_name( + bt_field *field, const char *name); + +extern bt_field *bt_field_array_borrow_element_field_by_index( + bt_field *field, uint64_t index); + +extern bt_field_status bt_field_dynamic_array_set_length(bt_field *field, + uint64_t length); + +extern bt_field_status bt_field_variant_select_option_field( + bt_field *field, uint64_t index); + +extern bt_field *bt_field_variant_borrow_selected_option_field( + bt_field *field); diff --git a/src/bindings/python/bt2/bt2/native_bt_field_class.i b/src/bindings/python/bt2/bt2/native_bt_field_class.i new file mode 100644 index 00000000..b6173c06 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_field_class.i @@ -0,0 +1,320 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +%typemap(in, numinputs=0) + (bt_field_class_enumeration_mapping_label_array *LABELARRAY, uint64_t *LABELCOUNT) + (bt_field_class_enumeration_mapping_label_array temp_array, uint64_t temp_label_count = 0) { + $1 = &temp_array; + $2 = &temp_label_count; +} + +%typemap(argout) + (bt_field_class_enumeration_mapping_label_array *LABELARRAY, uint64_t *LABELCOUNT) { + if (*$1) { + PyObject *py_label_list = PyList_New(*$2); + for (int i = 0; i < *$2; i++) { + PyList_SET_ITEM(py_label_list, i, PyUnicode_FromString((*$1)[i])); + } + + $result = SWIG_Python_AppendOutput($result, py_label_list); + } else { + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for value output (always appends) */ +%typemap(in, numinputs=0) + (const bt_field_class_signed_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) + (bt_field_class_signed_enumeration_mapping_ranges *temp_value = NULL) { + $1 = &temp_value; +} + +%typemap(argout) + (const bt_field_class_signed_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_field_class_signed_enumeration_mapping_ranges, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for value output (always appends) */ +%typemap(in, numinputs=0) + (const bt_field_class_unsigned_enumeration_mapping_ranges **ENUM_RANGE_MAPPING) + (bt_field_class_unsigned_enumeration_mapping_ranges *temp_value = NULL) { + $1 = &temp_value; +} + +%typemap(argout) + (const bt_field_class_unsigned_enumeration_mapping_ranges **ENUM_RANGE_MAPPING ) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_field_class_unsigned_enumeration_mapping_ranges, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* From field-class-const.h */ + +typedef enum bt_field_class_status { + BT_FIELD_CLASS_STATUS_OK = 0, + BT_FIELD_CLASS_STATUS_NOMEM = -12, +} bt_field_class_status; + +typedef enum bt_field_class_type { + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER, + BT_FIELD_CLASS_TYPE_SIGNED_INTEGER, + BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, + BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, + BT_FIELD_CLASS_TYPE_REAL, + BT_FIELD_CLASS_TYPE_STRING, + BT_FIELD_CLASS_TYPE_STRUCTURE, + BT_FIELD_CLASS_TYPE_STATIC_ARRAY, + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, + BT_FIELD_CLASS_TYPE_VARIANT, +} bt_field_class_type; + +typedef enum bt_field_class_integer_preferred_display_base { + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL, +} bt_field_class_integer_preferred_display_base; + +extern bt_field_class_type bt_field_class_get_type( + const bt_field_class *field_class); + +extern uint64_t bt_field_class_integer_get_field_value_range( + const bt_field_class *field_class); + +extern bt_field_class_integer_preferred_display_base +bt_field_class_integer_get_preferred_display_base( + const bt_field_class *field_class); + +extern bt_bool bt_field_class_real_is_single_precision( + const bt_field_class *field_class); + +extern uint64_t bt_field_class_enumeration_get_mapping_count( + const bt_field_class *field_class); + +extern const bt_field_class_unsigned_enumeration_mapping * +bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( + const bt_field_class *field_class, uint64_t index); + +extern const bt_field_class_signed_enumeration_mapping * +bt_field_class_signed_enumeration_borrow_mapping_by_index_const( + const bt_field_class *field_class, uint64_t index); + +const bt_field_class_enumeration_mapping * +bt_field_class_unsigned_enumeration_mapping_as_mapping_const( + const bt_field_class_unsigned_enumeration_mapping *mapping); + +const bt_field_class_enumeration_mapping * +bt_field_class_signed_enumeration_mapping_as_mapping_const( + const bt_field_class_signed_enumeration_mapping *mapping); + +extern const char *bt_field_class_enumeration_mapping_get_label( + const bt_field_class_enumeration_mapping *mapping); + +extern uint64_t bt_field_class_enumeration_mapping_get_range_count( + const bt_field_class_enumeration_mapping *mapping); + +extern void +bt_field_class_unsigned_enumeration_mapping_get_range_by_index( + const bt_field_class_unsigned_enumeration_mapping *mapping, + uint64_t index, uint64_t *OUT, uint64_t *OUT); + +extern void +bt_field_class_signed_enumeration_mapping_get_range_by_index( + const bt_field_class_signed_enumeration_mapping *mapping, + uint64_t index, int64_t *OUT, int64_t *OUT); + +extern bt_field_class_status +bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( + const bt_field_class *field_class, uint64_t value, + bt_field_class_enumeration_mapping_label_array *LABELARRAY, + uint64_t *LABELCOUNT); + +extern bt_field_class_status +bt_field_class_signed_enumeration_get_mapping_labels_by_value( + const bt_field_class *field_class, int64_t value, + bt_field_class_enumeration_mapping_label_array *LABELARRAY, + uint64_t *LABELCOUNT); + +extern uint64_t bt_field_class_structure_get_member_count( + const bt_field_class *field_class); + +extern const bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_index_const( + const bt_field_class *field_class, uint64_t index); + +extern const bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_name_const( + const bt_field_class *field_class, const char *name); + +extern const char *bt_field_class_structure_member_get_name( + const bt_field_class_structure_member *member); + +extern const bt_field_class * +bt_field_class_structure_member_borrow_field_class_const( + const bt_field_class_structure_member *member); + +extern const bt_field_class * +bt_field_class_array_borrow_element_field_class_const( + const bt_field_class *field_class); + +extern uint64_t bt_field_class_static_array_get_length( + const bt_field_class *field_class); + +extern const bt_field_path * +bt_field_class_dynamic_array_borrow_length_field_path_const( + const bt_field_class *field_class); + +extern const bt_field_path * +bt_field_class_variant_borrow_selector_field_path_const( + const bt_field_class *field_class); + +extern uint64_t bt_field_class_variant_get_option_count( + const bt_field_class *field_class); + +extern const bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_index_const( + const bt_field_class *field_class, uint64_t index); + +extern const bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_name_const( + const bt_field_class *field_class, const char *name); + +extern const char *bt_field_class_variant_option_get_name( + const bt_field_class_variant_option *option); + +extern const bt_field_class * +bt_field_class_variant_option_borrow_field_class_const( + const bt_field_class_variant_option *option); + +extern void bt_field_class_get_ref(const bt_field_class *field_class); + +extern void bt_field_class_put_ref(const bt_field_class *field_class); + +/* From field-class.h */ + +extern bt_field_class *bt_field_class_unsigned_integer_create( + bt_trace_class *trace_class); + +extern bt_field_class *bt_field_class_signed_integer_create( + bt_trace_class *trace_class); + +extern void bt_field_class_integer_set_field_value_range( + bt_field_class *field_class, uint64_t size); + +extern void bt_field_class_integer_set_preferred_display_base( + bt_field_class *field_class, + bt_field_class_integer_preferred_display_base base); + +extern bt_field_class *bt_field_class_real_create(bt_trace_class *trace_class); + +extern void bt_field_class_real_set_is_single_precision( + bt_field_class *field_class, + bt_bool is_single_precision); + +extern bt_field_class *bt_field_class_unsigned_enumeration_create( + bt_trace_class *trace_class); + +extern bt_field_class *bt_field_class_signed_enumeration_create( + bt_trace_class *trace_class); + +extern bt_field_class_status bt_field_class_unsigned_enumeration_map_range( + bt_field_class *field_class, const char *label, + uint64_t range_lower, uint64_t range_upper); + +extern bt_field_class_status bt_field_class_signed_enumeration_map_range( + bt_field_class *field_class, const char *label, + int64_t range_lower, int64_t range_upper); + +extern bt_field_class *bt_field_class_string_create( + bt_trace_class *trace_class); + +extern bt_field_class *bt_field_class_structure_create( + bt_trace_class *trace_class); + +extern bt_field_class_status bt_field_class_structure_append_member( + bt_field_class *struct_field_class, + const char *name, bt_field_class *field_class); + +extern bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_index( + bt_field_class *field_class, uint64_t index); + +extern bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_name( + bt_field_class *field_class, const char *name); + +extern bt_field_class *bt_field_class_static_array_create( + bt_trace_class *trace_class, + bt_field_class *elem_field_class, uint64_t length); + +extern bt_field_class *bt_field_class_dynamic_array_create( + bt_trace_class *trace_class, + bt_field_class *elem_field_class); + +extern bt_field_class *bt_field_class_array_borrow_element_field_class( + bt_field_class *field_class); + +extern bt_field_class_status +bt_field_class_dynamic_array_set_length_field_class( + bt_field_class *field_class, + bt_field_class *length_field_class); + +extern bt_field_class *bt_field_class_variant_create( + bt_trace_class *trace_class); + +extern bt_field_class_status +bt_field_class_variant_set_selector_field_class(bt_field_class *field_class, + bt_field_class *selector_field_class); + +extern bt_field_class_status bt_field_class_variant_append_option( + bt_field_class *var_field_class, + const char *name, bt_field_class *field_class); + +extern bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_index( + bt_field_class *field_class, uint64_t index); + +extern bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_name( + bt_field_class *field_class, char *name); + +extern bt_field_class *bt_field_class_variant_option_borrow_field_class( + bt_field_class_variant_option *option); diff --git a/src/bindings/python/bt2/bt2/native_bt_field_path.i b/src/bindings/python/bt2/bt2/native_bt_field_path.i new file mode 100644 index 00000000..5369f442 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_field_path.i @@ -0,0 +1,56 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From field-path-const.h */ + +typedef enum bt_field_path_item_type { + BT_FIELD_PATH_ITEM_TYPE_INDEX, + BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT, +} bt_field_path_item_type; + +typedef enum bt_scope { + BT_SCOPE_PACKET_CONTEXT, + BT_SCOPE_EVENT_COMMON_CONTEXT, + BT_SCOPE_EVENT_SPECIFIC_CONTEXT, + BT_SCOPE_EVENT_PAYLOAD, +} bt_scope; + +extern bt_scope bt_field_path_get_root_scope( + const bt_field_path *field_path); + +extern uint64_t bt_field_path_get_item_count( + const bt_field_path *field_path); + +extern const bt_field_path_item *bt_field_path_borrow_item_by_index_const( + const bt_field_path *field_path, uint64_t index); + +extern bt_field_path_item_type bt_field_path_item_get_type( + const bt_field_path_item *field_path_item); + +extern uint64_t bt_field_path_item_index_get_index( + const bt_field_path_item *field_path_item); + +extern void bt_field_path_get_ref(const bt_field_path *field_path); + +extern void bt_field_path_put_ref(const bt_field_path *field_path); diff --git a/src/bindings/python/bt2/bt2/native_bt_graph.i b/src/bindings/python/bt2/bt2/native_bt_graph.i new file mode 100644 index 00000000..bb7a5d13 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_graph.i @@ -0,0 +1,723 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Output argument typemap for connection output (always appends) */ +%typemap(in, numinputs=0) + (const bt_connection **BTOUTCONN) + (bt_connection *temp_conn = NULL) { + $1 = &temp_conn; +} + +%typemap(argout) + (const bt_connection **BTOUTCONN) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_connection, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* Output argument typemap for component output (always appends) */ +%typemap(in, numinputs=0) + (const bt_component_source **OUT) + (bt_component_source *temp_comp = NULL) { + $1 = &temp_comp; +} + +%typemap(in, numinputs=0) + (const bt_component_filter **OUT) + (bt_component_filter *temp_comp = NULL) { + $1 = &temp_comp; +} + +%typemap(in, numinputs=0) + (const bt_component_sink **OUT) + (bt_component_sink *temp_comp = NULL) { + $1 = &temp_comp; +} + +%typemap(argout) (const bt_component_source **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_component_source, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +%typemap(argout) (const bt_component_filter **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_component_filter, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +%typemap(argout) (const bt_component_sink **OUT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_component_sink, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* From graph-const.h */ + +typedef enum bt_graph_status { + BT_GRAPH_STATUS_OK = 0, + BT_GRAPH_STATUS_END = 1, + BT_GRAPH_STATUS_AGAIN = 11, + BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION = 111, + BT_GRAPH_STATUS_CANCELED = 125, + BT_GRAPH_STATUS_ERROR = -1, + BT_GRAPH_STATUS_NOMEM = -12, +} bt_graph_status; + +extern bt_bool bt_graph_is_canceled(const bt_graph *graph); + +extern void bt_graph_get_ref(const bt_graph *graph); + +extern void bt_graph_put_ref(const bt_graph *graph); + +/* From graph.h */ + +typedef enum bt_graph_listener_status { + BT_GRAPH_LISTENER_STATUS_OK = 0, + BT_GRAPH_LISTENER_STATUS_ERROR = -1, + BT_GRAPH_LISTENER_STATUS_NOMEM = -12, +} bt_graph_listener_status; + + +typedef bt_graph_listener_status +(*bt_graph_filter_component_input_port_added_listener_func)( + const bt_component_filter *component, + const bt_port_input *port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_sink_component_input_port_added_listener_func)( + const bt_component_sink *component, + const bt_port_input *port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_source_component_output_port_added_listener_func)( + const bt_component_source *component, + const bt_port_output *port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_filter_component_output_port_added_listener_func)( + const bt_component_filter *component, + const bt_port_output *port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_source_filter_component_ports_connected_listener_func)( + const bt_component_source *source_component, + const bt_component_filter *filter_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_source_sink_component_ports_connected_listener_func)( + const bt_component_source *source_component, + const bt_component_sink *sink_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *data); + +typedef bt_graph_listener_status +(*bt_graph_filter_filter_component_ports_connected_listener_func)( + const bt_component_filter *filter_component_upstream, + const bt_component_filter *filter_component_downstream, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, + void *data); + +typedef bt_graph_listener_status +(*bt_graph_filter_sink_component_ports_connected_listener_func)( + const bt_component_filter *filter_component, + const bt_component_sink *sink_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *data); + +typedef void (* bt_graph_listener_removed_func)(void *data); + +extern bt_graph *bt_graph_create(void); + +extern bt_graph_status bt_graph_add_source_component(bt_graph *graph, + const bt_component_class_source *component_class, + const char *name, const bt_value *params, + const bt_component_source **OUT); + +extern bt_graph_status bt_graph_add_source_component_with_init_method_data( + bt_graph *graph, + const bt_component_class_source *component_class, + const char *name, const bt_value *params, + void *init_method_data, + const bt_component_source **OUT); + +extern bt_graph_status bt_graph_add_filter_component(bt_graph *graph, + const bt_component_class_filter *component_class, + const char *name, const bt_value *params, + const bt_component_filter **OUT); + +extern bt_graph_status bt_graph_add_filter_component_with_init_method_data( + bt_graph *graph, + const bt_component_class_filter *component_class, + const char *name, const bt_value *params, + void *init_method_data, + const bt_component_filter **OUT); + +extern bt_graph_status bt_graph_add_sink_component( + bt_graph *graph, const bt_component_class_sink *component_class, + const char *name, const bt_value *params, + const bt_component_sink **OUT); + +extern bt_graph_status bt_graph_add_sink_component_with_init_method_data( + bt_graph *graph, const bt_component_class_sink *component_class, + const char *name, const bt_value *params, + void *init_method_data, + const bt_component_sink **OUT); + +extern bt_graph_status bt_graph_connect_ports(bt_graph *graph, + const bt_port_output *upstream, + const bt_port_input *downstream, + const bt_connection **BTOUTCONN); + +extern bt_graph_status bt_graph_run(bt_graph *graph); + +extern bt_graph_status bt_graph_consume(bt_graph *graph); + +extern bt_graph_status bt_graph_add_filter_component_input_port_added_listener( + bt_graph *graph, + bt_graph_filter_component_input_port_added_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status bt_graph_add_sink_component_input_port_added_listener( + bt_graph *graph, + bt_graph_sink_component_input_port_added_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status bt_graph_add_source_component_output_port_added_listener( + bt_graph *graph, + bt_graph_source_component_output_port_added_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status bt_graph_add_filter_component_output_port_added_listener( + bt_graph *graph, + bt_graph_filter_component_output_port_added_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status +bt_graph_add_source_filter_component_ports_connected_listener( + bt_graph *graph, + bt_graph_source_filter_component_ports_connected_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status +bt_graph_add_filter_filter_component_ports_connected_listener( + bt_graph *graph, + bt_graph_filter_filter_component_ports_connected_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status +bt_graph_add_source_sink_component_ports_connected_listener( + bt_graph *graph, + bt_graph_source_sink_component_ports_connected_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status +bt_graph_add_filter_sink_component_ports_connected_listener( + bt_graph *graph, + bt_graph_filter_sink_component_ports_connected_listener_func listener, + bt_graph_listener_removed_func listener_removed, void *data, + int *listener_id); + +extern bt_graph_status bt_graph_cancel(bt_graph *graph); + +/* Helper functions for Python */ + +%{ + +static void graph_listener_removed(void *py_callable) +{ + BT_ASSERT(py_callable); + Py_DECREF(py_callable); +} + +static bt_graph_listener_status +port_added_listener( + const void *component, + swig_type_info *component_swig_type, + bt_component_class_type component_class_type, + const void *port, + swig_type_info *port_swig_type, + bt_port_type port_type, + void *py_callable) +{ + PyObject *py_component_ptr = NULL; + PyObject *py_port_ptr = NULL; + PyObject *py_res = NULL; + bt_graph_listener_status status; + + py_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(component), component_swig_type, 0); + if (!py_component_ptr) { + BT_LOGF_STR("Failed to create component SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_port_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(port), port_swig_type, 0); + if (!py_port_ptr) { + BT_LOGF_STR("Failed to create port SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_res = PyObject_CallFunction(py_callable, "(OiOi)", + py_component_ptr, component_class_type, py_port_ptr, port_type); + if (!py_res) { + bt2_py_loge_exception(); + PyErr_Clear(); + status = BT_GRAPH_LISTENER_STATUS_ERROR; + goto end; + } + + BT_ASSERT(py_res == Py_None); + status = BT_GRAPH_LISTENER_STATUS_OK; + +end: + Py_XDECREF(py_res); + Py_XDECREF(py_port_ptr); + Py_XDECREF(py_component_ptr); + + return status; +} + +static bt_graph_listener_status +source_component_output_port_added_listener(const bt_component_source *component_source, + const bt_port_output *port_output, void *py_callable) +{ + return port_added_listener( + component_source, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, + port_output, SWIGTYPE_p_bt_port_output, BT_PORT_TYPE_OUTPUT, py_callable); +} + +static bt_graph_listener_status +filter_component_input_port_added_listener(const bt_component_filter *component_filter, + const bt_port_input *port_input, void *py_callable) +{ + return port_added_listener( + component_filter, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + port_input, SWIGTYPE_p_bt_port_input, BT_PORT_TYPE_INPUT, py_callable); +} + +static bt_graph_listener_status +filter_component_output_port_added_listener(const bt_component_filter *component_filter, + const bt_port_output *port_output, void *py_callable) +{ + return port_added_listener( + component_filter, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + port_output, SWIGTYPE_p_bt_port_output, BT_PORT_TYPE_OUTPUT, py_callable); +} + +static bt_graph_listener_status +sink_component_input_port_added_listener(const bt_component_sink *component_sink, + const bt_port_input *port_input, void *py_callable) +{ + return port_added_listener( + component_sink, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, + port_input, SWIGTYPE_p_bt_port_input, BT_PORT_TYPE_INPUT, py_callable); +} + +static PyObject * +bt_py3_graph_add_port_added_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + PyObject *py_listener_ids = NULL; + PyObject *py_listener_id = NULL; + int listener_id; + bt_graph_status status; + + BT_ASSERT(graph); + BT_ASSERT(py_callable); + + /* + * Behind the scene, we will be registering 4 different listeners and + * return all of their ids. + */ + py_listener_ids = PyTuple_New(4); + if (!py_listener_ids) { + goto error; + } + + /* source output port */ + status = bt_graph_add_source_component_output_port_added_listener( + graph, source_component_output_port_added_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 0, py_listener_id); + py_listener_id = NULL; + + /* filter input port */ + status = bt_graph_add_filter_component_input_port_added_listener( + graph, filter_component_input_port_added_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 1, py_listener_id); + py_listener_id = NULL; + + /* filter output port */ + status = bt_graph_add_filter_component_output_port_added_listener( + graph, filter_component_output_port_added_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 2, py_listener_id); + py_listener_id = NULL; + + /* sink input port */ + status = bt_graph_add_sink_component_input_port_added_listener( + graph, sink_component_input_port_added_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + + PyTuple_SET_ITEM(py_listener_ids, 3, py_listener_id); + py_listener_id = NULL; + + Py_INCREF(py_callable); + Py_INCREF(py_callable); + Py_INCREF(py_callable); + Py_INCREF(py_callable); + + goto end; + +error: + Py_XDECREF(py_listener_ids); + py_listener_ids = Py_None; + Py_INCREF(py_listener_ids); + +end: + + Py_XDECREF(py_listener_id); + return py_listener_ids; +} + +static bt_graph_listener_status +ports_connected_listener( + const void *upstream_component, + swig_type_info *upstream_component_swig_type, + bt_component_class_type upstream_component_class_type, + const bt_port_output *upstream_port, + const void *downstream_component, + swig_type_info *downstream_component_swig_type, + bt_component_class_type downstream_component_class_type, + const bt_port_input *downstream_port, + void *py_callable) +{ + PyObject *py_upstream_component_ptr = NULL; + PyObject *py_upstream_port_ptr = NULL; + PyObject *py_downstream_component_ptr = NULL; + PyObject *py_downstream_port_ptr = NULL; + PyObject *py_res = NULL; + bt_graph_listener_status status; + + py_upstream_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(upstream_component), + upstream_component_swig_type, 0); + if (!py_upstream_component_ptr) { + BT_LOGF_STR("Failed to create upstream component SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_upstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(upstream_port), SWIGTYPE_p_bt_port_output, 0); + if (!py_upstream_port_ptr) { + BT_LOGF_STR("Failed to create upstream port SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_downstream_component_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(downstream_component), + downstream_component_swig_type, 0); + if (!py_downstream_component_ptr) { + BT_LOGF_STR("Failed to create downstream component SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_downstream_port_ptr = SWIG_NewPointerObj( + SWIG_as_voidptr(downstream_port), SWIGTYPE_p_bt_port_input, 0); + if (!py_downstream_port_ptr) { + BT_LOGF_STR("Failed to create downstream port SWIG pointer object."); + status = BT_GRAPH_LISTENER_STATUS_NOMEM; + goto end; + } + + py_res = PyObject_CallFunction(py_callable, "(OiOOiO)", + py_upstream_component_ptr, upstream_component_class_type, + py_upstream_port_ptr, + py_downstream_component_ptr, downstream_component_class_type, + py_downstream_port_ptr); + if (!py_res) { + bt2_py_loge_exception(); + PyErr_Clear(); + status = BT_GRAPH_LISTENER_STATUS_ERROR; + goto end; + } + + BT_ASSERT(py_res == Py_None); + status = BT_GRAPH_LISTENER_STATUS_OK; + +end: + Py_XDECREF(py_upstream_component_ptr); + Py_XDECREF(py_upstream_port_ptr); + Py_XDECREF(py_downstream_component_ptr); + Py_XDECREF(py_downstream_port_ptr); + Py_XDECREF(py_res); + + return status; +} + +static bt_graph_listener_status +source_filter_component_ports_connected_listener( + const bt_component_source *source_component, + const bt_component_filter *filter_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *py_callable) +{ + return ports_connected_listener( + source_component, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, + upstream_port, + filter_component, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + downstream_port, + py_callable); +} + +static bt_graph_listener_status +source_sink_component_ports_connected_listener( + const bt_component_source *source_component, + const bt_component_sink *sink_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *py_callable) +{ + return ports_connected_listener( + source_component, SWIGTYPE_p_bt_component_source, BT_COMPONENT_CLASS_TYPE_SOURCE, + upstream_port, + sink_component, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, + downstream_port, + py_callable); +} + +static bt_graph_listener_status +filter_filter_component_ports_connected_listener( + const bt_component_filter *filter_component_left, + const bt_component_filter *filter_component_right, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *py_callable) +{ + return ports_connected_listener( + filter_component_left, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + upstream_port, + filter_component_right, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + downstream_port, + py_callable); +} + +static bt_graph_listener_status +filter_sink_component_ports_connected_listener( + const bt_component_filter *filter_component, + const bt_component_sink *sink_component, + const bt_port_output *upstream_port, + const bt_port_input *downstream_port, void *py_callable) +{ + return ports_connected_listener( + filter_component, SWIGTYPE_p_bt_component_filter, BT_COMPONENT_CLASS_TYPE_FILTER, + upstream_port, + sink_component, SWIGTYPE_p_bt_component_sink, BT_COMPONENT_CLASS_TYPE_SINK, + downstream_port, + py_callable); +} + +static PyObject* +bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, + PyObject *py_callable) +{ + PyObject *py_listener_ids = NULL; + PyObject *py_listener_id = NULL; + int listener_id; + bt_graph_status status; + + BT_ASSERT(graph); + BT_ASSERT(py_callable); + + /* Behind the scene, we will be registering 4 different listeners and + * return all of their ids. */ + py_listener_ids = PyTuple_New(4); + if (!py_listener_ids) { + goto error; + } + + /* source -> filter connection */ + status = bt_graph_add_source_filter_component_ports_connected_listener( + graph, source_filter_component_ports_connected_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 0, py_listener_id); + py_listener_id = NULL; + + /* source -> sink connection */ + status = bt_graph_add_source_sink_component_ports_connected_listener( + graph, source_sink_component_ports_connected_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 1, py_listener_id); + py_listener_id = NULL; + + /* filter -> filter connection */ + status = bt_graph_add_filter_filter_component_ports_connected_listener( + graph, filter_filter_component_ports_connected_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 2, py_listener_id); + py_listener_id = NULL; + + /* filter -> sink connection */ + status = bt_graph_add_filter_sink_component_ports_connected_listener( + graph, filter_sink_component_ports_connected_listener, + graph_listener_removed, py_callable, &listener_id); + if (status != BT_GRAPH_STATUS_OK) { + goto error; + } + + py_listener_id = PyLong_FromLong(listener_id); + if (!py_listener_id) { + goto error; + } + + PyTuple_SET_ITEM(py_listener_ids, 3, py_listener_id); + py_listener_id = NULL; + + Py_INCREF(py_callable); + Py_INCREF(py_callable); + Py_INCREF(py_callable); + Py_INCREF(py_callable); + + goto end; + +error: + Py_XDECREF(py_listener_ids); + py_listener_ids = Py_None; + Py_INCREF(py_listener_ids); + +end: + + Py_XDECREF(py_listener_id); + return py_listener_ids; +} + +%} + +PyObject *bt_py3_graph_add_port_added_listener(struct bt_graph *graph, + PyObject *py_callable); +PyObject *bt_py3_graph_add_ports_connected_listener(struct bt_graph *graph, + PyObject *py_callable); diff --git a/src/bindings/python/bt2/bt2/native_bt_logging.i b/src/bindings/python/bt2/bt2/native_bt_logging.i new file mode 100644 index 00000000..df364bdf --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_logging.i @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +%{ +#include +%} + +/* Log levels */ +enum bt_logging_level { + BT_LOGGING_LEVEL_VERBOSE = 1, + BT_LOGGING_LEVEL_DEBUG = 2, + BT_LOGGING_LEVEL_INFO = 3, + BT_LOGGING_LEVEL_WARN = 4, + BT_LOGGING_LEVEL_ERROR = 5, + BT_LOGGING_LEVEL_FATAL = 6, + BT_LOGGING_LEVEL_NONE = 0xff, +}; + +/* Logging functions */ +enum bt_logging_level bt_logging_get_minimal_level(void); +enum bt_logging_level bt_logging_get_global_level(void); +void bt_logging_set_global_level(enum bt_logging_level log_level); diff --git a/src/bindings/python/bt2/bt2/native_bt_message.i b/src/bindings/python/bt2/bt2/native_bt_message.i new file mode 100644 index 00000000..0c81b938 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_message.i @@ -0,0 +1,337 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/* Output argument typemap for clock value output (always appends) */ +%typemap(in, numinputs=0) + (const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT) + (bt_clock_snapshot *temp_clock_snapshot = NULL) { + $1 = &temp_clock_snapshot; +} + +%typemap(argout) + (const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT) { + if (*$1) { + /* SWIG_Python_AppendOutput() steals the created object */ + $result = SWIG_Python_AppendOutput($result, + SWIG_NewPointerObj(SWIG_as_voidptr(*$1), + SWIGTYPE_p_bt_clock_snapshot, 0)); + } else { + /* SWIG_Python_AppendOutput() steals Py_None */ + Py_INCREF(Py_None); + $result = SWIG_Python_AppendOutput($result, Py_None); + } +} + +/* From message-const.h */ + +typedef enum bt_message_type { + BT_MESSAGE_TYPE_EVENT = 0, + BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY = 1, + BT_MESSAGE_TYPE_STREAM_BEGINNING = 2, + BT_MESSAGE_TYPE_STREAM_END = 3, + BT_MESSAGE_TYPE_PACKET_BEGINNING = 4, + BT_MESSAGE_TYPE_PACKET_END = 5, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING = 6, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_END = 7, + BT_MESSAGE_TYPE_DISCARDED_EVENTS = 8, + BT_MESSAGE_TYPE_DISCARDED_PACKETS = 9, +} bt_message_type; + +extern bt_message_type bt_message_get_type(const bt_message *message); + +extern void bt_message_get_ref(const bt_message *message); + +extern void bt_message_put_ref(const bt_message *message); + +/* From message-event-const.h */ + +extern const bt_event *bt_message_event_borrow_event_const( + const bt_message *message); + +extern const bt_clock_snapshot * +bt_message_event_borrow_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_class * +bt_message_event_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +/* From message-event.h */ + +extern +bt_message *bt_message_event_create( + bt_self_message_iterator *message_iterator, + const bt_event_class *event_class, + const bt_packet *packet); + +extern +bt_message *bt_message_event_create_with_default_clock_snapshot( + bt_self_message_iterator *message_iterator, + const bt_event_class *event_class, + const bt_packet *packet, uint64_t raw_clock_value); + +extern bt_event *bt_message_event_borrow_event( + bt_message *message); + +/* From message-message-iterator-inactivity-const.h */ + +extern const bt_clock_snapshot * +bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + const bt_message *msg); + +/* From message-message-iterator-inactivity.h */ + +extern +bt_message *bt_message_message_iterator_inactivity_create( + bt_self_message_iterator *message_iterator, + const bt_clock_class *default_clock_class, uint64_t raw_value); + +/* From message-stream-beginning-const.h */ + +extern const bt_stream *bt_message_stream_beginning_borrow_stream_const( + const bt_message *message); + +/* From message-stream-beginning.h */ + +extern +bt_message *bt_message_stream_beginning_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern bt_stream *bt_message_stream_beginning_borrow_stream( + bt_message *message); + +/* From message-stream-end-const.h */ + +extern const bt_stream *bt_message_stream_end_borrow_stream_const( + const bt_message *message); + +/* From message-stream-end.h */ + +extern +bt_message *bt_message_stream_end_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern bt_stream *bt_message_stream_end_borrow_stream( + bt_message *message); + +/* From message-packet-beginning-const.h */ + +extern const bt_packet *bt_message_packet_beginning_borrow_packet_const( + const bt_message *message); + +extern const bt_clock_snapshot * +bt_message_packet_beginning_borrow_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_class * +bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +/* From message-packet-beginning.h */ + +extern +bt_message *bt_message_packet_beginning_create( + bt_self_message_iterator *message_iterator, + const bt_packet *packet); + +extern +bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot( + bt_self_message_iterator *message_iterator, + const bt_packet *packet, uint64_t raw_value); + +extern bt_packet *bt_message_packet_beginning_borrow_packet( + bt_message *message); + +/* From message-packet-end-const.h */ + +extern const bt_packet *bt_message_packet_end_borrow_packet_const( + const bt_message *message); + +extern const bt_clock_snapshot * +bt_message_packet_end_borrow_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_class * +bt_message_packet_end_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +/* From message-packet-end.h */ + +extern +bt_message *bt_message_packet_end_create( + bt_self_message_iterator *message_iterator, + const bt_packet *packet); + +extern +bt_message *bt_message_packet_end_create_with_default_clock_snapshot( + bt_self_message_iterator *message_iterator, + const bt_packet *packet, uint64_t raw_value); + +extern bt_packet *bt_message_packet_end_borrow_packet( + bt_message *message); + +/* From message-stream-activity-const.h */ + +typedef enum bt_message_stream_activity_clock_snapshot_state { + BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN, + BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN, + BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE, +} bt_message_stream_activity_clock_snapshot_state; + +/* From message-stream-activity-beginning-const.h */ + +extern bt_message_stream_activity_clock_snapshot_state +bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + const bt_message *msg, const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT); + +extern const bt_clock_class * +bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +extern const bt_stream * +bt_message_stream_activity_beginning_borrow_stream_const( + const bt_message *message); + +/* From message-stream-activity-beginning.h */ + +extern bt_message *bt_message_stream_activity_beginning_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern bt_stream *bt_message_stream_activity_beginning_borrow_stream( + bt_message *message); + +extern void bt_message_stream_activity_beginning_set_default_clock_snapshot_state( + bt_message *msg, + bt_message_stream_activity_clock_snapshot_state state); + +extern void bt_message_stream_activity_beginning_set_default_clock_snapshot( + bt_message *msg, uint64_t raw_value); + +/* From message-stream-activity-end-const.h */ + +extern bt_message_stream_activity_clock_snapshot_state +bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + const bt_message *msg, const bt_clock_snapshot **BTOUTCLOCKSNAPSHOT); + +extern const bt_clock_class * +bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +extern const bt_stream * +bt_message_stream_activity_end_borrow_stream_const( + const bt_message *message); + +/* From message-stream-activity-end.h */ + +extern bt_message *bt_message_stream_activity_end_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern void bt_message_stream_activity_end_set_default_clock_snapshot_state( + bt_message *msg, + bt_message_stream_activity_clock_snapshot_state state); + +extern void bt_message_stream_activity_end_set_default_clock_snapshot( + bt_message *msg, uint64_t raw_value); + +extern bt_stream *bt_message_stream_activity_end_borrow_stream( + bt_message *message); + +/* From message-discarded-events-const.h */ + +extern const bt_clock_snapshot * +bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_snapshot * +bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_class * +bt_message_discarded_events_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +extern const bt_stream * +bt_message_discarded_events_borrow_stream_const(const bt_message *message); + +extern bt_property_availability bt_message_discarded_events_get_count( + const bt_message *message, uint64_t *OUT); + +/* From message-discarded-events.h */ + +extern bt_message *bt_message_discarded_events_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern bt_message *bt_message_discarded_events_create_with_default_clock_snapshots( + bt_self_message_iterator *message_iterator, + const bt_stream *stream, uint64_t beginning_raw_value, + uint64_t end_raw_value); + +extern bt_stream *bt_message_discarded_events_borrow_stream( + bt_message *message); + +extern void bt_message_discarded_events_set_count(bt_message *message, + uint64_t count); + +/* From message-discarded-packets-const.h */ + +extern const bt_clock_snapshot * +bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_snapshot * +bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + const bt_message *msg); + +extern const bt_clock_class * +bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( + const bt_message *msg); + +extern const bt_stream * +bt_message_discarded_packets_borrow_stream_const(const bt_message *message); + +extern bt_property_availability bt_message_discarded_packets_get_count( + const bt_message *message, uint64_t *OUT); + +/* From message-discarded-packets.h */ + +extern bt_message *bt_message_discarded_packets_create( + bt_self_message_iterator *message_iterator, + const bt_stream *stream); + +extern bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots( + bt_self_message_iterator *message_iterator, + const bt_stream *stream, uint64_t beginning_raw_value, + uint64_t end_raw_value); + +extern bt_stream *bt_message_discarded_packets_borrow_stream( + bt_message *message); + +extern void bt_message_discarded_packets_set_count(bt_message *message, + uint64_t count); diff --git a/src/bindings/python/bt2/bt2/native_bt_notifier.i b/src/bindings/python/bt2/bt2/native_bt_notifier.i new file mode 100644 index 00000000..b5f53900 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_notifier.i @@ -0,0 +1,232 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From message-iterator-const.h */ + +typedef enum bt_message_iterator_status { + BT_MESSAGE_ITERATOR_STATUS_OK = 0, + BT_MESSAGE_ITERATOR_STATUS_END = 1, + BT_MESSAGE_ITERATOR_STATUS_AGAIN = 11, + BT_MESSAGE_ITERATOR_STATUS_ERROR = -1, + BT_MESSAGE_ITERATOR_STATUS_NOMEM = -12, +} bt_message_iterator_status; + +/* From self-message-iterator.h */ + +typedef enum bt_self_message_iterator_status { + BT_SELF_MESSAGE_ITERATOR_STATUS_OK = BT_MESSAGE_ITERATOR_STATUS_OK, + BT_SELF_MESSAGE_ITERATOR_STATUS_END = BT_MESSAGE_ITERATOR_STATUS_END, + BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN = BT_MESSAGE_ITERATOR_STATUS_AGAIN, + BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR = BT_MESSAGE_ITERATOR_STATUS_ERROR, + BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM = BT_MESSAGE_ITERATOR_STATUS_NOMEM, +} bt_self_message_iterator_status; + +extern bt_self_component * +bt_self_message_iterator_borrow_component( + bt_self_message_iterator *message_iterator); + +extern bt_self_port_output * +bt_self_message_iterator_borrow_port( + bt_self_message_iterator *message_iterator); + +extern void bt_self_message_iterator_set_data( + bt_self_message_iterator *message_iterator, + void *user_data); + +extern void *bt_self_message_iterator_get_data( + const bt_self_message_iterator *message_iterator); + +/* From self-component-port-input-message-iterator.h */ + +bt_message_iterator * +bt_self_component_port_input_message_iterator_as_message_iterator( + bt_self_component_port_input_message_iterator *iterator); + +extern bt_self_component_port_input_message_iterator * +bt_self_component_port_input_message_iterator_create( + bt_self_component_port_input *input_port); + +extern bt_component * +bt_self_component_port_input_message_iterator_borrow_component( + bt_self_component_port_input_message_iterator *iterator); + +extern bt_message_iterator_status +bt_self_component_port_input_message_iterator_next( + bt_self_component_port_input_message_iterator *iterator, + bt_message_array_const *msgs, uint64_t *count); + +extern bt_bool +bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( + bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin); + +extern bt_bool bt_self_component_port_input_message_iterator_can_seek_beginning( + bt_self_component_port_input_message_iterator *iterator); + +extern bt_message_iterator_status +bt_self_component_port_input_message_iterator_seek_ns_from_origin( + bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin); + +extern bt_message_iterator_status +bt_self_component_port_input_message_iterator_seek_beginning( + bt_self_component_port_input_message_iterator *iterator); + +extern void bt_self_component_port_input_message_iterator_get_ref( + const bt_self_component_port_input_message_iterator *self_component_port_input_message_iterator); + +extern void bt_self_component_port_input_message_iterator_put_ref( + const bt_self_component_port_input_message_iterator *self_component_port_input_message_iterator); + +/* From port-output-message-iterator.h */ + +bt_message_iterator * +bt_port_output_message_iterator_as_message_iterator( + bt_port_output_message_iterator *iterator); + +extern bt_port_output_message_iterator * +bt_port_output_message_iterator_create( + bt_graph *graph, + const bt_port_output *output_port); + +extern bt_message_iterator_status +bt_port_output_message_iterator_next( + bt_port_output_message_iterator *iterator, + bt_message_array_const *msgs, uint64_t *count); + +extern bt_bool bt_port_output_message_iterator_can_seek_ns_from_origin( + bt_port_output_message_iterator *iterator, + int64_t ns_from_origin); + +extern bt_bool bt_port_output_message_iterator_can_seek_beginning( + bt_port_output_message_iterator *iterator); + +extern bt_message_iterator_status +bt_port_output_message_iterator_seek_ns_from_origin( + bt_port_output_message_iterator *iterator, + int64_t ns_from_origin); + +extern bt_message_iterator_status +bt_port_output_message_iterator_seek_beginning( + bt_port_output_message_iterator *iterator); + +extern void bt_port_output_message_iterator_get_ref( + const bt_port_output_message_iterator *port_output_message_iterator); + +extern void bt_port_output_message_iterator_put_ref( + const bt_port_output_message_iterator *port_output_message_iterator); + +/* Helper functions for Python */ +%{ +static PyObject *bt_py3_get_user_component_from_user_msg_iter( + bt_self_message_iterator *self_message_iterator) +{ + bt_self_component *self_component = bt_self_message_iterator_borrow_component(self_message_iterator); + PyObject *py_comp; + + BT_ASSERT(self_component); + py_comp = bt_self_component_get_data(self_component); + BT_ASSERT(py_comp); + + /* Return new reference */ + Py_INCREF(py_comp); + return py_comp; +} + +static inline +PyObject *create_pylist_from_messages(bt_message_array_const messages, + uint64_t message_count) +{ + uint64_t i; + PyObject *py_msg_list = PyList_New(message_count); + BT_ASSERT(py_msg_list); + for (i = 0; i < message_count; i++) { + PyList_SET_ITEM(py_msg_list, i, + SWIG_NewPointerObj(SWIG_as_voidptr(messages[i]), + SWIGTYPE_p_bt_message, 0)); + } + + return py_msg_list; +} + +static +PyObject *bt_py3_get_msg_range_common(bt_message_iterator_status status, + bt_message_array_const messages, uint64_t message_count) +{ + PyObject *py_status; + PyObject *py_return_tuple; + PyObject *py_msg_list = Py_None; + + py_status = SWIG_From_long_SS_long(status); + if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + py_msg_list = create_pylist_from_messages(messages, message_count); + +end: + py_return_tuple = PyTuple_New(2); + BT_ASSERT(py_return_tuple); + PyTuple_SET_ITEM(py_return_tuple, 0, py_status); + PyTuple_SET_ITEM(py_return_tuple, 1, py_msg_list); + + return py_return_tuple; +} + +static PyObject +*bt_py3_self_component_port_input_get_msg_range( + bt_self_component_port_input_message_iterator *iter) +{ + bt_message_array_const messages; + uint64_t message_count = 0; + bt_message_iterator_status status; + + status = bt_self_component_port_input_message_iterator_next(iter, &messages, + &message_count); + + return bt_py3_get_msg_range_common(status, messages, message_count); +} + +static PyObject +*bt_py3_port_output_get_msg_range( + bt_port_output_message_iterator *iter) +{ + bt_message_array_const messages; + uint64_t message_count = 0; + bt_message_iterator_status status; + + status = + bt_port_output_message_iterator_next(iter, &messages, + &message_count); + + return bt_py3_get_msg_range_common(status, messages, message_count); +} +%} + +PyObject *bt_py3_get_user_component_from_user_msg_iter( + bt_self_message_iterator *self_message_iterator); +PyObject *bt_py3_self_component_port_input_get_msg_range( + bt_self_component_port_input_message_iterator *iter); +PyObject *bt_py3_port_output_get_msg_range( + bt_port_output_message_iterator *iter); diff --git a/src/bindings/python/bt2/bt2/native_bt_packet.i b/src/bindings/python/bt2/bt2/native_bt_packet.i new file mode 100644 index 00000000..b4c90735 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_packet.i @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From packet-const.h */ + +typedef enum bt_packet_status { + BT_PACKET_STATUS_OK = 0, + BT_PACKET_STATUS_NOMEM = -12, +} bt_packet_status; + +extern const bt_stream *bt_packet_borrow_stream_const( + const bt_packet *packet); + +extern +const bt_field *bt_packet_borrow_context_field_const( + const bt_packet *packet); + +extern void bt_packet_get_ref(const bt_packet *packet); + +extern void bt_packet_put_ref(const bt_packet *packet); + +/* From packet.h */ + +extern bt_packet *bt_packet_create(const bt_stream *stream); + +extern bt_stream *bt_packet_borrow_stream(bt_packet *packet); + +extern +bt_field *bt_packet_borrow_context_field(bt_packet *packet); + +extern +bt_packet_status bt_packet_move_context_field(bt_packet *packet, + bt_packet_context_field *context); diff --git a/src/bindings/python/bt2/bt2/native_bt_plugin.i b/src/bindings/python/bt2/bt2/native_bt_plugin.i new file mode 100644 index 00000000..435d40f2 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_plugin.i @@ -0,0 +1,131 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From plugin-const.h */ + +extern const bt_plugin *bt_plugin_find(const char *plugin_name); + +extern const bt_plugin_set *bt_plugin_find_all_from_file( + const char *path); + +extern const bt_plugin_set *bt_plugin_find_all_from_dir( + const char *path, bt_bool recurse); + +extern const bt_plugin_set *bt_plugin_find_all_from_static(void); + +extern const char *bt_plugin_get_name(const bt_plugin *plugin); + +extern const char *bt_plugin_get_author(const bt_plugin *plugin); + +extern const char *bt_plugin_get_license(const bt_plugin *plugin); + +extern const char *bt_plugin_get_description(const bt_plugin *plugin); + +extern const char *bt_plugin_get_path(const bt_plugin *plugin); + +extern bt_property_availability bt_plugin_get_version( + const bt_plugin *plugin, unsigned int *OUT, + unsigned int *OUT, unsigned int *OUT, const char **OUT); + +extern uint64_t bt_plugin_get_source_component_class_count( + const bt_plugin *plugin); + +extern uint64_t bt_plugin_get_filter_component_class_count( + const bt_plugin *plugin); + +extern uint64_t bt_plugin_get_sink_component_class_count( + const bt_plugin *plugin); + +extern const bt_component_class_source * +bt_plugin_borrow_source_component_class_by_index_const( + const bt_plugin *plugin, uint64_t index); + +extern const bt_component_class_filter * +bt_plugin_borrow_filter_component_class_by_index_const( + const bt_plugin *plugin, uint64_t index); + +extern const bt_component_class_sink * +bt_plugin_borrow_sink_component_class_by_index_const( + const bt_plugin *plugin, uint64_t index); + +extern const bt_component_class_source * +bt_plugin_borrow_source_component_class_by_name_const( + const bt_plugin *plugin, const char *name); + +extern const bt_component_class_filter * +bt_plugin_borrow_filter_component_class_by_name_const( + const bt_plugin *plugin, const char *name); + +extern const bt_component_class_sink * +bt_plugin_borrow_sink_component_class_by_name_const( + const bt_plugin *plugin, const char *name); + +extern void bt_plugin_get_ref(const bt_plugin *plugin); + +extern void bt_plugin_put_ref(const bt_plugin *plugin); + +/* From plugin-set-const.h */ + +extern uint64_t bt_plugin_set_get_plugin_count( + const bt_plugin_set *plugin_set); + +extern const bt_plugin *bt_plugin_set_borrow_plugin_by_index_const( + const bt_plugin_set *plugin_set, uint64_t index); + +extern void bt_plugin_set_get_ref(const bt_plugin_set *plugin_set); + +extern void bt_plugin_set_put_ref(const bt_plugin_set *plugin_set); + +/* Helpers */ + +bt_property_availability bt_plugin_get_version_wrapper( + const bt_plugin *plugin, unsigned int *OUT, + unsigned int *OUT, unsigned int *OUT, const char **OUT); + +%{ + +/* + * This wrapper ensures that when the API function fails, the `*extra` output + * parameter is set to NULL. This is necessary because the epilogue of the + * "char **OUT" typemap will use that value to make a Python str object. We + * can't rely on the initial value of `*extra`, it could point to unreadable + * memory. + */ + +bt_property_availability bt_plugin_get_version_wrapper( + const bt_plugin *plugin, unsigned int *major, + unsigned int *minor, unsigned int *patch, const char **extra) +{ + bt_property_availability ret; + + ret = bt_plugin_get_version(plugin, major, minor, patch, extra); + + if (ret == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { + *extra = NULL; + } + + return ret; +} + +%} diff --git a/src/bindings/python/bt2/bt2/native_bt_port.i b/src/bindings/python/bt2/bt2/native_bt_port.i new file mode 100644 index 00000000..873c6136 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_port.i @@ -0,0 +1,116 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Typemap for the user data attached to (and owned by) a self component port. + * The pointer saved as the port's user data is directly the PyObject *. + * + * As per the CPython calling convention, we need to return a new reference to + * the returned object, which will be transferred to the caller. The following + * typedef allows us to apply the typemap. + */ +%{ +typedef void *PY_SELF_PORT_USER_DATA; +%} + +%typemap(out) PY_SELF_PORT_USER_DATA { + Py_INCREF($1); + $result = $1; +} + +/* From port-const.h */ + +typedef enum bt_port_type { + BT_PORT_TYPE_INPUT = 0, + BT_PORT_TYPE_OUTPUT = 1, +} bt_port_type; + +extern const char *bt_port_get_name(const bt_port *port); + +extern bt_port_type bt_port_get_type(const bt_port *port); + +extern const bt_connection *bt_port_borrow_connection_const( + const bt_port *port); + +extern const bt_component *bt_port_borrow_component_const( + const bt_port *port); + +extern bt_bool bt_port_is_connected(const bt_port *port); + +bt_bool bt_port_is_input(const bt_port *port); + +bt_bool bt_port_is_output(const bt_port *port); + +extern void bt_port_get_ref(const bt_port *port); + +extern void bt_port_put_ref(const bt_port *port); + +/* From port-output-const.h */ + +const bt_port *bt_port_output_as_port_const(const bt_port_output *port_output); + +extern void bt_port_output_get_ref(const bt_port_output *port_output); + +extern void bt_port_output_put_ref(const bt_port_output *port_output); + +/* From port-input-const.h */ + +const bt_port *bt_port_input_as_port_const(const bt_port_input *port_input); + +extern void bt_port_input_get_ref(const bt_port_input *port_input); + +extern void bt_port_input_put_ref(const bt_port_input *port_input); + +/* From self-component-port.h */ + +typedef enum bt_self_component_port_status { + BT_SELF_PORT_STATUS_OK = 0, +} bt_self_component_port_status; + +const bt_port *bt_self_component_port_as_port( + bt_self_component_port *self_port); + +extern bt_self_component *bt_self_component_port_borrow_component( + bt_self_component_port *self_port); + +extern PY_SELF_PORT_USER_DATA bt_self_component_port_get_data( + const bt_self_component_port *self_port); + +/* From self-component-port-output.h */ + +bt_self_component_port * +bt_self_component_port_output_as_self_component_port( + bt_self_component_port_output *self_component_port); + +const bt_port_output *bt_self_component_port_output_as_port_output( + bt_self_component_port_output *self_component_port); + +/* From self-component-port-input.h */ + +bt_self_component_port * +bt_self_component_port_input_as_self_component_port( + bt_self_component_port_input *self_component_port); + +const bt_port_input *bt_self_component_port_input_as_port_input( + const bt_self_component_port_input *self_component_port); diff --git a/src/bindings/python/bt2/bt2/native_bt_query_exec.i b/src/bindings/python/bt2/bt2/native_bt_query_exec.i new file mode 100644 index 00000000..af78efaf --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_query_exec.i @@ -0,0 +1,62 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From query-executor-const.h */ + +typedef enum bt_query_executor_status { + BT_QUERY_EXECUTOR_STATUS_OK = 0, + BT_QUERY_EXECUTOR_STATUS_AGAIN = 11, + BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED = 95, + BT_QUERY_EXECUTOR_STATUS_CANCELED = 125, + BT_QUERY_EXECUTOR_STATUS_ERROR = -1, + BT_QUERY_EXECUTOR_STATUS_NOMEM = -12, + BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT = -23, + BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS = -24, +} bt_query_executor_status; + +extern +bt_bool bt_query_executor_is_canceled( + const bt_query_executor *query_executor); + +extern void bt_query_executor_get_ref( + const bt_query_executor *query_executor); + +extern void bt_query_executor_put_ref( + const bt_query_executor *query_executor); + +/* From query-executor.h */ + +extern +bt_query_executor *bt_query_executor_create(void); + +extern +bt_query_executor_status bt_query_executor_query( + bt_query_executor *query_executor, + const bt_component_class *component_class, + const char *object, const bt_value *params, + const bt_value **OUT); + +extern +bt_query_executor_status bt_query_executor_cancel( + bt_query_executor *query_executor); diff --git a/src/bindings/python/bt2/bt2/native_bt_stream.i b/src/bindings/python/bt2/bt2/native_bt_stream.i new file mode 100644 index 00000000..394d9872 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_stream.i @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From stream-const.h */ + +typedef enum bt_stream_status { + BT_STREAM_STATUS_OK = 0, + BT_STREAM_STATUS_NOMEM = -12, +} bt_stream_status; + +extern const bt_stream_class *bt_stream_borrow_class_const( + const bt_stream *stream); + +extern const bt_trace *bt_stream_borrow_trace_const( + const bt_stream *stream); + +extern const char *bt_stream_get_name(const bt_stream *stream); + +extern uint64_t bt_stream_get_id(const bt_stream *stream); + +extern void bt_stream_get_ref(const bt_stream *stream); + +extern void bt_stream_put_ref(const bt_stream *stream); + +/* From stream.h */ + +extern bt_stream *bt_stream_create(bt_stream_class *stream_class, + bt_trace *trace); + +extern bt_stream *bt_stream_create_with_id( + bt_stream_class *stream_class, + bt_trace *trace, uint64_t id); + +extern bt_trace *bt_stream_borrow_trace(bt_stream *stream); + +extern bt_stream_class *bt_stream_borrow_class(bt_stream *stream); + +extern bt_stream_status bt_stream_set_name(bt_stream *stream, + const char *name); diff --git a/src/bindings/python/bt2/bt2/native_bt_stream_class.i b/src/bindings/python/bt2/bt2/native_bt_stream_class.i new file mode 100644 index 00000000..e5927043 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_stream_class.i @@ -0,0 +1,159 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From stream-class-const.h */ + +typedef enum bt_stream_class_status { + BT_STREAM_CLASS_STATUS_OK = 0, + BT_STREAM_CLASS_STATUS_NOMEM = -12, +} bt_stream_class_status; + +extern const bt_trace_class *bt_stream_class_borrow_trace_class_const( + const bt_stream_class *stream_class); + +extern const char *bt_stream_class_get_name( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_assigns_automatic_event_class_id( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_assigns_automatic_stream_id( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_packets_have_end_default_clock_snapshot( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_supports_discarded_events( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_supports_discarded_packets( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots( + const bt_stream_class *stream_class); + +extern bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots( + const bt_stream_class *stream_class); + +extern uint64_t bt_stream_class_get_id( + const bt_stream_class *stream_class); + +extern const bt_field_class * +bt_stream_class_borrow_packet_context_field_class_const( + const bt_stream_class *stream_class); + +extern const bt_field_class * +bt_stream_class_borrow_event_common_context_field_class_const( + const bt_stream_class *stream_class); + +extern uint64_t bt_stream_class_get_event_class_count( + const bt_stream_class *stream_class); + +extern const bt_event_class * +bt_stream_class_borrow_event_class_by_index_const( + const bt_stream_class *stream_class, uint64_t index); + +extern const bt_event_class * +bt_stream_class_borrow_event_class_by_id_const( + const bt_stream_class *stream_class, uint64_t id); + +extern const bt_clock_class * +bt_stream_class_borrow_default_clock_class_const( + const bt_stream_class *stream_class); + +extern void bt_stream_class_get_ref(const bt_stream_class *stream_class); + +extern void bt_stream_class_put_ref(const bt_stream_class *stream_class); + +/* From stream-class-const.h */ + +extern bt_stream_class *bt_stream_class_create( + bt_trace_class *trace_class); + +extern bt_stream_class *bt_stream_class_create_with_id( + bt_trace_class *trace_class, uint64_t id); + +extern bt_trace_class *bt_stream_class_borrow_trace_class( + bt_stream_class *stream_class); + +extern bt_stream_class_status bt_stream_class_set_name( + bt_stream_class *stream_class, const char *name); + +extern void bt_stream_class_set_assigns_automatic_event_class_id( + bt_stream_class *stream_class, bt_bool value); + +extern void bt_stream_class_set_assigns_automatic_stream_id( + bt_stream_class *stream_class, bt_bool value); + +extern void bt_stream_class_set_packets_have_beginning_default_clock_snapshot( + bt_stream_class *stream_class, bt_bool value); + +extern void bt_stream_class_set_packets_have_end_default_clock_snapshot( + bt_stream_class *stream_class, bt_bool value); + +extern void bt_stream_class_set_supports_discarded_events( + bt_stream_class *stream_class, + bt_bool supports_discarded_events, + bt_bool with_default_clock_snapshots); + +extern void bt_stream_class_set_supports_discarded_packets( + bt_stream_class *stream_class, + bt_bool supports_discarded_packets, + bt_bool with_default_clock_snapshots); + +extern bt_stream_class_status +bt_stream_class_set_packet_context_field_class( + bt_stream_class *stream_class, + bt_field_class *field_class); + +extern bt_field_class * +bt_stream_class_borrow_packet_context_field_class( + bt_stream_class *stream_class); + +extern bt_stream_class_status +bt_stream_class_set_event_common_context_field_class( + bt_stream_class *stream_class, + bt_field_class *field_class); + +extern bt_field_class * +bt_stream_class_borrow_event_common_context_field_class( + bt_stream_class *stream_class); + +extern bt_event_class * +bt_stream_class_borrow_event_class_by_index( + bt_stream_class *stream_class, uint64_t index); + +extern bt_event_class * +bt_stream_class_borrow_event_class_by_id( + bt_stream_class *stream_class, uint64_t id); + +extern bt_clock_class *bt_stream_class_borrow_default_clock_class( + bt_stream_class *stream_class); + +extern bt_stream_class_status bt_stream_class_set_default_clock_class( + bt_stream_class *stream_class, + bt_clock_class *clock_class); diff --git a/src/bindings/python/bt2/bt2/native_bt_trace.i b/src/bindings/python/bt2/bt2/native_bt_trace.i new file mode 100644 index 00000000..2d8f538b --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_trace.i @@ -0,0 +1,122 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From trace-const.h */ + +typedef enum bt_trace_status { + BT_TRACE_STATUS_OK = 0, + BT_TRACE_STATUS_NOMEM = -12, +} bt_trace_status; + +typedef void (* bt_trace_destruction_listener_func)( + const bt_trace *trace, void *data); + +extern const bt_trace_class *bt_trace_borrow_class_const( + const bt_trace *trace); + +extern const char *bt_trace_get_name(const bt_trace *trace); + +extern uint64_t bt_trace_get_stream_count(const bt_trace *trace); + +extern const bt_stream *bt_trace_borrow_stream_by_index_const( + const bt_trace *trace, uint64_t index); + +extern const bt_stream *bt_trace_borrow_stream_by_id_const( + const bt_trace *trace, uint64_t id); + +extern bt_trace_status bt_trace_add_destruction_listener( + const bt_trace *trace, + bt_trace_destruction_listener_func listener, + void *data, uint64_t *listener_id); + +extern bt_trace_status bt_trace_remove_destruction_listener( + const bt_trace *trace, uint64_t listener_id); + +extern void bt_trace_get_ref(const bt_trace *trace); + +extern void bt_trace_put_ref(const bt_trace *trace); + +/* From trace.h */ + +extern bt_trace_class *bt_trace_borrow_class(bt_trace *trace); + +extern bt_trace *bt_trace_create(bt_trace_class *trace_class); + +extern bt_trace_status bt_trace_set_name(bt_trace *trace, + const char *name); + +extern bt_stream *bt_trace_borrow_stream_by_index(bt_trace *trace, + uint64_t index); + +extern bt_stream *bt_trace_borrow_stream_by_id(bt_trace *trace, + uint64_t id); + +%{ +static void +trace_destroyed_listener(const bt_trace *trace, void *py_callable) +{ + PyObject *py_trace_ptr = NULL; + PyObject *py_res = NULL; + + py_trace_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(trace), + SWIGTYPE_p_bt_trace, 0); + if (!py_trace_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(O)", py_trace_ptr); + if (py_res != NULL) { + BT_ASSERT(py_res == Py_None); + } else { + bt2_py_loge_exception(); + } + + Py_DECREF(py_trace_ptr); + Py_XDECREF(py_res); +} + +uint64_t bt_py3_trace_add_destruction_listener(bt_trace *trace, PyObject *py_callable) +{ + uint64_t id = UINT64_C(-1); + bt_trace_status status; + + BT_ASSERT(trace); + BT_ASSERT(py_callable); + + status = bt_trace_add_destruction_listener( + trace, trace_destroyed_listener, py_callable, &id); + if (status != BT_TRACE_STATUS_OK) { + BT_LOGF_STR("Failed to add trace destruction listener."); + abort(); + } + + Py_INCREF(py_callable); + + return id; +} +%} + +uint64_t bt_py3_trace_add_destruction_listener(bt_trace *trace, + PyObject *py_callable); diff --git a/src/bindings/python/bt2/bt2/native_bt_trace_class.i b/src/bindings/python/bt2/bt2/native_bt_trace_class.i new file mode 100644 index 00000000..71061ffa --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_trace_class.i @@ -0,0 +1,153 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From trace-class-const.h */ + +typedef enum bt_trace_class_status { + BT_TRACE_CLASS_STATUS_OK = 0, + BT_TRACE_CLASS_STATUS_NOMEM = -12, +} bt_trace_class_status; + +typedef void (* bt_trace_class_destruction_listener_func)( + const bt_trace_class *trace_class, void *data); + +extern bt_bool bt_trace_class_assigns_automatic_stream_class_id( + const bt_trace_class *trace_class); + +extern const char *bt_trace_class_get_name( + const bt_trace_class *trace_class); + +extern bt_uuid bt_trace_class_get_uuid( + const bt_trace_class *trace_class); + +extern uint64_t bt_trace_class_get_environment_entry_count( + const bt_trace_class *trace_class); + +extern void bt_trace_class_borrow_environment_entry_by_index_const( + const bt_trace_class *trace_class, uint64_t index, + const char **OUT, const bt_value **OUT); + +extern const bt_value * +bt_trace_class_borrow_environment_entry_value_by_name_const( + const bt_trace_class *trace_class, const char *name); + +extern uint64_t bt_trace_class_get_stream_class_count( + const bt_trace_class *trace_class); + +extern const bt_stream_class * +bt_trace_class_borrow_stream_class_by_index_const( + const bt_trace_class *trace_class, uint64_t index); + +extern const bt_stream_class *bt_trace_class_borrow_stream_class_by_id_const( + const bt_trace_class *trace_class, uint64_t id); + +extern bt_trace_class_status bt_trace_class_add_destruction_listener( + const bt_trace_class *trace_class, + bt_trace_class_destruction_listener_func listener, + void *data, uint64_t *listener_id); + +extern bt_trace_class_status bt_trace_class_remove_destruction_listener( + const bt_trace_class *trace_class, uint64_t listener_id); + +extern void bt_trace_class_get_ref(const bt_trace_class *trace_class); + +extern void bt_trace_class_put_ref(const bt_trace_class *trace_class); + +/* From trace-class.h */ + +extern bt_trace_class *bt_trace_class_create(bt_self_component *self_comp); + +extern void bt_trace_class_set_assigns_automatic_stream_class_id( + bt_trace_class *trace_class, bt_bool value); + +extern bt_trace_class_status bt_trace_class_set_name( + bt_trace_class *trace_class, const char *name); + +extern void bt_trace_class_set_uuid(bt_trace_class *trace_class, + bt_uuid uuid); + +extern bt_trace_class_status bt_trace_class_set_environment_entry_integer( + bt_trace_class *trace_class, + const char *name, int64_t value); + +extern bt_trace_class_status bt_trace_class_set_environment_entry_string( + bt_trace_class *trace_class, + const char *name, const char *value); + +extern bt_stream_class *bt_trace_class_borrow_stream_class_by_index( + bt_trace_class *trace_class, uint64_t index); + +extern bt_stream_class *bt_trace_class_borrow_stream_class_by_id( + bt_trace_class *trace_class, uint64_t id); + +/* Helper functions for Python */ +%{ +static void +trace_class_destroyed_listener(const bt_trace_class *trace_class, void *py_callable) +{ + PyObject *py_trace_class_ptr = NULL; + PyObject *py_res = NULL; + + py_trace_class_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(trace_class), + SWIGTYPE_p_bt_trace_class, 0); + if (!py_trace_class_ptr) { + BT_LOGF_STR("Failed to create a SWIG pointer object."); + abort(); + } + + py_res = PyObject_CallFunction(py_callable, "(O)", py_trace_class_ptr); + if (py_res != NULL) { + BT_ASSERT(py_res == Py_None); + } else { + bt2_py_loge_exception(); + } + + Py_DECREF(py_trace_class_ptr); + Py_XDECREF(py_res); +} + +uint64_t bt_py3_trace_class_add_destruction_listener(bt_trace_class *trace_class, + PyObject *py_callable) +{ + uint64_t id = UINT64_C(-1); + bt_trace_class_status status; + + BT_ASSERT(trace_class); + BT_ASSERT(py_callable); + + status = bt_trace_class_add_destruction_listener( + trace_class, trace_class_destroyed_listener, py_callable, &id); + if (status != BT_TRACE_CLASS_STATUS_OK) { + BT_LOGF_STR("Failed to add trace class destruction listener."); + abort(); + } + + Py_INCREF(py_callable); + + return id; +} +%} + +uint64_t bt_py3_trace_class_add_destruction_listener(bt_trace_class *trace_class, + PyObject *py_callable); diff --git a/src/bindings/python/bt2/bt2/native_bt_value.i b/src/bindings/python/bt2/bt2/native_bt_value.i new file mode 100644 index 00000000..1ecd21e3 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_value.i @@ -0,0 +1,264 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* From value-const.h */ + +typedef enum bt_value_status { + /// Operation canceled. + BT_VALUE_STATUS_CANCELED = 125, + + /// Cannot allocate memory. + BT_VALUE_STATUS_NOMEM = -12, + + /// Okay, no error. + BT_VALUE_STATUS_OK = 0, +} bt_value_status; + +typedef enum bt_value_type { + /// Null value object. + BT_VALUE_TYPE_NULL = 0, + + /// Boolean value object (holds #BT_TRUE or #BT_FALSE). + BT_VALUE_TYPE_BOOL = 1, + + /// Unsigned integer value object (holds an unsigned 64-bit integer raw value). + BT_VALUE_TYPE_UNSIGNED_INTEGER = 2, + + /// Signed integer value object (holds a signed 64-bit integer raw value). + BT_VALUE_TYPE_SIGNED_INTEGER = 3, + + /// Floating point number value object (holds a \c double raw value). + BT_VALUE_TYPE_REAL = 4, + + /// String value object. + BT_VALUE_TYPE_STRING = 5, + + /// Array value object. + BT_VALUE_TYPE_ARRAY = 6, + + /// Map value object. + BT_VALUE_TYPE_MAP = 7, +} bt_value_type; + +extern bt_value_type bt_value_get_type(const bt_value *object); + +extern bt_value_status bt_value_copy(const bt_value *object, + bt_value **copy); + +extern bt_bool bt_value_compare(const bt_value *object_a, + const bt_value *object_b); + +extern bt_bool bt_value_bool_get(const bt_value *bool_obj); + +extern uint64_t bt_value_unsigned_integer_get(const bt_value *integer_obj); + +extern int64_t bt_value_signed_integer_get(const bt_value *integer_obj); + +extern double bt_value_real_get(const bt_value *real_obj); + +extern const char *bt_value_string_get(const bt_value *string_obj); + +extern uint64_t bt_value_array_get_size(const bt_value *array_obj); + +extern const bt_value *bt_value_array_borrow_element_by_index_const( + const bt_value *array_obj, uint64_t index); + +extern uint64_t bt_value_map_get_size(const bt_value *map_obj); + +extern const bt_value *bt_value_map_borrow_entry_value_const( + const bt_value *map_obj, const char *key); + +typedef bt_bool (* bt_value_map_foreach_entry_const_func)(const char *key, + const bt_value *object, void *data); + +extern bt_value_status bt_value_map_foreach_entry_const( + const bt_value *map_obj, + bt_value_map_foreach_entry_const_func func, void *data); + +extern bt_bool bt_value_map_has_entry(const bt_value *map_obj, + const char *key); + +extern bt_value_status bt_value_map_extend( + const bt_value *base_map_obj, + const bt_value *extension_map_obj, + bt_value **extended_map_obj); + +extern void bt_value_get_ref(const bt_value *value); + +extern void bt_value_put_ref(const bt_value *value); + +/* From value.h */ + +extern bt_value *const bt_value_null; + +extern bt_value *bt_value_bool_create(void); + +extern bt_value *bt_value_bool_create_init(bt_bool val); + +extern void bt_value_bool_set(bt_value *bool_obj, bt_bool val); + +extern bt_value *bt_value_unsigned_integer_create(void); + +extern bt_value *bt_value_unsigned_integer_create_init(uint64_t val); + +extern void bt_value_unsigned_integer_set(bt_value *integer_obj, uint64_t val); + +extern bt_value *bt_value_signed_integer_create(void); + +extern bt_value *bt_value_signed_integer_create_init(int64_t val); + +extern void bt_value_signed_integer_set(bt_value *integer_obj, int64_t val); + +extern bt_value *bt_value_real_create(void); + +extern bt_value *bt_value_real_create_init(double val); + +extern void bt_value_real_set(bt_value *real_obj, double val); + +extern bt_value *bt_value_string_create(void); + +extern bt_value *bt_value_string_create_init(const char *val); + +extern bt_value_status bt_value_string_set(bt_value *string_obj, + const char *val); + +extern bt_value *bt_value_array_create(void); + +extern bt_value *bt_value_array_borrow_element_by_index( + bt_value *array_obj, uint64_t index); + +extern bt_value_status bt_value_array_append_element( + bt_value *array_obj, + bt_value *element_obj); + +extern bt_value_status bt_value_array_append_bool_element( + bt_value *array_obj, bt_bool val); + +extern bt_value_status bt_value_array_append_unsigned_integer_element( + bt_value *array_obj, uint64_t val); + +extern bt_value_status bt_value_array_append_signed_integer_element( + bt_value *array_obj, int64_t val); + +extern bt_value_status bt_value_array_append_real_element( + bt_value *array_obj, double val); + +extern bt_value_status bt_value_array_append_string_element( + bt_value *array_obj, const char *val); + +extern bt_value_status bt_value_array_append_empty_array_element( + bt_value *array_obj); + +extern bt_value_status bt_value_array_append_empty_map_element( + bt_value *array_obj); + +extern bt_value_status bt_value_array_set_element_by_index( + bt_value *array_obj, uint64_t index, + bt_value *element_obj); + +extern bt_value *bt_value_map_create(void); + +extern bt_value *bt_value_map_borrow_entry_value( + bt_value *map_obj, const char *key); + +typedef bt_bool (* bt_value_map_foreach_entry_func)(const char *key, + bt_value *object, void *data); + +extern bt_value_status bt_value_map_foreach_entry( + bt_value *map_obj, + bt_value_map_foreach_entry_func func, void *data); + +extern bt_value_status bt_value_map_insert_entry( + bt_value *map_obj, const char *key, + bt_value *element_obj); + +extern bt_value_status bt_value_map_insert_bool_entry( + bt_value *map_obj, const char *key, bt_bool val); + +extern bt_value_status bt_value_map_insert_unsigned_integer_entry( + bt_value *map_obj, const char *key, uint64_t val); + +extern bt_value_status bt_value_map_insert_signed_integer_entry( + bt_value *map_obj, const char *key, int64_t val); + +extern bt_value_status bt_value_map_insert_real_entry( + bt_value *map_obj, const char *key, double val); + +extern bt_value_status bt_value_map_insert_string_entry( + bt_value *map_obj, const char *key, + const char *val); + +extern bt_value_status bt_value_map_insert_empty_array_entry( + bt_value *map_obj, const char *key); + +extern bt_value_status bt_value_map_insert_empty_map_entry( + bt_value *map_obj, const char *key); + +%{ +struct bt_value_map_get_keys_data { + struct bt_value *keys; +}; + +static int bt_value_map_get_keys_cb(const char *key, const struct bt_value *object, void *data) +{ + enum bt_value_status status; + struct bt_value_map_get_keys_data *priv_data = data; + + status = bt_value_array_append_string_element(priv_data->keys, key); + if (status != BT_VALUE_STATUS_OK) { + return BT_FALSE; + } + + return BT_TRUE; +} + +static struct bt_value *bt_value_map_get_keys(const struct bt_value *map_obj) +{ + enum bt_value_status status; + struct bt_value_map_get_keys_data data; + + data.keys = bt_value_array_create(); + if (!data.keys) { + return NULL; + } + + status = bt_value_map_foreach_entry_const(map_obj, bt_value_map_get_keys_cb, + &data); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + + goto end; + +error: + if (data.keys) { + BT_VALUE_PUT_REF_AND_RESET(data.keys); + } + +end: + return data.keys; +} +%} + +struct bt_value *bt_value_map_get_keys(const struct bt_value *map_obj); diff --git a/src/bindings/python/bt2/bt2/native_bt_version.i b/src/bindings/python/bt2/bt2/native_bt_version.i new file mode 100644 index 00000000..3d99e7d2 --- /dev/null +++ b/src/bindings/python/bt2/bt2/native_bt_version.i @@ -0,0 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Version functions */ +int bt_version_get_major(void); +int bt_version_get_minor(void); +int bt_version_get_patch(void); +const char *bt_version_get_extra(void); diff --git a/src/bindings/python/bt2/bt2/object.py b/src/bindings/python/bt2/bt2/object.py new file mode 100644 index 00000000..218965de --- /dev/null +++ b/src/bindings/python/bt2/bt2/object.py @@ -0,0 +1,138 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +class _BaseObject: + # Ensure that the object always has _ptr set, even if it throws during + # construction. + + def __new__(cls, *args, **kwargs): + obj = super().__new__(cls) + obj._ptr = None + return obj + + def __init__(self, ptr): + self._ptr = ptr + + @property + def addr(self): + return int(self._ptr) + + def __repr__(self): + return '<{}.{} object @ {}>'.format(self.__class__.__module__, + self.__class__.__name__, + hex(self.addr)) + + def __copy__(self): + raise NotImplementedError + + def __deepcopy__(self, memo): + raise NotImplementedError + + +# A Python object that is itself not refcounted, but is wholly owned by an +# object that is itself refcounted (a _SharedObject). A Babeltrace unique +# object gets destroyed once its owner gets destroyed (its refcount drops to +# 0). +# +# In the Python bindings, to avoid having to deal with issues with the lifetime +# of unique objects, we make it so acquiring a reference on a unique object +# acquires a reference on its owner. + +class _UniqueObject(_BaseObject): + + # Create a _UniqueObject. + # + # - ptr: SWIG Object, pointer to the unique object. + # - owner_ptr: SWIG Object, pointer to the owner of the unique + # object. A new reference is acquired. + # - owner_get_ref: Callback to get a reference on the owner + # - owner_put_ref: Callback to put a reference on the owner. + + @classmethod + def _create_from_ptr_and_get_ref(cls, ptr, owner_ptr, + owner_get_ref, owner_put_ref): + obj = cls.__new__(cls) + + obj._ptr = ptr + obj._owner_ptr = owner_ptr + obj._owner_get_ref = owner_get_ref + obj._owner_put_ref = owner_put_ref + + obj._owner_get_ref(obj._owner_ptr) + + return obj + + def __del__(self): + self._owner_put_ref(self._owner_ptr) + + +# Python object that owns a reference to a Babeltrace object. +class _SharedObject(_BaseObject): + + # Get a new reference on ptr. + # + # This must be implemented by subclasses to work correctly with a pointer + # of the native type they wrap. + + @staticmethod + def _get_ref(ptr): + raise NotImplementedError + + # Put a reference on ptr. + # + # This must be implemented by subclasses to work correctly with a pointer + # of the native type they wrap. + + @staticmethod + def _put_ref(ptr): + raise NotImplementedError + + # Create a _SharedObject from an existing reference. + # + # This assumes that the caller owns a reference to the Babeltrace object + # and transfers this ownership to the newly created Python object. + + @classmethod + def _create_from_ptr(cls, ptr_owned): + obj = cls.__new__(cls) + obj._ptr = ptr_owned + return obj + + # Like _create_from_ptr, but acquire a new reference rather than + # stealing the caller's reference. + + @classmethod + def _create_from_ptr_and_get_ref(cls, ptr): + obj = cls._create_from_ptr(ptr) + cls._get_ref(obj._ptr) + return obj + + def _release(self): + """Return the wrapped pointer, transfer its ownership to the + caller.""" + ptr = self._ptr + self._ptr = None + return ptr + + def __del__(self): + self._put_ref(self._ptr) diff --git a/src/bindings/python/bt2/bt2/packet.py b/src/bindings/python/bt2/bt2/packet.py new file mode 100644 index 00000000..93e9ac8f --- /dev/null +++ b/src/bindings/python/bt2/bt2/packet.py @@ -0,0 +1,47 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016-2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object +import bt2.field +import bt2 + + +class _Packet(object._SharedObject): + _get_ref = staticmethod(native_bt.packet_get_ref) + _put_ref = staticmethod(native_bt.packet_put_ref) + + @property + def stream(self): + stream_ptr = native_bt.packet_borrow_stream(self._ptr) + assert stream_ptr is not None + return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) + + @property + def context_field(self): + field_ptr = native_bt.packet_borrow_context_field(self._ptr) + + if field_ptr is None: + return + + return bt2.field._create_field_from_ptr(field_ptr, self._ptr, + self._get_ref, + self._put_ref) diff --git a/src/bindings/python/bt2/bt2/plugin.py b/src/bindings/python/bt2/bt2/plugin.py new file mode 100644 index 00000000..2d0d8b05 --- /dev/null +++ b/src/bindings/python/bt2/bt2/plugin.py @@ -0,0 +1,218 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import collections.abc +import bt2.component +import os.path +import bt2 + + +def find_plugins(path, recurse=True): + utils._check_str(path) + utils._check_bool(recurse) + plugin_set_ptr = None + + if os.path.isfile(path): + plugin_set_ptr = native_bt.plugin_find_all_from_file(path) + elif os.path.isdir(path): + plugin_set_ptr = native_bt.plugin_find_all_from_dir(path, int(recurse)) + + if plugin_set_ptr is None: + return + + return _PluginSet._create_from_ptr(plugin_set_ptr) + + +def find_plugin(name): + utils._check_str(name) + ptr = native_bt.plugin_find(name) + + if ptr is None: + return + + return _Plugin._create_from_ptr(ptr) + + +class _PluginSet(object._SharedObject, collections.abc.Sequence): + _put_ref = staticmethod(native_bt.plugin_set_put_ref) + _get_ref = staticmethod(native_bt.plugin_set_get_ref) + + def __len__(self): + count = native_bt.plugin_set_get_plugin_count(self._ptr) + assert(count >= 0) + return count + + def __getitem__(self, index): + utils._check_uint64(index) + + if index >= len(self): + raise IndexError + + plugin_ptr = native_bt.plugin_set_borrow_plugin_by_index_const(self._ptr, index) + assert plugin_ptr is not None + return _Plugin._create_from_ptr_and_get_ref(plugin_ptr) + + +class _PluginVersion: + def __init__(self, major, minor, patch, extra): + self._major = major + self._minor = minor + self._patch = patch + self._extra = extra + + @property + def major(self): + return self._major + + @property + def minor(self): + return self._minor + + @property + def patch(self): + return self._patch + + @property + def extra(self): + return self._extra + + def __str__(self): + extra = '' + + if self._extra is not None: + extra = self._extra + + return '{}.{}.{}{}'.format(self._major, self._minor, self._patch, extra) + + +class _PluginComponentClassesIterator(collections.abc.Iterator): + def __init__(self, plugin_comp_cls): + self._plugin_comp_cls = plugin_comp_cls + self._at = 0 + + def __next__(self): + plugin_ptr = self._plugin_comp_cls._plugin._ptr + total = self._plugin_comp_cls._component_class_count(plugin_ptr) + + if self._at == total: + raise StopIteration + + comp_cls_ptr = self._plugin_comp_cls._borrow_component_class_by_index(plugin_ptr, self._at) + assert comp_cls_ptr is not None + self._at += 1 + + comp_cls_type = self._plugin_comp_cls._comp_cls_type + comp_cls_pycls = bt2.component._COMP_CLS_TYPE_TO_GENERIC_COMP_CLS_PYCLS[comp_cls_type] + comp_cls_ptr = comp_cls_pycls._as_component_class_ptr(comp_cls_ptr) + name = native_bt.component_class_get_name(comp_cls_ptr) + assert name is not None + return name + + +class _PluginComponentClasses(collections.abc.Mapping): + def __init__(self, plugin): + self._plugin = plugin + + def __getitem__(self, key): + utils._check_str(key) + cc_ptr = self._borrow_component_class_by_name(self._plugin._ptr, key) + + if cc_ptr is None: + raise KeyError(key) + + return bt2.component._create_component_class_from_ptr_and_get_ref(cc_ptr, self._comp_cls_type) + + def __len__(self): + return self._component_class_count(self._plugin._ptr) + + def __iter__(self): + return _PluginComponentClassesIterator(self) + + +class _PluginSourceComponentClasses(_PluginComponentClasses): + _component_class_count = staticmethod(native_bt.plugin_get_source_component_class_count) + _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_source_component_class_by_name_const) + _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_source_component_class_by_index_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SOURCE + + +class _PluginFilterComponentClasses(_PluginComponentClasses): + _component_class_count = staticmethod(native_bt.plugin_get_filter_component_class_count) + _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_filter_component_class_by_name_const) + _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_filter_component_class_by_index_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_FILTER + + +class _PluginSinkComponentClasses(_PluginComponentClasses): + _component_class_count = staticmethod(native_bt.plugin_get_sink_component_class_count) + _borrow_component_class_by_name = staticmethod(native_bt.plugin_borrow_sink_component_class_by_name_const) + _borrow_component_class_by_index = staticmethod(native_bt.plugin_borrow_sink_component_class_by_index_const) + _comp_cls_type = native_bt.COMPONENT_CLASS_TYPE_SINK + + +class _Plugin(object._SharedObject): + _put_ref = staticmethod(native_bt.plugin_put_ref) + _get_ref = staticmethod(native_bt.plugin_get_ref) + + @property + def name(self): + name = native_bt.plugin_get_name(self._ptr) + assert(name is not None) + return name + + @property + def author(self): + return native_bt.plugin_get_author(self._ptr) + + @property + def license(self): + return native_bt.plugin_get_license(self._ptr) + + @property + def description(self): + return native_bt.plugin_get_description(self._ptr) + + @property + def path(self): + return native_bt.plugin_get_path(self._ptr) + + @property + def version(self): + status, major, minor, patch, extra = native_bt.plugin_get_version_wrapper(self._ptr) + + if status == native_bt.PROPERTY_AVAILABILITY_NOT_AVAILABLE: + return + + return _PluginVersion(major, minor, patch, extra) + + @property + def source_component_classes(self): + return _PluginSourceComponentClasses(self) + + @property + def filter_component_classes(self): + return _PluginFilterComponentClasses(self) + + @property + def sink_component_classes(self): + return _PluginSinkComponentClasses(self) diff --git a/src/bindings/python/bt2/bt2/port.py b/src/bindings/python/bt2/bt2/port.py new file mode 100644 index 00000000..f7c23669 --- /dev/null +++ b/src/bindings/python/bt2/bt2/port.py @@ -0,0 +1,136 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object +import bt2.component +import bt2.connection +import bt2.message_iterator +import bt2.message +import bt2 + + +def _create_from_ptr_and_get_ref(ptr, port_type): + cls = _PORT_TYPE_TO_PYCLS.get(port_type, None) + + if cls is None: + raise bt2.Error('unknown port type: {}'.format(port_type)) + + return cls._create_from_ptr_and_get_ref(ptr) + + +def _create_self_from_ptr_and_get_ref(ptr, port_type): + cls = _PORT_TYPE_TO_USER_PYCLS.get(port_type, None) + + if cls is None: + raise bt2.Error('unknown port type: {}'.format(port_type)) + + return cls._create_from_ptr_and_get_ref(ptr) + + +class _Port(object._SharedObject): + @classmethod + def _get_ref(cls, ptr): + ptr = cls._as_port_ptr(ptr) + return native_bt.port_get_ref(ptr) + + @classmethod + def _put_ref(cls, ptr): + ptr = cls._as_port_ptr(ptr) + return native_bt.port_put_ref(ptr) + + @property + def name(self): + ptr = self._as_port_ptr(self._ptr) + name = native_bt.port_get_name(ptr) + assert name is not None + return name + + @property + def connection(self): + ptr = self._as_port_ptr(self._ptr) + conn_ptr = native_bt.port_borrow_connection_const(ptr) + + if conn_ptr is None: + return + + return bt2.connection._Connection._create_from_ptr_and_get_ref(conn_ptr) + + @property + def is_connected(self): + return self.connection is not None + + +class _InputPort(_Port): + _as_port_ptr = staticmethod(native_bt.port_input_as_port_const) + + +class _OutputPort(_Port): + _as_port_ptr = staticmethod(native_bt.port_output_as_port_const) + + +class _UserComponentPort(_Port): + @classmethod + def _as_port_ptr(cls, ptr): + ptr = cls._as_self_port_ptr(ptr) + return native_bt.self_component_port_as_port(ptr) + + @property + def connection(self): + ptr = self._as_port_ptr(self._ptr) + conn_ptr = native_bt.port_borrow_connection_const(ptr) + + if conn_ptr is None: + return + + return bt2.connection._Connection._create_from_ptr_and_get_ref(conn_ptr) + + @property + def user_data(self): + ptr = self._as_self_port_ptr(self._ptr) + return native_bt.self_component_port_get_data(ptr) + + +class _UserComponentInputPort(_UserComponentPort, _InputPort): + _as_self_port_ptr = staticmethod(native_bt.self_component_port_input_as_self_component_port) + + def create_message_iterator(self): + msg_iter_ptr = native_bt.self_component_port_input_message_iterator_create(self._ptr) + if msg_iter_ptr is None: + raise bt2.CreationError('cannot create message iterator object') + + return bt2.message_iterator._UserComponentInputPortMessageIterator(msg_iter_ptr) + + +class _UserComponentOutputPort(_UserComponentPort, _OutputPort): + _as_self_port_ptr = staticmethod(native_bt.self_component_port_output_as_self_component_port) + + +_PORT_TYPE_TO_PYCLS = { + native_bt.PORT_TYPE_INPUT: _InputPort, + native_bt.PORT_TYPE_OUTPUT: _OutputPort, +} + + +_PORT_TYPE_TO_USER_PYCLS = { + native_bt.PORT_TYPE_INPUT: _UserComponentInputPort, + native_bt.PORT_TYPE_OUTPUT: _UserComponentOutputPort, +} diff --git a/src/bindings/python/bt2/bt2/py_plugin.py b/src/bindings/python/bt2/bt2/py_plugin.py new file mode 100644 index 00000000..b8450308 --- /dev/null +++ b/src/bindings/python/bt2/bt2/py_plugin.py @@ -0,0 +1,135 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import utils +import bt2.component + + +def plugin_component_class(component_class): + if not issubclass(component_class, bt2.component._UserComponent): + raise TypeError('component class is not a subclass of a user component class') + + component_class._bt_plugin_component_class = None + return component_class + + +def register_plugin(module_name, name, description=None, author=None, + license=None, version=None): + import sys + + if module_name not in sys.modules: + raise RuntimeError("cannot find module '{}' in loaded modules".format(module_name)) + + utils._check_str(name) + + if description is not None: + utils._check_str(description) + + if author is not None: + utils._check_str(author) + + if license is not None: + utils._check_str(license) + + if version is not None: + if not _validate_version(version): + raise ValueError('wrong version: expecting a tuple: (major, minor, patch) or (major, minor, patch, extra)') + + sys.modules[module_name]._bt_plugin_info = _PluginInfo(name, description, + author, license, + version) + + +def _validate_version(version): + if version is None: + return True + + if not isinstance(version, tuple): + return False + + if len(version) < 3 or len(version) > 4: + return False + + if not isinstance(version[0], int): + return False + + if not isinstance(version[1], int): + return False + + if not isinstance(version[2], int): + return False + + if len(version) == 4: + if not isinstance(version[3], str): + return False + + return True + + +class _PluginInfo: + def __init__(self, name, description, author, license, version): + self.name = name + self.description = description + self.author = author + self.license = license + self.version = version + self.comp_class_addrs = None + + +# called by the BT plugin system +def _try_load_plugin_module(path): + import importlib.machinery + import inspect + import hashlib + + if path is None: + raise TypeError('missing path') + + # In order to load the module uniquely from its path, even from + # different files which have the same basename, we hash the path + # and prefix with `bt_plugin_`. This is its key in sys.modules. + h = hashlib.sha256() + h.update(path.encode()) + module_name = 'bt_plugin_{}'.format(h.hexdigest()) + + # try loading the module: any raised exception is catched by the caller + mod = importlib.machinery.SourceFileLoader(module_name, path).load_module() + + # we have the module: look for its plugin info first + if not hasattr(mod, '_bt_plugin_info'): + raise RuntimeError("missing '_bt_plugin_info' module attribute") + + plugin_info = mod._bt_plugin_info + + # search for user component classes + def is_user_comp_class(obj): + if not inspect.isclass(obj): + return False + + if not hasattr(obj, '_bt_plugin_component_class'): + return False + + return True + + comp_class_entries = inspect.getmembers(mod, is_user_comp_class) + plugin_info.comp_class_addrs = [entry[1].addr for entry in comp_class_entries] + return plugin_info diff --git a/src/bindings/python/bt2/bt2/query_executor.py b/src/bindings/python/bt2/bt2/query_executor.py new file mode 100644 index 00000000..a87713ff --- /dev/null +++ b/src/bindings/python/bt2/bt2/query_executor.py @@ -0,0 +1,93 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.component +import bt2 + + +class QueryExecutor(object._SharedObject): + _get_ref = staticmethod(native_bt.query_executor_get_ref) + _put_ref = staticmethod(native_bt.query_executor_put_ref) + + def _handle_status(self, status, gen_error_msg): + if status == native_bt.QUERY_EXECUTOR_STATUS_AGAIN: + raise bt2.TryAgain + elif status == native_bt.QUERY_EXECUTOR_STATUS_CANCELED: + raise bt2.QueryExecutorCanceled + elif status == native_bt.QUERY_EXECUTOR_STATUS_INVALID_OBJECT: + raise bt2.InvalidQueryObject + elif status == native_bt.QUERY_EXECUTOR_STATUS_INVALID_PARAMS: + raise bt2.InvalidQueryParams + elif status < 0: + raise bt2.Error(gen_error_msg) + + def __init__(self): + ptr = native_bt.query_executor_create() + + if ptr is None: + raise bt2.CreationError('cannot create query executor object') + + super().__init__(ptr) + + def cancel(self): + status = native_bt.query_executor_cancel(self._ptr) + self._handle_status(status, 'cannot cancel query executor object') + + @property + def is_canceled(self): + is_canceled = native_bt.query_executor_is_canceled(self._ptr) + assert(is_canceled >= 0) + return is_canceled > 0 + + def query(self, component_class, object, params=None): + if self.is_canceled: + raise bt2.QueryExecutorCanceled + + if not isinstance(component_class, bt2.component._GenericComponentClass): + err = False + + try: + if not issubclass(component_class, bt2.component._UserComponent): + err = True + except TypeError: + err = True + + if err: + o = component_class + raise TypeError("'{}' is not a component class object".format(o)) + + utils._check_str(object) + + if params is None: + params_ptr = native_bt.value_null + else: + params = bt2.create_value(params) + params_ptr = params._ptr + + cc_ptr = component_class._component_class_ptr() + + status, result_ptr = native_bt.query_executor_query(self._ptr, cc_ptr, + object, params_ptr) + self._handle_status(status, 'cannot query component class') + assert(result_ptr) + return bt2.value._create_from_ptr(result_ptr) diff --git a/src/bindings/python/bt2/bt2/stream.py b/src/bindings/python/bt2/bt2/stream.py new file mode 100644 index 00000000..5accfcd0 --- /dev/null +++ b/src/bindings/python/bt2/bt2/stream.py @@ -0,0 +1,60 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016-2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, utils +import bt2.packet +import bt2.event +import bt2 + + +class _Stream(bt2.object._SharedObject): + _get_ref = staticmethod(native_bt.stream_get_ref) + _put_ref = staticmethod(native_bt.stream_put_ref) + + @property + def cls(self): + stream_class_ptr = native_bt.stream_borrow_class(self._ptr) + assert stream_class_ptr is not None + return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(stream_class_ptr) + + @property + def name(self): + return native_bt.stream_get_name(self._ptr) + + def _name(self, name): + utils._check_str(name) + native_bt.stream_set_name(self._ptr, name) + + _name = property(fset=_name) + + @property + def id(self): + id = native_bt.stream_get_id(self._ptr) + return id if id >= 0 else None + + def create_packet(self): + packet_ptr = native_bt.packet_create(self._ptr) + + if packet_ptr is None: + raise bt2.CreationError('cannot create packet object') + + return bt2.packet._Packet._create_from_ptr(packet_ptr) diff --git a/src/bindings/python/bt2/bt2/stream_class.py b/src/bindings/python/bt2/bt2/stream_class.py new file mode 100644 index 00000000..54b5b38b --- /dev/null +++ b/src/bindings/python/bt2/bt2/stream_class.py @@ -0,0 +1,253 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.field_class +import bt2.event_class +import collections.abc +import bt2.stream +import bt2 + + +class _StreamClass(object._SharedObject, collections.abc.Mapping): + _get_ref = staticmethod(native_bt.stream_class_get_ref) + _put_ref = staticmethod(native_bt.stream_class_put_ref) + + def __getitem__(self, key): + utils._check_int64(key) + ec_ptr = native_bt.stream_class_borrow_event_class_by_id(self._ptr, key) + + if ec_ptr is None: + raise KeyError(key) + + return bt2.event_class._EventClass._create_from_ptr_and_get_ref(ec_ptr) + + def __len__(self): + count = native_bt.stream_class_get_event_class_count(self._ptr) + assert count >= 0 + return count + + def __iter__(self): + for idx in range(len(self)): + ec_ptr = native_bt.stream_class_borrow_event_class_by_index_const(self._ptr, idx) + assert ec_ptr is not None + + id = native_bt.event_class_get_id(ec_ptr) + assert id >= 0 + + yield id + + def create_event_class(self, id=None, name=None, log_level=None, emf_uri=None, + specific_context_field_class=None, + payload_field_class=None): + if self.assigns_automatic_event_class_id: + if id is not None: + raise ValueError('id provided, but stream class assigns automatic event class ids') + + ec_ptr = native_bt.event_class_create(self._ptr) + else: + if id is None: + raise ValueError('id not provided, but stream class does not assign automatic event class ids') + + utils._check_uint64(id) + ec_ptr = native_bt.event_class_create_with_id(self._ptr, id) + + event_class = bt2.event_class._EventClass._create_from_ptr(ec_ptr) + + if name is not None: + event_class._name = name + + if log_level is not None: + event_class._log_level = log_level + + if emf_uri is not None: + event_class._emf_uri = emf_uri + + if specific_context_field_class is not None: + event_class._specific_context_field_class = specific_context_field_class + + if payload_field_class is not None: + event_class._payload_field_class = payload_field_class + + return event_class + + @property + def trace_class(self): + tc_ptr = native_bt.stream_class_borrow_trace_class_const(self._ptr) + + if tc_ptr is not None: + return bt2._TraceClass._create_from_ptr_and_get_ref(tc_ptr) + + @property + def name(self): + return native_bt.stream_class_get_name(self._ptr) + + def _name(self, name): + utils._check_str(name) + ret = native_bt.stream_class_set_name(self._ptr, name) + utils._handle_ret(ret, "cannot set stream class object's name") + + _name = property(fset=_name) + + @property + def assigns_automatic_event_class_id(self): + return native_bt.stream_class_assigns_automatic_event_class_id(self._ptr) + + def _assigns_automatic_event_class_id(self, auto_id): + utils._check_bool(auto_id) + return native_bt.stream_class_set_assigns_automatic_event_class_id(self._ptr, auto_id) + + _assigns_automatic_event_class_id = property(fset=_assigns_automatic_event_class_id) + + @property + def assigns_automatic_stream_id(self): + return native_bt.stream_class_assigns_automatic_stream_id(self._ptr) + + def _assigns_automatic_stream_id(self, auto_id): + utils._check_bool(auto_id) + return native_bt.stream_class_set_assigns_automatic_stream_id(self._ptr, auto_id) + + _assigns_automatic_stream_id = property(fset=_assigns_automatic_stream_id) + + @property + def packets_have_beginning_default_clock_snapshot(self): + return native_bt.stream_class_packets_have_beginning_default_clock_snapshot(self._ptr) + + def _packets_have_beginning_default_clock_snapshot(self, value): + utils._check_bool(value) + native_bt.stream_class_set_packets_have_beginning_default_clock_snapshot(self._ptr, value) + + _packets_have_beginning_default_clock_snapshot = property(fset=_packets_have_beginning_default_clock_snapshot) + + @property + def packets_have_end_default_clock_snapshot(self): + return native_bt.stream_class_packets_have_end_default_clock_snapshot(self._ptr) + + def _packets_have_end_default_clock_snapshot(self, value): + utils._check_bool(value) + native_bt.stream_class_set_packets_have_end_default_clock_snapshot(self._ptr, value) + + _packets_have_end_default_clock_snapshot = property(fset=_packets_have_end_default_clock_snapshot) + + @property + def supports_discarded_events(self): + return native_bt.stream_class_supports_discarded_events(self._ptr) + + def _set_supports_discarded_events(self, supports, with_cs=False): + utils._check_bool(supports) + utils._check_bool(with_cs) + + if not supports and with_cs: + raise ValueError('cannot not support discarded events, but have default clock snapshots') + + native_bt.stream_class_set_supports_discarded_events(self._ptr, supports, with_cs) + + @property + def discarded_events_have_default_clock_snapshots(self): + return native_bt.stream_class_discarded_events_have_default_clock_snapshots(self._ptr) + + @property + def supports_discarded_packets(self): + return native_bt.stream_class_supports_discarded_packets(self._ptr) + + def _set_supports_discarded_packets(self, supports, with_cs): + utils._check_bool(supports) + utils._check_bool(with_cs) + + if not supports and with_cs: + raise ValueError('cannot not support discarded packets, but have default clock snapshots') + + native_bt.stream_class_set_supports_discarded_packets(self._ptr, supports, with_cs) + + @property + def discarded_packets_have_default_clock_snapshots(self): + return native_bt.stream_class_discarded_packets_have_default_clock_snapshots(self._ptr) + + @property + def id(self): + id = native_bt.stream_class_get_id(self._ptr) + + if id < 0: + return + + return id + + @id.setter + def id(self, id): + utils._check_int64(id) + ret = native_bt.stream_class_set_id(self._ptr, id) + utils._handle_ret(ret, "cannot set stream class object's ID") + + @property + def packet_context_field_class(self): + fc_ptr = native_bt.stream_class_borrow_packet_context_field_class_const(self._ptr) + + if fc_ptr is None: + return + + return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) + + def _packet_context_field_class(self, packet_context_field_class): + if packet_context_field_class is not None: + utils._check_type(packet_context_field_class, + bt2.field_class._StructureFieldClass) + ret = native_bt.stream_class_set_packet_context_field_class(self._ptr, + packet_context_field_class._ptr) + utils._handle_ret(ret, "cannot set stream class' packet context field class") + + _packet_context_field_class = property(fset=_packet_context_field_class) + + @property + def event_common_context_field_class(self): + fc_ptr = native_bt.stream_class_borrow_event_common_context_field_class_const(self._ptr) + + if fc_ptr is None: + return + + return bt2.field_class._create_field_class_from_ptr_and_get_ref(fc_ptr) + + def _event_common_context_field_class(self, event_common_context_field_class): + if event_common_context_field_class is not None: + utils._check_type(event_common_context_field_class, + bt2.field_class._StructureFieldClass) + + set_context_fn = native_bt.stream_class_set_event_common_context_field_class + ret = set_context_fn(self._ptr, event_common_context_field_class._ptr) + + utils._handle_ret(ret, "cannot set stream class' event context field type") + + _event_common_context_field_class = property(fset=_event_common_context_field_class) + + @property + def default_clock_class(self): + cc_ptr = native_bt.stream_class_borrow_default_clock_class(self._ptr) + if cc_ptr is None: + return + + return bt2.clock_class._ClockClass._create_from_ptr_and_get_ref(cc_ptr) + + def _default_clock_class(self, clock_class): + utils._check_type(clock_class, bt2.clock_class._ClockClass) + native_bt.stream_class_set_default_clock_class( + self._ptr, clock_class._ptr) + + _default_clock_class = property(fset=_default_clock_class) diff --git a/src/bindings/python/bt2/bt2/trace.py b/src/bindings/python/bt2/bt2/trace.py new file mode 100644 index 00000000..5830ec3f --- /dev/null +++ b/src/bindings/python/bt2/bt2/trace.py @@ -0,0 +1,122 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import bt2.field_class +import collections.abc +import bt2.value +import bt2.stream +import bt2.trace_class +import bt2 +import functools + + +def _trace_destruction_listener_from_native(user_listener, trace_ptr): + trace = bt2.trace._Trace._create_from_ptr_and_get_ref(trace_ptr) + user_listener(trace) + + +class _Trace(object._SharedObject, collections.abc.Mapping): + _get_ref = staticmethod(native_bt.trace_get_ref) + _put_ref = staticmethod(native_bt.trace_put_ref) + + def __len__(self): + count = native_bt.trace_get_stream_count(self._ptr) + assert count >= 0 + return count + + def __getitem__(self, id): + utils._check_uint64(id) + + stream_ptr = native_bt.trace_borrow_stream_by_id_const(self._ptr, id) + + if stream_ptr is None: + raise KeyError(id) + + return bt2.stream._Stream._create_from_ptr_and_get_ref(stream_ptr) + + def __iter__(self): + for idx in range(len(self)): + stream_ptr = native_bt.trace_borrow_stream_by_index_const(self._ptr, idx) + assert stream_ptr is not None + + id = native_bt.stream_get_id(stream_ptr) + assert id >= 0 + + yield id + + @property + def cls(self): + trace_class_ptr = native_bt.trace_borrow_class(self._ptr) + assert trace_class_ptr is not None + return bt2.trace_class._TraceClass._create_from_ptr_and_get_ref(trace_class_ptr) + + @property + def name(self): + return native_bt.trace_get_name(self._ptr) + + def _name(self, name): + utils._check_str(name) + ret = native_bt.trace_set_name(self._ptr, name) + utils._handle_ret(ret, "cannot set trace class object's name") + + _name = property(fset=_name) + + def create_stream(self, stream_class, id=None, name=None): + utils._check_type(stream_class, bt2.stream_class._StreamClass) + + if stream_class.assigns_automatic_stream_id: + if id is not None: + raise ValueError("id provided, but stream class assigns automatic stream ids") + + stream_ptr = native_bt.stream_create(stream_class._ptr, self._ptr) + else: + if id is None: + raise ValueError("id not provided, but stream class does not assign automatic stream ids") + + utils._check_uint64(id) + stream_ptr = native_bt.stream_create_with_id(stream_class._ptr, self._ptr, id) + + if stream_ptr is None: + raise bt2.CreationError('cannot create stream object') + + stream = bt2.stream._Stream._create_from_ptr(stream_ptr) + + if name is not None: + stream._name = name + + return stream + + def add_destruction_listener(self, listener): + '''Add a listener to be called when the trace is destroyed.''' + if not callable(listener): + raise TypeError("'listener' parameter is not callable") + + fn = native_bt.py3_trace_add_destruction_listener + listener_from_native = functools.partial(_trace_destruction_listener_from_native, + listener) + + listener_id = fn(self._ptr, listener_from_native) + if listener_id is None: + utils._raise_bt2_error('cannot add destruction listener to trace object') + + return bt2._ListenerHandle(listener_id, self) diff --git a/src/bindings/python/bt2/bt2/trace_class.py b/src/bindings/python/bt2/bt2/trace_class.py new file mode 100644 index 00000000..5713db8c --- /dev/null +++ b/src/bindings/python/bt2/bt2/trace_class.py @@ -0,0 +1,335 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# Copyright (c) 2018 Francis Deslauriers +# Copyright (c) 2019 Simon Marchi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +__all__ = ['_TraceClass'] + +import bt2 +from bt2 import native_bt, utils, object +import bt2.stream_class +import uuid as uuidp +import collections.abc +import functools + + +class _TraceClassEnv(collections.abc.MutableMapping): + def __init__(self, trace_class): + self._trace_class = trace_class + + def __getitem__(self, key): + utils._check_str(key) + + borrow_entry_fn = native_bt.trace_class_borrow_environment_entry_value_by_name_const + value_ptr = borrow_entry_fn(self._trace_class._ptr, key) + + if value_ptr is None: + raise KeyError(key) + + return bt2.value._create_from_ptr_and_get_ref(value_ptr) + + def __setitem__(self, key, value): + if isinstance(value, str): + set_env_entry_fn = native_bt.trace_class_set_environment_entry_string + elif isinstance(value, int): + set_env_entry_fn = native_bt.trace_class_set_environment_entry_integer + else: + raise TypeError('expected str or int, got {}'.format(type(value))) + + ret = set_env_entry_fn(self._trace_class._ptr, key, value) + + utils._handle_ret(ret, "cannot set trace class object's environment entry") + + def __delitem__(self, key): + raise NotImplementedError + + def __len__(self): + count = native_bt.trace_class_get_environment_entry_count(self._trace_class._ptr) + assert count >= 0 + return count + + def __iter__(self): + trace_class_ptr = self._trace_class_env._trace_class._ptr + + for idx in range(len(self)): + borrow_entry_fn = native_bt.trace_class_borrow_environment_entry_by_index_const + entry_name, _ = borrow_entry_fn(trace_class_ptr, idx) + assert entry_name is not None + yield entry_name + + +class _StreamClassIterator(collections.abc.Iterator): + def __init__(self, trace_class): + self._trace_class = trace_class + self._at = 0 + + def __next__(self): + if self._at == len(self._trace_class): + raise StopIteration + + borrow_stream_class_fn = native_bt.trace_class_borrow_stream_class_by_index_const + sc_ptr = borrow_stream_class_fn(self._trace_class._ptr, self._at) + assert sc_ptr + id = native_bt.stream_class_get_id(sc_ptr) + assert id >= 0 + self._at += 1 + return id + + +def _trace_class_destruction_listener_from_native(user_listener, trace_class_ptr): + trace_class = bt2.trace_class._TraceClass._create_from_ptr_and_get_ref(trace_class_ptr) + user_listener(trace_class) + + +class _TraceClass(object._SharedObject, collections.abc.Mapping): + _get_ref = staticmethod(native_bt.trace_class_get_ref) + _put_ref = staticmethod(native_bt.trace_class_put_ref) + + @property + def uuid(self): + uuid_bytes = native_bt.trace_class_get_uuid(self._ptr) + if uuid_bytes is None: + return + + return uuidp.UUID(bytes=uuid_bytes) + + def _uuid(self, uuid): + utils._check_type(uuid, uuidp.UUID) + native_bt.trace_class_set_uuid(self._ptr, uuid.bytes) + + _uuid = property(fset=_uuid) + + # Instantiate a trace of this class. + + def __call__(self, name=None): + trace_ptr = native_bt.trace_create(self._ptr) + + if trace_ptr is None: + raise bt2.CreationError('cannot create trace class object') + + trace = bt2.trace._Trace._create_from_ptr(trace_ptr) + + if name is not None: + trace._name = name + + return trace + + # Number of stream classes in this trace class. + + def __len__(self): + count = native_bt.trace_class_get_stream_class_count(self._ptr) + assert count >= 0 + return count + + # Get a stream class by stream id. + + def __getitem__(self, key): + utils._check_uint64(key) + + sc_ptr = native_bt.trace_class_borrow_stream_class_by_id_const(self._ptr, key) + if sc_ptr is None: + raise KeyError(key) + + return bt2.stream_class._StreamClass._create_from_ptr_and_get_ref(sc_ptr) + + def __iter__(self): + for idx in range(len(self)): + sc_ptr = native_bt.trace_class_borrow_stream_class_by_index_const(self._ptr, idx) + assert sc_ptr is not None + + id = native_bt.stream_class_get_id(sc_ptr) + assert id >= 0 + + yield id + + @property + def env(self): + return _TraceClassEnv(self) + + def create_stream_class(self, id=None, + name=None, + packet_context_field_class=None, + event_common_context_field_class=None, + default_clock_class=None, + assigns_automatic_event_class_id=True, + assigns_automatic_stream_id=True, + packets_have_beginning_default_clock_snapshot=False, + packets_have_end_default_clock_snapshot=False, + supports_discarded_events=False, + discarded_events_have_default_clock_snapshots=False, + supports_discarded_packets=False, + discarded_packets_have_default_clock_snapshots=False): + + if self.assigns_automatic_stream_class_id: + if id is not None: + raise ValueError('id provided, but trace class assigns automatic stream class ids') + + sc_ptr = native_bt.stream_class_create(self._ptr) + else: + if id is None: + raise ValueError('id not provided, but trace class does not assign automatic stream class ids') + + utils._check_uint64(id) + sc_ptr = native_bt.stream_class_create_with_id(self._ptr, id) + + sc = bt2.stream_class._StreamClass._create_from_ptr(sc_ptr) + + if name is not None: + sc._name = name + + if packet_context_field_class is not None: + sc._packet_context_field_class = packet_context_field_class + + if event_common_context_field_class is not None: + sc._event_common_context_field_class = event_common_context_field_class + + if default_clock_class is not None: + sc._default_clock_class = default_clock_class + + sc._assigns_automatic_event_class_id = assigns_automatic_event_class_id + sc._assigns_automatic_stream_id = assigns_automatic_stream_id + sc._packets_have_beginning_default_clock_snapshot = packets_have_beginning_default_clock_snapshot + sc._packets_have_end_default_clock_snapshot = packets_have_end_default_clock_snapshot + sc._set_supports_discarded_events(supports_discarded_events, + discarded_events_have_default_clock_snapshots) + sc._set_supports_discarded_packets(supports_discarded_packets, + discarded_packets_have_default_clock_snapshots) + return sc + + @property + def assigns_automatic_stream_class_id(self): + return native_bt.trace_class_assigns_automatic_stream_class_id(self._ptr) + + def _assigns_automatic_stream_class_id(self, auto_id): + utils._check_bool(auto_id) + return native_bt.trace_class_set_assigns_automatic_stream_class_id(self._ptr, auto_id) + + _assigns_automatic_stream_class_id = property(fset=_assigns_automatic_stream_class_id) + + # Field class creation methods. + + def _check_create_status(self, ptr, type_name): + if ptr is None: + raise bt2.CreationError( + 'cannot create {} field class'.format(type_name)) + + def _create_integer_field_class(self, create_func, py_cls, type_name, field_value_range, preferred_display_base): + field_class_ptr = create_func(self._ptr) + self._check_create_status(field_class_ptr, type_name) + + field_class = py_cls._create_from_ptr(field_class_ptr) + + if field_value_range is not None: + field_class._field_value_range = field_value_range + + if preferred_display_base is not None: + field_class._preferred_display_base = preferred_display_base + + return field_class + + def create_signed_integer_field_class(self, field_value_range=None, preferred_display_base=None): + return self._create_integer_field_class(native_bt.field_class_signed_integer_create, + bt2.field_class._SignedIntegerFieldClass, + 'signed integer', field_value_range, preferred_display_base) + + def create_unsigned_integer_field_class(self, field_value_range=None, preferred_display_base=None): + return self._create_integer_field_class(native_bt.field_class_unsigned_integer_create, + bt2.field_class._UnsignedIntegerFieldClass, + 'unsigned integer', field_value_range, preferred_display_base) + + def create_signed_enumeration_field_class(self, field_value_range=None, preferred_display_base=None): + return self._create_integer_field_class(native_bt.field_class_signed_enumeration_create, + bt2.field_class._SignedEnumerationFieldClass, + 'signed enumeration', field_value_range, preferred_display_base) + + def create_unsigned_enumeration_field_class(self, field_value_range=None, preferred_display_base=None): + return self._create_integer_field_class(native_bt.field_class_unsigned_enumeration_create, + bt2.field_class._UnsignedEnumerationFieldClass, + 'unsigned enumeration', field_value_range, preferred_display_base) + + def create_real_field_class(self, is_single_precision=False): + field_class_ptr = native_bt.field_class_real_create(self._ptr) + self._check_create_status(field_class_ptr, 'real') + + field_class = bt2.field_class._RealFieldClass._create_from_ptr(field_class_ptr) + + field_class._is_single_precision = is_single_precision + + return field_class + + def create_structure_field_class(self): + field_class_ptr = native_bt.field_class_structure_create(self._ptr) + self._check_create_status(field_class_ptr, 'structure') + + return bt2.field_class._StructureFieldClass._create_from_ptr(field_class_ptr) + + def create_string_field_class(self): + field_class_ptr = native_bt.field_class_string_create(self._ptr) + self._check_create_status(field_class_ptr, 'string') + + return bt2.field_class._StringFieldClass._create_from_ptr(field_class_ptr) + + def create_static_array_field_class(self, elem_fc, length): + utils._check_type(elem_fc, bt2.field_class._FieldClass) + utils._check_uint64(length) + ptr = native_bt.field_class_static_array_create(self._ptr, elem_fc._ptr, length) + self._check_create_status(ptr, 'static array') + + return bt2.field_class._StaticArrayFieldClass._create_from_ptr_and_get_ref(ptr) + + def create_dynamic_array_field_class(self, elem_fc, length_fc=None): + utils._check_type(elem_fc, bt2.field_class._FieldClass) + ptr = native_bt.field_class_dynamic_array_create(self._ptr, elem_fc._ptr) + self._check_create_status(ptr, 'dynamic array') + obj = bt2.field_class._DynamicArrayFieldClass._create_from_ptr(ptr) + + if length_fc is not None: + obj._length_field_class = length_fc + + return obj + + def create_variant_field_class(self, selector_fc=None): + ptr = native_bt.field_class_variant_create(self._ptr) + self._check_create_status(ptr, 'variant') + obj = bt2.field_class._VariantFieldClass._create_from_ptr(ptr) + + if selector_fc is not None: + obj._selector_field_class = selector_fc + + return obj + + # Add a listener to be called when the trace class is destroyed. + + def add_destruction_listener(self, listener): + + if not callable(listener): + raise TypeError("'listener' parameter is not callable") + + fn = native_bt.py3_trace_class_add_destruction_listener + listener_from_native = functools.partial(_trace_class_destruction_listener_from_native, + listener) + + listener_id = fn(self._ptr, listener_from_native) + if listener_id is None: + utils._raise_bt2_error('cannot add destruction listener to trace class object') + + return bt2._ListenerHandle(listener_id, self) diff --git a/src/bindings/python/bt2/bt2/trace_collection_message_iterator.py b/src/bindings/python/bt2/bt2/trace_collection_message_iterator.py new file mode 100644 index 00000000..10b555f1 --- /dev/null +++ b/src/bindings/python/bt2/bt2/trace_collection_message_iterator.py @@ -0,0 +1,322 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import utils +import bt2 +import itertools +import bt2.message_iterator +import datetime +from collections import namedtuple +import numbers + + +# a pair of component and ComponentSpec +_ComponentAndSpec = namedtuple('_ComponentAndSpec', ['comp', 'spec']) + + +class ComponentSpec: + def __init__(self, plugin_name, class_name, params=None): + utils._check_str(plugin_name) + utils._check_str(class_name) + self._plugin_name = plugin_name + self._class_name = class_name + + if type(params) is str: + self._params = bt2.create_value({'paths': [params]}) + else: + self._params = bt2.create_value(params) + + @property + def plugin_name(self): + return self._plugin_name + + @property + def class_name(self): + return self._class_name + + @property + def params(self): + return self._params + + +# datetime.datetime or integral to nanoseconds +def _get_ns(obj): + if obj is None: + return + + if isinstance(obj, numbers.Real): + # consider that it's already in seconds + s = obj + elif isinstance(obj, datetime.datetime): + # s -> ns + s = obj.timestamp() + else: + raise TypeError('"{}" is not an integral number or a datetime.datetime object'.format(obj)) + + return int(s * 1e9) + + +class _CompClsType: + SOURCE = 0 + FILTER = 1 + + +class TraceCollectionMessageIterator(bt2.message_iterator._MessageIterator): + def __init__(self, source_component_specs, filter_component_specs=None, + stream_intersection_mode=False, begin=None, end=None): + utils._check_bool(stream_intersection_mode) + self._stream_intersection_mode = stream_intersection_mode + self._begin_ns = _get_ns(begin) + self._end_ns = _get_ns(end) + + if type(source_component_specs) is ComponentSpec: + source_component_specs = [source_component_specs] + + if type(filter_component_specs) is ComponentSpec: + filter_component_specs = [filter_component_specs] + elif filter_component_specs is None: + filter_component_specs = [] + + self._src_comp_specs = source_component_specs + self._flt_comp_specs = filter_component_specs + self._next_suffix = 1 + self._connect_ports = False + + # lists of _ComponentAndSpec + self._src_comps_and_specs = [] + self._flt_comps_and_specs = [] + + self._validate_component_specs(source_component_specs) + self._validate_component_specs(filter_component_specs) + self._build_graph() + + def _validate_component_specs(self, comp_specs): + for comp_spec in comp_specs: + if type(comp_spec) is not ComponentSpec: + raise TypeError('"{}" object is not a ComponentSpec'.format(type(comp_spec))) + + def __next__(self): + return next(self._msg_iter) + + def _create_stream_intersection_trimmer(self, component, port): + # find the original parameters specified by the user to create + # this port's component to get the `path` parameter + for src_comp_and_spec in self._src_comps_and_specs: + if component == src_comp_and_spec.comp: + break + + try: + paths = src_comp_and_spec.spec.params['paths'] + except Exception as e: + raise bt2.Error('all source components must be created with a "paths" parameter in stream intersection mode') from e + + params = {'paths': paths} + + # query the port's component for the `trace-info` object which + # contains the stream intersection range for each exposed + # trace + query_exec = bt2.QueryExecutor() + trace_info_res = query_exec.query(src_comp_and_spec.comp.cls, + 'trace-info', params) + begin = None + end = None + + # find the trace info for this port's trace + try: + for trace_info in trace_info_res: + for stream in trace_info['streams']: + if stream['port-name'] == port.name: + range_ns = trace_info['intersection-range-ns'] + begin = range_ns['begin'] + end = range_ns['end'] + break + except Exception: + pass + + if begin is None or end is None: + raise bt2.Error('cannot find stream intersection range for port "{}"'.format(port.name)) + + name = 'trimmer-{}-{}'.format(src_comp_and_spec.comp.name, port.name) + return self._create_trimmer(begin, end, name) + + def _create_muxer(self): + plugin = bt2.find_plugin('utils') + + if plugin is None: + raise bt2.Error('cannot find "utils" plugin (needed for the muxer)') + + if 'muxer' not in plugin.filter_component_classes: + raise bt2.Error('cannot find "muxer" filter component class in "utils" plugin') + + comp_cls = plugin.filter_component_classes['muxer'] + return self._graph.add_component(comp_cls, 'muxer') + + def _create_trimmer(self, begin_ns, end_ns, name): + plugin = bt2.find_plugin('utils') + + if plugin is None: + raise bt2.Error('cannot find "utils" plugin (needed for the trimmer)') + + if 'trimmer' not in plugin.filter_component_classes: + raise bt2.Error('cannot find "trimmer" filter component class in "utils" plugin') + + params = {} + + def ns_to_string(ns): + s_part = ns // 1000000000 + ns_part = ns % 1000000000 + return '{}.{:09d}'.format(s_part, ns_part) + + if begin_ns is not None: + params['begin'] = ns_to_string(begin_ns) + + if end_ns is not None: + params['end'] = ns_to_string(end_ns) + + comp_cls = plugin.filter_component_classes['trimmer'] + return self._graph.add_component(comp_cls, name, params) + + def _get_unique_comp_name(self, comp_spec): + name = '{}-{}'.format(comp_spec.plugin_name, + comp_spec.class_name) + comps_and_specs = itertools.chain(self._src_comps_and_specs, + self._flt_comps_and_specs) + + if name in [comp_and_spec.comp.name for comp_and_spec in comps_and_specs]: + name += '-{}'.format(self._next_suffix) + self._next_suffix += 1 + + return name + + def _create_comp(self, comp_spec, comp_cls_type): + plugin = bt2.find_plugin(comp_spec.plugin_name) + + if plugin is None: + raise bt2.Error('no such plugin: {}'.format(comp_spec.plugin_name)) + + if comp_cls_type == _CompClsType.SOURCE: + comp_classes = plugin.source_component_classes + else: + comp_classes = plugin.filter_component_classes + + if comp_spec.class_name not in comp_classes: + cc_type = 'source' if comp_cls_type == _CompClsType.SOURCE else 'filter' + raise bt2.Error('no such {} component class in "{}" plugin: {}'.format(cc_type, + comp_spec.plugin_name, + comp_spec.class_name)) + + comp_cls = comp_classes[comp_spec.class_name] + name = self._get_unique_comp_name(comp_spec) + comp = self._graph.add_component(comp_cls, name, comp_spec.params) + return comp + + def _get_free_muxer_input_port(self): + for port in self._muxer_comp.input_ports.values(): + if not port.is_connected: + return port + + def _connect_src_comp_port(self, component, port): + # if this trace collection iterator is in stream intersection + # mode, we need this connection: + # + # port -> trimmer -> muxer + # + # otherwise, simply: + # + # port -> muxer + if self._stream_intersection_mode: + trimmer_comp = self._create_stream_intersection_trimmer(component, port) + self._graph.connect_ports(port, trimmer_comp.input_ports['in']) + port_to_muxer = trimmer_comp.output_ports['out'] + else: + port_to_muxer = port + + self._graph.connect_ports(port_to_muxer, self._get_free_muxer_input_port()) + + def _graph_port_added(self, component, port): + if not self._connect_ports: + return + + if type(port) is bt2.port._InputPort: + return + + if component not in [comp.comp for comp in self._src_comps_and_specs]: + # do not care about non-source components (muxer, trimmer, etc.) + return + + self._connect_src_comp_port(component, port) + + def _build_graph(self): + self._graph = bt2.Graph() + self._graph.add_port_added_listener(self._graph_port_added) + self._muxer_comp = self._create_muxer() + + if self._begin_ns is not None or self._end_ns is not None: + trimmer_comp = self._create_trimmer(self._begin_ns, + self._end_ns, 'trimmer') + self._graph.connect_ports(self._muxer_comp.output_ports['out'], + trimmer_comp.input_ports['in']) + msg_iter_port = trimmer_comp.output_ports['out'] + else: + msg_iter_port = self._muxer_comp.output_ports['out'] + + # create extra filter components (chained) + for comp_spec in self._flt_comp_specs: + comp = self._create_comp(comp_spec, _CompClsType.FILTER) + self._flt_comps_and_specs.append(_ComponentAndSpec(comp, comp_spec)) + + # connect the extra filter chain + for comp_and_spec in self._flt_comps_and_specs: + in_port = list(comp_and_spec.comp.input_ports.values())[0] + out_port = list(comp_and_spec.comp.output_ports.values())[0] + self._graph.connect_ports(msg_iter_port, in_port) + msg_iter_port = out_port + + # Here we create the components, self._graph_port_added() is + # called when they add ports, but the callback returns early + # because self._connect_ports is False. This is because the + # self._graph_port_added() could not find the associated source + # component specification in self._src_comps_and_specs because + # it does not exist yet (it needs the created component to + # exist). + for comp_spec in self._src_comp_specs: + comp = self._create_comp(comp_spec, _CompClsType.SOURCE) + self._src_comps_and_specs.append(_ComponentAndSpec(comp, comp_spec)) + + # Now we connect the ports which exist at this point. We allow + # self._graph_port_added() to automatically connect _new_ ports. + self._connect_ports = True + + for comp_and_spec in self._src_comps_and_specs: + # Keep a separate list because comp_and_spec.output_ports + # could change during the connection of one of its ports. + # Any new port is handled by self._graph_port_added(). + out_ports = [port for port in comp_and_spec.comp.output_ports.values()] + + for out_port in out_ports: + if out_port.is_connected: + continue + + self._connect_src_comp_port(comp_and_spec.comp, out_port) + + # create this trace collection iterator's message iterator + self._msg_iter = self._graph.create_output_port_message_iterator(msg_iter_port) diff --git a/src/bindings/python/bt2/bt2/utils.py b/src/bindings/python/bt2/bt2/utils.py new file mode 100644 index 00000000..bd8ebf8e --- /dev/null +++ b/src/bindings/python/bt2/bt2/utils.py @@ -0,0 +1,109 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import bt2 + + +def _check_bool(o): + if not isinstance(o, bool): + raise TypeError("'{}' is not a 'bool' object".format(o.__class__.__name__)) + + +def _check_int(o): + if not isinstance(o, int): + raise TypeError("'{}' is not an 'int' object".format(o.__class__.__name__)) + + +def _check_float(o): + if not isinstance(o, float): + raise TypeError("'{}' is not a 'float' object".format(o.__class__.__name__)) + + +def _check_str(o): + if not isinstance(o, str): + raise TypeError("'{}' is not a 'str' object".format(o.__class__.__name__)) + + +def _check_type(o, expected_type): + if not isinstance(o, expected_type): + raise TypeError("'{}' is not a '{}' object".format(o.__class__.__name__, + expected_type)) + + +def _is_int64(v): + _check_int(v) + return v >= -(2**63) and v <= (2**63 - 1) + + +def _is_uint64(v): + _check_int(v) + return v >= 0 and v <= (2**64 - 1) + + +def _check_int64(v, msg=None): + if not _is_int64(v): + if msg is None: + msg = 'expecting a signed 64-bit integral value' + + msg += ' (got {})'.format(v) + raise ValueError(msg) + + +def _check_uint64(v, msg=None): + if not _is_uint64(v): + if msg is None: + msg = 'expecting an unsigned 64-bit integral value' + + msg += ' (got {})'.format(v) + raise ValueError(msg) + + +def _is_m1ull(v): + return v == 18446744073709551615 + + +def _is_pow2(v): + return v != 0 and ((v & (v - 1)) == 0) + + +def _check_alignment(a): + _check_uint64(a) + + if not _is_pow2(a): + raise ValueError('{} is not a power of two'.format(a)) + + +def _raise_bt2_error(msg): + if msg is None: + raise bt2.Error + else: + raise bt2.Error(msg) + + +def _handle_ret(ret, msg=None): + if int(ret) < 0: + _raise_bt2_error(msg) + + +def _handle_ptr(ptr, msg=None): + if ptr is None: + _raise_bt2_error(msg) diff --git a/src/bindings/python/bt2/bt2/value.py b/src/bindings/python/bt2/bt2/value.py new file mode 100644 index 00000000..d8b8332e --- /dev/null +++ b/src/bindings/python/bt2/bt2/value.py @@ -0,0 +1,712 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Philippe Proulx +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +from bt2 import native_bt, object, utils +import collections.abc +import functools +import numbers +import math +import abc +import bt2 + + +def _handle_status(status, obj_name): + if status >= 0: + return + else: + raise RuntimeError('unexpected error') + + +def _create_from_ptr(ptr): + if ptr is None or ptr == native_bt.value_null: + return + + typeid = native_bt.value_get_type(ptr) + return _TYPE_TO_OBJ[typeid]._create_from_ptr(ptr) + + +def _create_from_ptr_and_get_ref(ptr): + if ptr is None or ptr == native_bt.value_null: + return + + typeid = native_bt.value_get_type(ptr) + return _TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) + + +def create_value(value): + if value is None: + # null value object + return + + if isinstance(value, _Value): + return value + + if isinstance(value, bool): + return BoolValue(value) + + if isinstance(value, int): + return SignedIntegerValue(value) + + if isinstance(value, float): + return RealValue(value) + + if isinstance(value, str): + return StringValue(value) + + try: + return MapValue(value) + except: + pass + + try: + return ArrayValue(value) + except: + pass + + raise TypeError("cannot create value object from '{}' object".format(value.__class__.__name__)) + + +class _Value(object._SharedObject, metaclass=abc.ABCMeta): + _get_ref = staticmethod(native_bt.value_get_ref) + _put_ref = staticmethod(native_bt.value_put_ref) + + def __eq__(self, other): + if other is None: + # self is never the null value object + return False + + # try type-specific comparison first + spec_eq = self._spec_eq(other) + + if spec_eq is not None: + return spec_eq + + if not isinstance(other, _Value): + # not comparing apples to apples + return False + + # fall back to native comparison function + return native_bt.value_compare(self._ptr, other._ptr) + + def __ne__(self, other): + return not (self == other) + + @abc.abstractmethod + def _spec_eq(self, other): + pass + + def _handle_status(self, status): + _handle_status(status, self._NAME) + + def _check_create_status(self, ptr): + if ptr is None: + raise bt2.CreationError( + 'cannot create {} value object'.format(self._NAME.lower())) + + +@functools.total_ordering +class _NumericValue(_Value): + @staticmethod + def _extract_value(other): + if isinstance(other, _NumericValue): + return other._value + + if other is True or other is False: + return other + + if isinstance(other, numbers.Integral): + return int(other) + + if isinstance(other, numbers.Real): + return float(other) + + if isinstance(other, numbers.Complex): + return complex(other) + + raise TypeError("'{}' object is not a number object".format(other.__class__.__name__)) + + def __int__(self): + return int(self._value) + + def __float__(self): + return float(self._value) + + def __repr__(self): + return repr(self._value) + + def __lt__(self, other): + if not isinstance(other, numbers.Number): + raise TypeError('unorderable types: {}() < {}()'.format(self.__class__.__name__, + other.__class__.__name__)) + + return self._value < float(other) + + def __le__(self, other): + if not isinstance(other, numbers.Number): + raise TypeError('unorderable types: {}() <= {}()'.format(self.__class__.__name__, + other.__class__.__name__)) + + return self._value <= float(other) + + def _spec_eq(self, other): + pass + + def __eq__(self, other): + if not isinstance(other, numbers.Number): + return False + + return self._value == complex(other) + + def __rmod__(self, other): + return self._extract_value(other) % self._value + + def __mod__(self, other): + return self._value % self._extract_value(other) + + def __rfloordiv__(self, other): + return self._extract_value(other) // self._value + + def __floordiv__(self, other): + return self._value // self._extract_value(other) + + def __round__(self, ndigits=None): + if ndigits is None: + return round(self._value) + else: + return round(self._value, ndigits) + + def __ceil__(self): + return math.ceil(self._value) + + def __floor__(self): + return math.floor(self._value) + + def __trunc__(self): + return int(self._value) + + def __abs__(self): + return abs(self._value) + + def __add__(self, other): + return self._value + self._extract_value(other) + + def __radd__(self, other): + return self.__add__(other) + + def __neg__(self): + return -self._value + + def __pos__(self): + return +self._value + + def __mul__(self, other): + return self._value * self._extract_value(other) + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + return self._value / self._extract_value(other) + + def __rtruediv__(self, other): + return self._extract_value(other) / self._value + + def __pow__(self, exponent): + return self._value ** self._extract_value(exponent) + + def __rpow__(self, base): + return self._extract_value(base) ** self._value + + def __iadd__(self, other): + self.value = self + other + return self + + def __isub__(self, other): + self.value = self - other + return self + + def __imul__(self, other): + self.value = self * other + return self + + def __itruediv__(self, other): + self.value = self / other + return self + + def __ifloordiv__(self, other): + self.value = self // other + return self + + def __imod__(self, other): + self.value = self % other + return self + + def __ipow__(self, other): + self.value = self ** other + return self + + +class _IntegralValue(_NumericValue, numbers.Integral): + def __lshift__(self, other): + return self._value << self._extract_value(other) + + def __rlshift__(self, other): + return self._extract_value(other) << self._value + + def __rshift__(self, other): + return self._value >> self._extract_value(other) + + def __rrshift__(self, other): + return self._extract_value(other) >> self._value + + def __and__(self, other): + return self._value & self._extract_value(other) + + def __rand__(self, other): + return self._extract_value(other) & self._value + + def __xor__(self, other): + return self._value ^ self._extract_value(other) + + def __rxor__(self, other): + return self._extract_value(other) ^ self._value + + def __or__(self, other): + return self._value | self._extract_value(other) + + def __ror__(self, other): + return self._extract_value(other) | self._value + + def __invert__(self): + return ~self._value + + def __ilshift__(self, other): + self.value = self << other + return self + + def __irshift__(self, other): + self.value = self >> other + return self + + def __iand__(self, other): + self.value = self & other + return self + + def __ixor__(self, other): + self.value = self ^ other + return self + + def __ior__(self, other): + self.value = self | other + return self + + +class _RealValue(_NumericValue, numbers.Real): + pass + + +class BoolValue(_Value): + _NAME = 'Boolean' + + def __init__(self, value=None): + if value is None: + ptr = native_bt.value_bool_create() + else: + ptr = native_bt.value_bool_create_init(self._value_to_bool(value)) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _spec_eq(self, other): + if isinstance(other, numbers.Number): + return self._value == bool(other) + + def __bool__(self): + return self._value + + def __repr__(self): + return repr(self._value) + + def _value_to_bool(self, value): + if isinstance(value, BoolValue): + value = value._value + + if not isinstance(value, bool): + raise TypeError("'{}' object is not a 'bool' or 'BoolValue' object".format(value.__class__)) + + return int(value) + + @property + def _value(self): + value = native_bt.value_bool_get(self._ptr) + return value != 0 + + def _set_value(self, value): + native_bt.value_bool_set(self._ptr, self._value_to_bool(value)) + + value = property(fset=_set_value) + + +class _IntegerValue(_IntegralValue): + def __init__(self, value=None): + if value is None: + ptr = self._create_default_value() + else: + ptr = self._create_value(self._value_to_int(value)) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _value_to_int(self, value): + if not isinstance(value, numbers.Real): + raise TypeError('expecting a number object') + + value = int(value) + self._check_int_range(value) + return value + + @property + def _value(self): + return self._get_value(self._ptr) + + def _prop_set_value(self, value): + self._set_value(self._ptr, self._value_to_int(value)) + + value = property(fset=_prop_set_value) + + +class UnsignedIntegerValue(_IntegerValue): + _check_int_range = staticmethod(utils._check_uint64) + _create_default_value = staticmethod(native_bt.value_unsigned_integer_create) + _create_value = staticmethod(native_bt.value_unsigned_integer_create_init) + _set_value = staticmethod(native_bt.value_unsigned_integer_set) + _get_value = staticmethod(native_bt.value_unsigned_integer_get) + + +class SignedIntegerValue(_IntegerValue): + _check_int_range = staticmethod(utils._check_int64) + _create_default_value = staticmethod(native_bt.value_signed_integer_create) + _create_value = staticmethod(native_bt.value_signed_integer_create_init) + _set_value = staticmethod(native_bt.value_signed_integer_set) + _get_value = staticmethod(native_bt.value_signed_integer_get) + + +class RealValue(_RealValue): + _NAME = 'Real number' + + def __init__(self, value=None): + if value is None: + ptr = native_bt.value_real_create() + else: + value = self._value_to_float(value) + ptr = native_bt.value_real_create_init(value) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _value_to_float(self, value): + if not isinstance(value, numbers.Real): + raise TypeError("expecting a real number object") + + return float(value) + + @property + def _value(self): + return native_bt.value_real_get(self._ptr) + + def _set_value(self, value): + native_bt.value_real_set(self._ptr, self._value_to_float(value)) + + value = property(fset=_set_value) + + +@functools.total_ordering +class StringValue(collections.abc.Sequence, _Value): + _NAME = 'String' + + def __init__(self, value=None): + if value is None: + ptr = native_bt.value_string_create() + else: + ptr = native_bt.value_string_create_init(self._value_to_str(value)) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _value_to_str(self, value): + if isinstance(value, self.__class__): + value = value._value + + utils._check_str(value) + return value + + @property + def _value(self): + return native_bt.value_string_get(self._ptr) + + def _set_value(self, value): + status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) + self._handle_status(status) + + value = property(fset=_set_value) + + def _spec_eq(self, other): + try: + return self._value == self._value_to_str(other) + except: + return + + def __le__(self, other): + return self._value <= self._value_to_str(other) + + def __lt__(self, other): + return self._value < self._value_to_str(other) + + def __bool__(self): + return bool(self._value) + + def __repr__(self): + return repr(self._value) + + def __str__(self): + return self._value + + def __getitem__(self, index): + return self._value[index] + + def __len__(self): + return len(self._value) + + def __iadd__(self, value): + curvalue = self._value + curvalue += self._value_to_str(value) + self.value = curvalue + return self + + +class _Container: + def __bool__(self): + return len(self) != 0 + + def __delitem__(self, index): + raise NotImplementedError + + +class ArrayValue(_Container, collections.abc.MutableSequence, _Value): + _NAME = 'Array' + + def __init__(self, value=None): + ptr = native_bt.value_array_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable protocol. + if value is not None: + for elem in value: + self.append(elem) + + def _spec_eq(self, other): + try: + if len(self) != len(other): + # early mismatch + return False + + for self_elem, other_elem in zip(self, other): + if self_elem != other_elem: + return False + + return True + except: + return + + def __len__(self): + size = native_bt.value_array_get_size(self._ptr) + assert(size >= 0) + return size + + def _check_index(self, index): + # TODO: support slices also + if not isinstance(index, numbers.Integral): + raise TypeError("'{}' object is not an integral number object: invalid index".format(index.__class__.__name__)) + + index = int(index) + + if index < 0 or index >= len(self): + raise IndexError('array value object index is out of range') + + def __getitem__(self, index): + self._check_index(index) + ptr = native_bt.value_array_borrow_element_by_index(self._ptr, index) + assert(ptr) + return _create_from_ptr_and_get_ref(ptr) + + def __setitem__(self, index, value): + self._check_index(index) + value = create_value(value) + + if value is None: + ptr = native_bt.value_null + else: + ptr = value._ptr + + status = native_bt.value_array_set_element_by_index( + self._ptr, index, ptr) + self._handle_status(status) + + def append(self, value): + value = create_value(value) + + if value is None: + ptr = native_bt.value_null + else: + ptr = value._ptr + + status = native_bt.value_array_append_element(self._ptr, ptr) + self._handle_status(status) + + def __iadd__(self, iterable): + # Python will raise a TypeError if there's anything wrong with + # the iterable protocol. + for elem in iterable: + self.append(elem) + + return self + + def __repr__(self): + return '[{}]'.format(', '.join([repr(v) for v in self])) + + def insert(self, value): + raise NotImplementedError + + +class _MapValueKeyIterator(collections.abc.Iterator): + def __init__(self, map_obj): + self._map_obj = map_obj + self._at = 0 + keys_ptr = native_bt.value_map_get_keys(map_obj._ptr) + + if keys_ptr is None: + raise RuntimeError('unexpected error: cannot get map value object keys') + + self._keys = _create_from_ptr(keys_ptr) + + def __next__(self): + if self._at == len(self._map_obj): + raise StopIteration + + key = self._keys[self._at] + self._at += 1 + return str(key) + + +class MapValue(_Container, collections.abc.MutableMapping, _Value): + _NAME = 'Map' + + def __init__(self, value=None): + ptr = native_bt.value_map_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable/mapping protocol. + if value is not None: + for key, elem in value.items(): + self[key] = elem + + def __eq__(self, other): + return _Value.__eq__(self, other) + + def __ne__(self, other): + return _Value.__ne__(self, other) + + def _spec_eq(self, other): + try: + if len(self) != len(other): + # early mismatch + return False + + for self_key in self: + if self_key not in other: + return False + + self_value = self[self_key] + other_value = other[self_key] + + if self_value != other_value: + return False + + return True + except: + return + + def __len__(self): + size = native_bt.value_map_get_size(self._ptr) + assert(size >= 0) + return size + + def __contains__(self, key): + self._check_key_type(key) + return native_bt.value_map_has_entry(self._ptr, key) + + def _check_key_type(self, key): + utils._check_str(key) + + def _check_key(self, key): + if key not in self: + raise KeyError(key) + + def __getitem__(self, key): + self._check_key(key) + ptr = native_bt.value_map_borrow_entry_value(self._ptr, key) + assert(ptr) + return _create_from_ptr_and_get_ref(ptr) + + def __iter__(self): + return _MapValueKeyIterator(self) + + def __setitem__(self, key, value): + self._check_key_type(key) + value = create_value(value) + + if value is None: + ptr = native_bt.value_null + else: + ptr = value._ptr + + status = native_bt.value_map_insert_entry(self._ptr, key, ptr) + self._handle_status(status) + + def __repr__(self): + items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] + return '{{{}}}'.format(', '.join(items)) + + +_TYPE_TO_OBJ = { + native_bt.VALUE_TYPE_BOOL: BoolValue, + native_bt.VALUE_TYPE_UNSIGNED_INTEGER: UnsignedIntegerValue, + native_bt.VALUE_TYPE_SIGNED_INTEGER: SignedIntegerValue, + native_bt.VALUE_TYPE_REAL: RealValue, + native_bt.VALUE_TYPE_STRING: StringValue, + native_bt.VALUE_TYPE_ARRAY: ArrayValue, + native_bt.VALUE_TYPE_MAP: MapValue, +} diff --git a/src/bindings/python/bt2/setup.py.in b/src/bindings/python/bt2/setup.py.in new file mode 100644 index 00000000..bc1a735f --- /dev/null +++ b/src/bindings/python/bt2/setup.py.in @@ -0,0 +1,86 @@ +# The MIT License (MIT) +# +# Copyright (C) 2017 - Francis Deslauriers +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys + +from distutils.core import setup, Extension + +PY_PATH_WARN_MSG = """ +-------------------------------------WARNING------------------------------------ +The install directory used:\n ({})\nis not included in your PYTHONPATH. + +To add this directory to your Python search path permanently you can add the +following command to your .bashrc/.zshrc: + export PYTHONPATH="${{PYTHONPATH}}:{}" +-------------------------------------------------------------------------------- +""" + +def main(): + babeltrace_ext = Extension('bt2._native_bt', + sources=['bt2/native_bt.i', 'bt2/logging.c'], + libraries=['babeltrace2', 'glib-2.0'], + extra_objects=['@top_builddir@/src/logging/.libs/libbabeltrace2-logging.a', + '@top_builddir@/src/common/.libs/libbabeltrace2-common.a'],) + + dist = setup(name='bt2', + version='@PACKAGE_VERSION@', + description='Babeltrace 2 Python Bindings', + packages=['bt2'], + package_dir={'bt2': 'bt2'}, + options={'build': + { + 'build_base': 'build', + 'build_lib': 'build/build_lib' + }, + 'build_ext': + { + 'build_lib': 'build/build_lib' + } + }, + url='http://diamon.org/babeltrace', + ext_modules=[babeltrace_ext], + license='MIT', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: The MIT License', + 'Programming Language :: Python :: 3' + 'Topic :: System :: Logging', + ]) + +# After the installation, we check that the install directory is included in +# the Python search path and we print a warning message when it's not. +# We need to do this because Python search path differs depending on the distro +# and some distros don't include any /usr/local/ in the search path. This is +# also useful for out-of-tree installs and tests. +# It's only relevant to make this check on the `install` command. + + if 'install' in dist.command_obj: + install_dir = dist.command_obj['install'].install_libbase + if install_dir not in sys.path: + # We can't consider this an error because if affects every + # distro differently. We only warn the user that some + # extra configuration is needed to use the bindings + print(PY_PATH_WARN_MSG.format(install_dir, install_dir)) + +if __name__ == "__main__": + main() diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am new file mode 100644 index 00000000..45297d52 --- /dev/null +++ b/src/cli/Makefile.am @@ -0,0 +1,100 @@ +PLUGINS_PATH = $(abs_top_builddir)/src/plugins +LTTNG_UTILS_PLUGIN_PATH = + +if ENABLE_DEBUG_INFO +LTTNG_UTILS_PLUGIN_PATH += :$(PLUGINS_PATH)/lttng-utils +endif + +if BABELTRACE_BUILD_WITH_MINGW +IN_TREE_PLUGIN_PATH := $(shell cygpath -pm "$(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/utils$(LTTNG_UTILS_PLUGIN_PATH)") +else +IN_TREE_PLUGIN_PATH = $(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/utils$(LTTNG_UTILS_PLUGIN_PATH) +endif + +AM_CPPFLAGS += '-DCONFIG_IN_TREE_PLUGIN_PATH="$(IN_TREE_PLUGIN_PATH)"' + +bin_PROGRAMS = babeltrace2.bin babeltrace2-log.bin +noinst_PROGRAMS = babeltrace2 babeltrace2-log + +babeltrace2_bin_SOURCES = \ + babeltrace2.c \ + babeltrace2-cfg.c \ + babeltrace2-cfg.h \ + babeltrace2-cfg-cli-args.c \ + babeltrace2-cfg-cli-args.h \ + babeltrace2-cfg-cli-args-connect.c \ + babeltrace2-cfg-cli-args-connect.h \ + babeltrace2-cfg-cli-args-default.h \ + babeltrace2-cfg-cli-args-default.c \ + logging.c logging.h + +# -Wl,--no-as-needed is needed for recent gold linker who seems to think +# it knows better and considers libraries with constructors having +# side-effects as dead code. +babeltrace2_bin_LDFLAGS = $(LD_NO_AS_NEEDED) + +# Add all the convenience libraries used by Babeltrace plugins and the +# library. They will be used when embedding plugins (--enable-built-in-plugins), +# otherwise we're looking after multiple definitions of the same symbols if +# a plugin's archive (.a) includes the convenience library because +# we're using --whole-archive below (needed to make sure the linker does +# not discard the plugins since the CLI does not use their symbols +# directly). +babeltrace2_bin_LDADD = \ + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/compat/libcompat.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(POPT_LIBS) + +if ENABLE_BUILT_IN_PLUGINS +# Takes a plugin name and outputs the needed LDFLAGS to embed it. +# +# The --whole-archive option is important here. From the GNU linker's +# documentation: +# +# For each archive mentioned on the command line after the +# --whole-archive option, include every object file in the archive in +# the link, rather than searching the archive for the required object +# files. +# +# In our case, we find the plugins thanks to special sections in the +# binary that are filled by plugin objects. If the linker discards those +# symbols because the CLI does not use them directly, the CLI reports +# no plugins found (plugins are effectively not embedded). +pluginarchive = $(LD_WHOLE_ARCHIVE)$(PLUGINS_PATH)/$(1)/.libs/babeltrace-plugin-$(1).a$(LD_NO_WHOLE_ARCHIVE) + +# Built-in plugins +babeltrace2_bin_LDFLAGS += $(call pluginarchive,ctf) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,text) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,utils) + +if ENABLE_DEBUG_INFO +babeltrace2_bin_LDFLAGS += $(call pluginarchive,lttng-utils) +babeltrace2_bin_LDADD += $(ELFUTILS_LIBS) +endif +endif + +if BABELTRACE_BUILD_WITH_MINGW +babeltrace2_bin_LDADD += -lws2_32 -lrpcrt4 -lintl -liconv -lole32 -lpthread +endif + +# Only used for in-tree execution and tests +babeltrace2_SOURCES = $(babeltrace2_bin_SOURCES) +babeltrace2_LDFLAGS = $(babeltrace2_bin_LDFLAGS) +babeltrace2_LDADD = $(babeltrace2_bin_LDADD) +babeltrace2_CFLAGS = $(AM_CFLAGS) -DBT_SET_DEFAULT_IN_TREE_CONFIGURATION + +# babeltrace2-log rules and config below +babeltrace2_log_bin_SOURCES = babeltrace2-log.c +babeltrace2_log_bin_LDADD = \ + $(top_builddir)/src/compat/libcompat.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(POPT_LIBS) +babeltrace2_log_bin_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(abs_top_builddir)/src/cli/babeltrace2$(EXEEXT)"' + +# Only used for in-tree execution and tests +babeltrace2_log_SOURCES = $(babeltrace2_log_bin_SOURCES) +babeltrace2_log_LDADD = $(babeltrace2_log_bin_LDADD) +babeltrace2_log_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(bindir)/babeltrace2$(EXEEXT)"' diff --git a/src/cli/babeltrace2-cfg-cli-args-connect.c b/src/cli/babeltrace2-cfg-cli-args-connect.c new file mode 100644 index 00000000..7e1657e0 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args-connect.c @@ -0,0 +1,726 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/common.h" +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args-connect.h" + +static bool all_named_and_printable_in_array(GPtrArray *comps) +{ + size_t i; + bool all_named_and_printable = true; + + for (i = 0; i < comps->len; i++) { + struct bt_config_component *comp = g_ptr_array_index(comps, i); + + if (comp->instance_name->len == 0) { + all_named_and_printable = false; + goto end; + } + + if (!bt_common_string_is_printable(comp->instance_name->str)) { + all_named_and_printable = false; + goto end; + } + } + +end: + return all_named_and_printable; +} + +static bool all_named_and_printable(struct bt_config *cfg) +{ + return all_named_and_printable_in_array(cfg->cmd_data.run.sources) && + all_named_and_printable_in_array(cfg->cmd_data.run.filters) && + all_named_and_printable_in_array(cfg->cmd_data.run.sinks); +} + +static struct bt_config_connection *bt_config_connection_create(const char *arg) +{ + struct bt_config_connection *cfg_connection; + + cfg_connection = g_new0(struct bt_config_connection, 1); + if (!cfg_connection) { + goto error; + } + + cfg_connection->upstream_comp_name = g_string_new(NULL); + if (!cfg_connection->upstream_comp_name) { + goto error; + } + + cfg_connection->downstream_comp_name = g_string_new(NULL); + if (!cfg_connection->downstream_comp_name) { + goto error; + } + + cfg_connection->upstream_port_glob = g_string_new("*"); + if (!cfg_connection->upstream_port_glob) { + goto error; + } + + cfg_connection->downstream_port_glob = g_string_new("*"); + if (!cfg_connection->downstream_port_glob) { + goto error; + } + + cfg_connection->arg = g_string_new(arg); + if (!cfg_connection->arg) { + goto error; + } + + goto end; + +error: + g_free(cfg_connection); + cfg_connection = NULL; + +end: + return cfg_connection; +} + +static bool validate_port_glob(const char *port_glob) +{ + bool is_valid = true; + const char *ch = port_glob; + + BT_ASSERT(port_glob); + + while (*ch != '\0') { + switch (*ch) { + case '\\': + switch (ch[1]) { + case '\0': + goto end; + default: + ch += 2; + continue; + } + case '?': + case '[': + /* + * This is reserved for future use, to support + * full globbing patterns. Those characters must + * be escaped with `\`. + */ + is_valid = false; + goto end; + default: + ch++; + break; + } + } + +end: + return is_valid; +} + +static int normalize_glob_pattern(GString *glob_pattern_gs) +{ + int ret = 0; + char *glob_pattern = strdup(glob_pattern_gs->str); + + if (!glob_pattern) { + ret = -1; + goto end; + } + + bt_common_normalize_star_glob_pattern(glob_pattern); + g_string_assign(glob_pattern_gs, glob_pattern); + free(glob_pattern); + +end: + return ret; +} + +static struct bt_config_connection *cfg_connection_from_arg(const char *arg) +{ + const char *at = arg; + size_t end_pos; + struct bt_config_connection *cfg_conn = NULL; + GString *gs = NULL; + enum { + UPSTREAM_NAME, + DOWNSTREAM_NAME, + UPSTREAM_PORT_GLOB, + DOWNSTREAM_PORT_GLOB, + } state = UPSTREAM_NAME; + + if (!bt_common_string_is_printable(arg)) { + goto error; + } + + cfg_conn = bt_config_connection_create(arg); + if (!cfg_conn) { + goto error; + } + + while (true) { + switch (state) { + case UPSTREAM_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->upstream_comp_name, TRUE); + cfg_conn->upstream_comp_name = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DOWNSTREAM_NAME; + } else if (at[end_pos] == '.') { + state = UPSTREAM_PORT_GLOB; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DOWNSTREAM_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->downstream_comp_name, TRUE); + cfg_conn->downstream_comp_name = gs; + gs = NULL; + + if (at[end_pos] == '.') { + state = DOWNSTREAM_PORT_GLOB; + } else if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + + at += end_pos + 1; + break; + case UPSTREAM_PORT_GLOB: + gs = bt_common_string_until(at, ".:", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + if (!validate_port_glob(gs->str)) { + goto error; + } + + if (normalize_glob_pattern(gs)) { + goto error; + } + + g_string_free(cfg_conn->upstream_port_glob, TRUE); + cfg_conn->upstream_port_glob = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DOWNSTREAM_NAME; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DOWNSTREAM_PORT_GLOB: + gs = bt_common_string_until(at, ".:", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + if (!validate_port_glob(gs->str)) { + goto error; + } + + if (normalize_glob_pattern(gs)) { + goto error; + } + + g_string_free(cfg_conn->downstream_port_glob, TRUE); + cfg_conn->downstream_port_glob = gs; + gs = NULL; + + if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + break; + default: + abort(); + } + } + +error: + bt_config_connection_destroy(cfg_conn); + cfg_conn = NULL; + +end: + if (gs) { + g_string_free(gs, TRUE); + } + + return cfg_conn; +} + +static struct bt_config_component *find_component_in_array(GPtrArray *comps, + const char *name) +{ + size_t i; + struct bt_config_component *found_comp = NULL; + + for (i = 0; i < comps->len; i++) { + struct bt_config_component *comp = g_ptr_array_index(comps, i); + + if (strcmp(name, comp->instance_name->str) == 0) { + found_comp = comp; + bt_object_get_ref(found_comp); + goto end; + } + } + +end: + return found_comp; +} + +static struct bt_config_component *find_component(struct bt_config *cfg, + const char *name) +{ + struct bt_config_component *comp; + + comp = find_component_in_array(cfg->cmd_data.run.sources, name); + if (comp) { + goto end; + } + + comp = find_component_in_array(cfg->cmd_data.run.filters, name); + if (comp) { + goto end; + } + + comp = find_component_in_array(cfg->cmd_data.run.sinks, name); + if (comp) { + goto end; + } + +end: + return comp; +} + +static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf, + size_t error_buf_size) +{ + size_t i; + int ret = 0; + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + struct bt_config_component *comp; + + comp = find_component(cfg, connection->upstream_comp_name->str); + bt_object_put_ref(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find upstream component `%s`:\n %s\n", + connection->upstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + comp = find_component(cfg, connection->downstream_comp_name->str); + bt_object_put_ref(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find downstream component `%s`:\n %s\n", + connection->downstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +static int validate_connection_directions(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + struct bt_config_component *src_comp = NULL; + struct bt_config_component *dst_comp = NULL; + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + src_comp = find_component(cfg, + connection->upstream_comp_name->str); + BT_ASSERT(src_comp); + dst_comp = find_component(cfg, + connection->downstream_comp_name->str); + BT_ASSERT(dst_comp); + + if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { + if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && + dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) { + snprintf(error_buf, error_buf_size, + "Invalid connection: source component `%s` not connected to filter or sink component:\n %s\n", + connection->upstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) { + if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && + dst_comp->type != BT_COMPONENT_CLASS_TYPE_SINK) { + snprintf(error_buf, error_buf_size, + "Invalid connection: filter component `%s` not connected to filter or sink component:\n %s\n", + connection->upstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } else { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n", + connection->upstream_comp_name->str, + connection->downstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + BT_OBJECT_PUT_REF_AND_RESET(src_comp); + BT_OBJECT_PUT_REF_AND_RESET(dst_comp); + } + +end: + bt_object_put_ref(src_comp); + bt_object_put_ref(dst_comp); + return ret; +} + +static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path, + char *error_buf, size_t error_buf_size) +{ + int ret = 0; + size_t conn_i; + const char *src_comp_name; + + BT_ASSERT(path && path->len > 0); + src_comp_name = g_ptr_array_index(path, path->len - 1); + + for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) { + struct bt_config_connection *conn = + g_ptr_array_index(cfg->cmd_data.run.connections, conn_i); + + if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) { + size_t path_i; + + for (path_i = 0; path_i < path->len; path_i++) { + const char *comp_name = + g_ptr_array_index(path, path_i); + + if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) { + snprintf(error_buf, error_buf_size, + "Invalid connection: connection forms a cycle:\n %s\n", + conn->arg->str); + ret = -1; + goto end; + } + } + + g_ptr_array_add(path, conn->downstream_comp_name->str); + ret = validate_no_cycles_rec(cfg, path, error_buf, + error_buf_size); + if (ret) { + goto end; + } + + g_ptr_array_remove_index(path, path->len - 1); + } + } + +end: + return ret; +} + +static int validate_no_cycles(struct bt_config *cfg, char *error_buf, + size_t error_buf_size) +{ + size_t i; + int ret = 0; + GPtrArray *path; + + path = g_ptr_array_new(); + if (!path) { + ret = -1; + goto end; + } + + g_ptr_array_add(path, NULL); + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *conn = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + g_ptr_array_index(path, 0) = conn->upstream_comp_name->str; + ret = validate_no_cycles_rec(cfg, path, + error_buf, error_buf_size); + if (ret) { + goto end; + } + } + +end: + if (path) { + g_ptr_array_free(path, TRUE); + } + + return ret; +} + +static int validate_all_components_connected_in_array(GPtrArray *comps, + const bt_value *connected_components, + char *error_buf, size_t error_buf_size) +{ + int ret = 0; + size_t i; + + for (i = 0; i < comps->len; i++) { + struct bt_config_component *comp = g_ptr_array_index(comps, i); + + if (!bt_value_map_has_entry(connected_components, + comp->instance_name->str)) { + snprintf(error_buf, error_buf_size, + "Component `%s` is not connected\n", + comp->instance_name->str); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +static int validate_all_components_connected(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + bt_value *connected_components = bt_value_map_create(); + + if (!connected_components) { + ret = -1; + goto end; + } + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + ret = bt_value_map_insert_entry(connected_components, + connection->upstream_comp_name->str, bt_value_null); + if (ret) { + goto end; + } + + ret = bt_value_map_insert_entry(connected_components, + connection->downstream_comp_name->str, bt_value_null); + if (ret) { + goto end; + } + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.sources, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.filters, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.sinks, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + +end: + bt_value_put_ref(connected_components); + return ret; +} + +static int validate_no_duplicate_connection(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + bt_value *flat_connection_names = + bt_value_map_create(); + GString *flat_connection_name = NULL; + + if (!flat_connection_names) { + ret = -1; + goto end; + } + + flat_connection_name = g_string_new(NULL); + if (!flat_connection_name) { + ret = -1; + goto end; + } + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s", + connection->upstream_comp_name->str, + connection->upstream_port_glob->str, + connection->downstream_comp_name->str, + connection->downstream_port_glob->str); + + if (bt_value_map_has_entry(flat_connection_names, + flat_connection_name->str)) { + snprintf(error_buf, error_buf_size, + "Duplicate connection:\n %s\n", + connection->arg->str); + ret = -1; + goto end; + } + + ret = bt_value_map_insert_entry(flat_connection_names, + flat_connection_name->str, bt_value_null); + if (ret) { + goto end; + } + } + +end: + bt_value_put_ref(flat_connection_names); + + if (flat_connection_name) { + g_string_free(flat_connection_name, TRUE); + } + + return ret; +} + +static int validate_connections(struct bt_config *cfg, char *error_buf, + size_t error_buf_size) +{ + int ret; + + ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_connection_directions(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_all_components_connected(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_no_cycles(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + +end: + return ret; +} + +int bt_config_cli_args_create_connections(struct bt_config *cfg, + const bt_value *connection_args, + char *error_buf, size_t error_buf_size) +{ + int ret; + size_t i; + + if (!all_named_and_printable(cfg)) { + snprintf(error_buf, error_buf_size, + "One or more components are unnamed (use --name) or contain a non-printable character\n"); + goto error; + } + + for (i = 0; i < bt_value_array_get_size(connection_args); i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index_const( + connection_args, i); + const char *arg; + struct bt_config_connection *cfg_connection; + + arg = bt_value_string_get(arg_value); + cfg_connection = cfg_connection_from_arg(arg); + if (!cfg_connection) { + snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n", + arg); + goto error; + } + + g_ptr_array_add(cfg->cmd_data.run.connections, + cfg_connection); + } + + + ret = validate_connections(cfg, error_buf, error_buf_size); + if (ret) { + goto error; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} diff --git a/src/cli/babeltrace2-cfg-cli-args-connect.h b/src/cli/babeltrace2-cfg-cli-args-connect.h new file mode 100644 index 00000000..37799bb8 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args-connect.h @@ -0,0 +1,36 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "babeltrace2-cfg.h" + +int bt_config_cli_args_create_connections(struct bt_config *cfg, + const bt_value *connection_args, + char *error_buf, size_t error_buf_size); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H */ diff --git a/src/cli/babeltrace2-cfg-cli-args-default.c b/src/cli/babeltrace2-cfg-cli-args-default.c new file mode 100644 index 00000000..aef23a53 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args-default.c @@ -0,0 +1,77 @@ +/* + * Copyright 2016 - Jérémie Galarneau + * Copyright 2017 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-default.h" + +#ifdef ENABLE_DEBUG_INFO +# define BT_ENABLE_DEBUG_INFO 1 +#else +# define BT_ENABLE_DEBUG_INFO 0 +#endif + +#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode) +{ + bt_value *initial_plugin_paths; + struct bt_config *cfg = NULL; + int ret; + + initial_plugin_paths = bt_value_array_create(); + if (!initial_plugin_paths) { + goto error; + } + + ret = bt_config_append_plugin_paths(initial_plugin_paths, + CONFIG_IN_TREE_PLUGIN_PATH); + if (ret) { + goto error; + } + + cfg = bt_config_cli_args_create(argc, argv, retcode, true, true, + initial_plugin_paths); + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + bt_value_put_ref(initial_plugin_paths); + return cfg; +} + +#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode) +{ + return bt_config_cli_args_create(argc, argv, retcode, false, false, + NULL); +} + +#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/src/cli/babeltrace2-cfg-cli-args-default.h b/src/cli/babeltrace2-cfg-cli-args-default.h new file mode 100644 index 00000000..839b08c1 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args-default.h @@ -0,0 +1,33 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H + +/* + * Babeltrace Trace Converter - Default Configuration + * + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "babeltrace2-cfg.h" + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H */ diff --git a/src/cli/babeltrace2-cfg-cli-args.c b/src/cli/babeltrace2-cfg-cli-args.c new file mode 100644 index 00000000..e1470d72 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args.c @@ -0,0 +1,5107 @@ +/* + * Babeltrace trace converter - parameter parsing + * + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CLI-CFG-CLI-ARGS" +#include "logging.h" + +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include +#include "common/common.h" +#include +#include +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-connect.h" +#include "common/version.h" + +/* + * Error printf() macro which prepends "Error: " the first time it's + * called. This gives a nicer feel than having a bunch of error prefixes + * (since the following lines usually describe the error and possible + * solutions), or the error prefix just at the end. + */ +#define printf_err(fmt, args...) \ + do { \ + if (is_first_error) { \ + fprintf(stderr, "Command line error: "); \ + is_first_error = false; \ + } \ + fprintf(stderr, fmt, ##args); \ + } while (0) + +static bool is_first_error = true; + +/* INI-style parsing FSM states */ +enum ini_parsing_fsm_state { + /* Expect a map key (identifier) */ + INI_EXPECT_MAP_KEY, + + /* Expect an equal character ('=') */ + INI_EXPECT_EQUAL, + + /* Expect a value */ + INI_EXPECT_VALUE, + + /* Expect a comma character (',') */ + INI_EXPECT_COMMA, +}; + +/* INI-style parsing state variables */ +struct ini_parsing_state { + /* Lexical scanner (owned by this) */ + GScanner *scanner; + + /* Output map value object being filled (owned by this) */ + bt_value *params; + + /* Next expected FSM state */ + enum ini_parsing_fsm_state expecting; + + /* Last decoded map key (owned by this) */ + char *last_map_key; + + /* Complete INI-style string to parse (not owned by this) */ + const char *arg; + + /* Error buffer (not owned by this) */ + GString *ini_error; +}; + +/* Offset option with "is set" boolean */ +struct offset_opt { + int64_t value; + bool is_set; +}; + +/* Legacy "ctf"/"lttng-live" format options */ +struct ctf_legacy_opts { + struct offset_opt offset_s; + struct offset_opt offset_ns; + bool stream_intersection; +}; + +/* Legacy "text" format options */ +struct text_legacy_opts { + /* + * output, dbg_info_dir, dbg_info_target_prefix, names, + * and fields are owned by this. + */ + GString *output; + GString *dbg_info_dir; + GString *dbg_info_target_prefix; + const bt_value *names; + const bt_value *fields; + + /* Flags */ + bool no_delta; + bool clock_cycles; + bool clock_seconds; + bool clock_date; + bool clock_gmt; + bool dbg_info_full_path; + bool verbose; +}; + +/* Legacy input format format */ +enum legacy_input_format { + LEGACY_INPUT_FORMAT_NONE = 0, + LEGACY_INPUT_FORMAT_CTF, + LEGACY_INPUT_FORMAT_LTTNG_LIVE, +}; + +/* Legacy output format format */ +enum legacy_output_format { + LEGACY_OUTPUT_FORMAT_NONE = 0, + LEGACY_OUTPUT_FORMAT_TEXT, + LEGACY_OUTPUT_FORMAT_DUMMY, +}; + +/* + * Prints the "out of memory" error. + */ +static +void print_err_oom(void) +{ + printf_err("Out of memory\n"); +} + +/* + * Appends an "expecting token" error to the INI-style parsing state's + * error buffer. + */ +static +void ini_append_error_expecting(struct ini_parsing_state *state, + GScanner *scanner, const char *expecting) +{ + size_t i; + size_t pos; + + g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting); + + /* Only print error if there's one line */ + if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) { + return; + } + + g_string_append_printf(state->ini_error, "\n %s\n", state->arg); + pos = g_scanner_cur_position(scanner) + 4; + + if (!g_scanner_eof(scanner)) { + pos--; + } + + for (i = 0; i < pos; ++i) { + g_string_append_printf(state->ini_error, " "); + } + + g_string_append_printf(state->ini_error, "^\n\n"); +} + +/* Parse the next token as an unsigned integer. */ +static +bt_value *ini_parse_uint(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = g_scanner_get_next_token(state->scanner); + + if (token_type != G_TOKEN_INT) { + ini_append_error_expecting(state, state->scanner, + "integer value"); + goto end; + } + + value = bt_value_unsigned_integer_create_init( + state->scanner->value.v_int64); + +end: + return value; +} + +/* Parse the next token as a number and return its negation. */ +static +bt_value *ini_parse_neg_number(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = g_scanner_get_next_token(state->scanner); + + switch (token_type) { + case G_TOKEN_INT: + { + /* Negative integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > (((uint64_t) INT64_MAX) + 1)) { + g_string_append_printf(state->ini_error, + "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + } else { + value = bt_value_signed_integer_create_init( + -((int64_t) int_val)); + } + + break; + } + case G_TOKEN_FLOAT: + /* Negative floating point number */ + value = bt_value_real_create_init(-state->scanner->value.v_float); + break; + default: + ini_append_error_expecting(state, state->scanner, "value"); + break; + } + + return value; +} + +static bt_value *ini_parse_value(struct ini_parsing_state *state); + +/* + * Parse the current and following tokens as an array. Arrays are formatted as + * an opening `[`, a list of comma-separated values and a closing `]`. For + * convenience we support an optional trailing comma, after the last value. + * + * The current token of the parser must be the opening square bracket of the + * array. + */ +static +bt_value *ini_parse_array(struct ini_parsing_state *state) +{ + /* The [ character must have already been ingested. */ + BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR); + BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '['); + + bt_value *array_value; + GTokenType token_type; + + array_value = bt_value_array_create (); + token_type = g_scanner_get_next_token(state->scanner); + + /* While the current token is not a ]... */ + while (!(token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ']')) { + /* Parse the item... */ + bt_value *item_value; + bt_value_status status; + + item_value = ini_parse_value(state); + if (!item_value) { + goto error; + } + + /* ... and add it to the result array. */ + status = bt_value_array_append_element(array_value, item_value); + BT_VALUE_PUT_REF_AND_RESET(item_value); + + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + + /* + * Ingest the token following the value, it should be either a + * comma or closing square brace. + */ + token_type = g_scanner_get_next_token(state->scanner); + + if (token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ',') { + /* + * Ingest the token following the comma. If it happens + * to be a closing square bracket, we'll exit the loop + * and we are done (we allow trailing commas). + * Otherwise, we are ready for the next ini_parse_value call. + */ + token_type = g_scanner_get_next_token(state->scanner); + } else if (token_type != G_TOKEN_CHAR || g_scanner_cur_value(state->scanner).v_char != ']') { + ini_append_error_expecting(state, state->scanner, ", or ]"); + goto error; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(array_value); + +end: + return array_value; +} + +/* + * Parse the current token (and the following ones if needed) as a value, return + * it as a bt_value. + */ +static +bt_value *ini_parse_value(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = state->scanner->token; + + switch (token_type) { + case G_TOKEN_CHAR: + if (state->scanner->value.v_char == '-') { + /* Negative number */ + value = ini_parse_neg_number(state); + } else if (state->scanner->value.v_char == '+') { + /* Unsigned integer */ + value = ini_parse_uint(state); + } else if (state->scanner->value.v_char == '[') { + /* Array */ + value = ini_parse_array(state); + } else { + ini_append_error_expecting(state, state->scanner, "value"); + } + break; + case G_TOKEN_INT: + { + /* Positive, signed integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > INT64_MAX) { + g_string_append_printf(state->ini_error, + "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + } else { + value = bt_value_signed_integer_create_init( + (int64_t) int_val); + } + break; + } + case G_TOKEN_FLOAT: + /* Positive floating point number */ + value = bt_value_real_create_init(state->scanner->value.v_float); + break; + case G_TOKEN_STRING: + /* Quoted string */ + value = bt_value_string_create_init(state->scanner->value.v_string); + break; + case G_TOKEN_IDENTIFIER: + { + /* + * Using symbols would be appropriate here, + * but said symbols are allowed as map key, + * so it's easier to consider everything an + * identifier. + * + * If one of the known symbols is not + * recognized here, then fall back to creating + * a string value. + */ + const char *id = state->scanner->value.v_identifier; + + if (!strcmp(id, "null") || !strcmp(id, "NULL") || + !strcmp(id, "nul")) { + value = bt_value_null; + } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") || + !strcmp(id, "yes") || + !strcmp(id, "YES")) { + value = bt_value_bool_create_init(true); + } else if (!strcmp(id, "false") || + !strcmp(id, "FALSE") || + !strcmp(id, "no") || + !strcmp(id, "NO")) { + value = bt_value_bool_create_init(false); + } else { + value = bt_value_string_create_init(id); + } + break; + } + default: + /* Unset value variable will trigger the error */ + ini_append_error_expecting(state, state->scanner, "value"); + break; + } + + return value; +} + +static +int ini_handle_state(struct ini_parsing_state *state) +{ + int ret = 0; + GTokenType token_type; + bt_value *value = NULL; + + token_type = g_scanner_get_next_token(state->scanner); + if (token_type == G_TOKEN_EOF) { + if (state->expecting != INI_EXPECT_COMMA) { + switch (state->expecting) { + case INI_EXPECT_EQUAL: + ini_append_error_expecting(state, + state->scanner, "'='"); + break; + case INI_EXPECT_VALUE: + ini_append_error_expecting(state, + state->scanner, "value"); + break; + case INI_EXPECT_MAP_KEY: + ini_append_error_expecting(state, + state->scanner, "unquoted map key"); + break; + default: + break; + } + goto error; + } + + /* We're done! */ + ret = 1; + goto success; + } + + switch (state->expecting) { + case INI_EXPECT_MAP_KEY: + if (token_type != G_TOKEN_IDENTIFIER) { + ini_append_error_expecting(state, state->scanner, + "unquoted map key"); + goto error; + } + + free(state->last_map_key); + state->last_map_key = + strdup(state->scanner->value.v_identifier); + if (!state->last_map_key) { + g_string_append(state->ini_error, + "Out of memory\n"); + goto error; + } + + if (bt_value_map_has_entry(state->params, + state->last_map_key)) { + g_string_append_printf(state->ini_error, + "Duplicate parameter key: `%s`\n", + state->last_map_key); + goto error; + } + + state->expecting = INI_EXPECT_EQUAL; + goto success; + case INI_EXPECT_EQUAL: + if (token_type != G_TOKEN_CHAR) { + ini_append_error_expecting(state, + state->scanner, "'='"); + goto error; + } + + if (state->scanner->value.v_char != '=') { + ini_append_error_expecting(state, + state->scanner, "'='"); + goto error; + } + + state->expecting = INI_EXPECT_VALUE; + goto success; + case INI_EXPECT_VALUE: + { + value = ini_parse_value(state); + if (!value) { + goto error; + } + + state->expecting = INI_EXPECT_COMMA; + goto success; + } + case INI_EXPECT_COMMA: + if (token_type != G_TOKEN_CHAR) { + ini_append_error_expecting(state, + state->scanner, "','"); + goto error; + } + + if (state->scanner->value.v_char != ',') { + ini_append_error_expecting(state, + state->scanner, "','"); + goto error; + } + + state->expecting = INI_EXPECT_MAP_KEY; + goto success; + default: + abort(); + } + +error: + ret = -1; + goto end; + +success: + if (value) { + if (bt_value_map_insert_entry(state->params, + state->last_map_key, value)) { + /* Only override return value on error */ + ret = -1; + } + } + +end: + BT_VALUE_PUT_REF_AND_RESET(value); + return ret; +} + +/* + * Converts an INI-style argument to an equivalent map value object. + * + * Return value is owned by the caller. + */ +static +bt_value *bt_value_from_ini(const char *arg, + GString *ini_error) +{ + /* Lexical scanner configuration */ + GScannerConfig scanner_config = { + /* Skip whitespaces */ + .cset_skip_characters = " \t\n", + + /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */ + .cset_identifier_first = + G_CSET_a_2_z + "_" + G_CSET_A_2_Z, + .cset_identifier_nth = + G_CSET_a_2_z + "_0123456789-.:" + G_CSET_A_2_Z, + + /* "hello" and "Hello" two different keys */ + .case_sensitive = TRUE, + + /* No comments */ + .cpair_comment_single = NULL, + .skip_comment_multi = TRUE, + .skip_comment_single = TRUE, + .scan_comment_multi = FALSE, + + /* + * Do scan identifiers, including 1-char identifiers, + * but NULL is a normal identifier. + */ + .scan_identifier = TRUE, + .scan_identifier_1char = TRUE, + .scan_identifier_NULL = FALSE, + + /* + * No specific symbols: null and boolean "symbols" are + * scanned as plain identifiers. + */ + .scan_symbols = FALSE, + .symbol_2_token = FALSE, + .scope_0_fallback = FALSE, + + /* + * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not + * integers prefixed with "$". + */ + .scan_binary = TRUE, + .scan_octal = TRUE, + .scan_float = TRUE, + .scan_hex = TRUE, + .scan_hex_dollar = FALSE, + + /* Convert scanned numbers to integer tokens */ + .numbers_2_int = TRUE, + + /* Support both integers and floating-point numbers */ + .int_2_float = FALSE, + + /* Scan integers as 64-bit signed integers */ + .store_int64 = TRUE, + + /* Only scan double-quoted strings */ + .scan_string_sq = FALSE, + .scan_string_dq = TRUE, + + /* Do not converter identifiers to string tokens */ + .identifier_2_string = FALSE, + + /* Scan characters as G_TOKEN_CHAR token */ + .char_2_token = FALSE, + }; + struct ini_parsing_state state = { + .scanner = NULL, + .params = NULL, + .expecting = INI_EXPECT_MAP_KEY, + .arg = arg, + .ini_error = ini_error, + }; + + state.params = bt_value_map_create(); + if (!state.params) { + goto error; + } + + state.scanner = g_scanner_new(&scanner_config); + if (!state.scanner) { + goto error; + } + + /* Let the scan begin */ + g_scanner_input_text(state.scanner, arg, strlen(arg)); + + while (true) { + int ret = ini_handle_state(&state); + + if (ret < 0) { + /* Error */ + goto error; + } else if (ret > 0) { + /* Done */ + break; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(state.params); + +end: + if (state.scanner) { + g_scanner_destroy(state.scanner); + } + + free(state.last_map_key); + return state.params; +} + +/* + * Returns the parameters map value object from a command-line + * parameter option's argument. + * + * Return value is owned by the caller. + */ +static +bt_value *bt_value_from_arg(const char *arg) +{ + bt_value *params = NULL; + GString *ini_error = NULL; + + ini_error = g_string_new(NULL); + if (!ini_error) { + print_err_oom(); + goto end; + } + + /* Try INI-style parsing */ + params = bt_value_from_ini(arg, ini_error); + if (!params) { + printf_err("%s", ini_error->str); + goto end; + } + +end: + if (ini_error) { + g_string_free(ini_error, TRUE); + } + + return params; +} + +/* + * Returns the plugin name, component class name, component class type, + * and component name from a command-line --component option's argument. + * arg must have the following format: + * + * [NAME:]TYPE.PLUGIN.CLS + * + * where NAME is the optional component name, TYPE is either `source`, + * `filter`, or `sink`, PLUGIN is the plugin name, and CLS is the + * component class name. + * + * On success, both *plugin and *component are not NULL. *plugin + * and *comp_cls are owned by the caller. On success, *name can be NULL + * if no component class name was found, and *comp_cls_type is set. + */ +static +void plugin_comp_cls_names(const char *arg, char **name, char **plugin, + char **comp_cls, bt_component_class_type *comp_cls_type) +{ + const char *at = arg; + GString *gs_name = NULL; + GString *gs_comp_cls_type = NULL; + GString *gs_plugin = NULL; + GString *gs_comp_cls = NULL; + size_t end_pos; + + BT_ASSERT(arg); + BT_ASSERT(plugin); + BT_ASSERT(comp_cls); + BT_ASSERT(comp_cls_type); + + if (!bt_common_string_is_printable(arg)) { + printf_err("Argument contains a non-printable character\n"); + goto error; + } + + /* Parse the component name */ + gs_name = bt_common_string_until(at, ".:\\", ":", &end_pos); + if (!gs_name) { + goto error; + } + + if (arg[end_pos] == ':') { + at += end_pos + 1; + } else { + /* No name */ + g_string_assign(gs_name, ""); + } + + /* Parse the component class type */ + gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_comp_cls_type || at[end_pos] == '\0') { + printf_err("Missing component class type (`source`, `filter`, or `sink`)\n"); + goto error; + } + + if (strcmp(gs_comp_cls_type->str, "source") == 0 || + strcmp(gs_comp_cls_type->str, "src") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SOURCE; + } else if (strcmp(gs_comp_cls_type->str, "filter") == 0 || + strcmp(gs_comp_cls_type->str, "flt") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_FILTER; + } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK; + } else { + printf_err("Unknown component class type: `%s`\n", + gs_comp_cls_type->str); + goto error; + } + + at += end_pos + 1; + + /* Parse the plugin name */ + gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { + printf_err("Missing plugin or component class name\n"); + goto error; + } + + at += end_pos + 1; + + /* Parse the component class name */ + gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_comp_cls || gs_comp_cls->len == 0) { + printf_err("Missing component class name\n"); + goto error; + } + + if (at[end_pos] != '\0') { + /* Found a non-escaped `.` */ + goto error; + } + + if (name) { + if (gs_name->len == 0) { + *name = NULL; + g_string_free(gs_name, TRUE); + } else { + *name = gs_name->str; + g_string_free(gs_name, FALSE); + } + } else { + g_string_free(gs_name, TRUE); + } + + *plugin = gs_plugin->str; + *comp_cls = gs_comp_cls->str; + g_string_free(gs_plugin, FALSE); + g_string_free(gs_comp_cls, FALSE); + gs_name = NULL; + gs_plugin = NULL; + gs_comp_cls = NULL; + goto end; + +error: + if (name) { + *name = NULL; + } + + *plugin = NULL; + *comp_cls = NULL; + +end: + if (gs_name) { + g_string_free(gs_name, TRUE); + } + + if (gs_plugin) { + g_string_free(gs_plugin, TRUE); + } + + if (gs_comp_cls) { + g_string_free(gs_comp_cls, TRUE); + } + + if (gs_comp_cls_type) { + g_string_free(gs_comp_cls_type, TRUE); + } + + return; +} + +/* + * Prints the Babeltrace version. + */ +static +void print_version(void) +{ + if (GIT_VERSION[0] == '\0') { + puts("Babeltrace " VERSION); + } else { + puts("Babeltrace " VERSION " - " GIT_VERSION); + } +} + +/* + * Destroys a component configuration. + */ +static +void bt_config_component_destroy(bt_object *obj) +{ + struct bt_config_component *bt_config_component = + container_of(obj, struct bt_config_component, base); + + if (!obj) { + goto end; + } + + if (bt_config_component->plugin_name) { + g_string_free(bt_config_component->plugin_name, TRUE); + } + + if (bt_config_component->comp_cls_name) { + g_string_free(bt_config_component->comp_cls_name, TRUE); + } + + if (bt_config_component->instance_name) { + g_string_free(bt_config_component->instance_name, TRUE); + } + + BT_VALUE_PUT_REF_AND_RESET(bt_config_component->params); + g_free(bt_config_component); + +end: + return; +} + +/* + * Creates a component configuration using the given plugin name and + * component name. `plugin_name` and `comp_cls_name` are copied (belong + * to the return value). + * + * Return value is owned by the caller. + */ +static +struct bt_config_component *bt_config_component_create( + bt_component_class_type type, + const char *plugin_name, const char *comp_cls_name) +{ + struct bt_config_component *cfg_component = NULL; + + cfg_component = g_new0(struct bt_config_component, 1); + if (!cfg_component) { + print_err_oom(); + goto error; + } + + bt_object_init_shared(&cfg_component->base, + bt_config_component_destroy); + cfg_component->type = type; + cfg_component->plugin_name = g_string_new(plugin_name); + if (!cfg_component->plugin_name) { + print_err_oom(); + goto error; + } + + cfg_component->comp_cls_name = g_string_new(comp_cls_name); + if (!cfg_component->comp_cls_name) { + print_err_oom(); + goto error; + } + + cfg_component->instance_name = g_string_new(NULL); + if (!cfg_component->instance_name) { + print_err_oom(); + goto error; + } + + /* Start with empty parameters */ + cfg_component->params = bt_value_map_create(); + if (!cfg_component->params) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg_component); + +end: + return cfg_component; +} + +/* + * Creates a component configuration from a command-line --component + * option's argument. + */ +static +struct bt_config_component *bt_config_component_from_arg(const char *arg) +{ + struct bt_config_component *cfg_comp = NULL; + char *name = NULL; + char *plugin_name = NULL; + char *comp_cls_name = NULL; + bt_component_class_type type; + + plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name, &type); + if (!plugin_name || !comp_cls_name) { + goto error; + } + + cfg_comp = bt_config_component_create(type, plugin_name, comp_cls_name); + if (!cfg_comp) { + goto error; + } + + if (name) { + g_string_assign(cfg_comp->instance_name, name); + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg_comp); + +end: + g_free(name); + g_free(plugin_name); + g_free(comp_cls_name); + return cfg_comp; +} + +/* + * Destroys a configuration. + */ +static +void bt_config_destroy(bt_object *obj) +{ + struct bt_config *cfg = + container_of(obj, struct bt_config, base); + + if (!obj) { + goto end; + } + + BT_VALUE_PUT_REF_AND_RESET(cfg->plugin_paths); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + if (cfg->cmd_data.run.sources) { + g_ptr_array_free(cfg->cmd_data.run.sources, TRUE); + } + + if (cfg->cmd_data.run.filters) { + g_ptr_array_free(cfg->cmd_data.run.filters, TRUE); + } + + if (cfg->cmd_data.run.sinks) { + g_ptr_array_free(cfg->cmd_data.run.sinks, TRUE); + } + + if (cfg->cmd_data.run.connections) { + g_ptr_array_free(cfg->cmd_data.run.connections, + TRUE); + } + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + break; + case BT_CONFIG_COMMAND_HELP: + BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component); + break; + case BT_CONFIG_COMMAND_QUERY: + BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.query.cfg_component); + + if (cfg->cmd_data.query.object) { + g_string_free(cfg->cmd_data.query.object, TRUE); + } + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + if (cfg->cmd_data.print_ctf_metadata.path) { + g_string_free(cfg->cmd_data.print_ctf_metadata.path, + TRUE); + g_string_free( + cfg->cmd_data.print_ctf_metadata.output_path, + TRUE); + } + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + if (cfg->cmd_data.print_lttng_live_sessions.url) { + g_string_free( + cfg->cmd_data.print_lttng_live_sessions.url, + TRUE); + g_string_free( + cfg->cmd_data.print_lttng_live_sessions.output_path, + TRUE); + } + break; + default: + abort(); + } + + g_free(cfg); + +end: + return; +} + +static +void destroy_glist_of_gstring(GList *list) +{ + GList *at; + + if (!list) { + return; + } + + for (at = list; at != NULL; at = g_list_next(at)) { + g_string_free(at->data, TRUE); + } + + g_list_free(list); +} + +/* + * Creates a simple lexical scanner for parsing comma-delimited names + * and fields. + * + * Return value is owned by the caller. + */ +static +GScanner *create_csv_identifiers_scanner(void) +{ + GScanner *scanner; + GScannerConfig scanner_config = { + .cset_skip_characters = " \t\n", + .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_", + .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-", + .case_sensitive = TRUE, + .cpair_comment_single = NULL, + .skip_comment_multi = TRUE, + .skip_comment_single = TRUE, + .scan_comment_multi = FALSE, + .scan_identifier = TRUE, + .scan_identifier_1char = TRUE, + .scan_identifier_NULL = FALSE, + .scan_symbols = FALSE, + .symbol_2_token = FALSE, + .scope_0_fallback = FALSE, + .scan_binary = FALSE, + .scan_octal = FALSE, + .scan_float = FALSE, + .scan_hex = FALSE, + .scan_hex_dollar = FALSE, + .numbers_2_int = FALSE, + .int_2_float = FALSE, + .store_int64 = FALSE, + .scan_string_sq = FALSE, + .scan_string_dq = FALSE, + .identifier_2_string = FALSE, + .char_2_token = TRUE, + }; + + scanner = g_scanner_new(&scanner_config); + if (!scanner) { + print_err_oom(); + } + + return scanner; +} + +/* + * Converts a comma-delimited list of known names (--names option) to + * an array value object containing those names as string value objects. + * + * Return value is owned by the caller. + */ +static +bt_value *names_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + bt_value *names = NULL; + bool found_all = false, found_none = false, found_item = false; + + names = bt_value_array_create(); + if (!names) { + print_err_oom(); + goto error; + } + + scanner = create_csv_identifiers_scanner(); + if (!scanner) { + goto error; + } + + g_scanner_input_text(scanner, arg, strlen(arg)); + + while (true) { + GTokenType token_type = g_scanner_get_next_token(scanner); + + switch (token_type) { + case G_TOKEN_IDENTIFIER: + { + const char *identifier = scanner->value.v_identifier; + + if (!strcmp(identifier, "payload") || + !strcmp(identifier, "args") || + !strcmp(identifier, "arg")) { + found_item = true; + if (bt_value_array_append_string_element(names, + "payload")) { + goto error; + } + } else if (!strcmp(identifier, "context") || + !strcmp(identifier, "ctx")) { + found_item = true; + if (bt_value_array_append_string_element(names, + "context")) { + goto error; + } + } else if (!strcmp(identifier, "scope") || + !strcmp(identifier, "header")) { + found_item = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "all")) { + found_all = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "none")) { + found_none = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else { + printf_err("Unknown name: `%s`\n", + identifier); + goto error; + } + break; + } + case G_TOKEN_COMMA: + continue; + case G_TOKEN_EOF: + goto end; + default: + goto error; + } + } + +end: + if (found_none && found_all) { + printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n"); + goto error; + } + /* + * Legacy behavior is to clear the defaults (show none) when at + * least one item is specified. + */ + if (found_item && !found_none && !found_all) { + if (bt_value_array_append_string_element(names, "none")) { + goto error; + } + } + if (scanner) { + g_scanner_destroy(scanner); + } + return names; + +error: + BT_VALUE_PUT_REF_AND_RESET(names); + if (scanner) { + g_scanner_destroy(scanner); + } + return names; +} + +/* + * Converts a comma-delimited list of known fields (--fields option) to + * an array value object containing those fields as string + * value objects. + * + * Return value is owned by the caller. + */ +static +bt_value *fields_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + bt_value *fields; + + fields = bt_value_array_create(); + if (!fields) { + print_err_oom(); + goto error; + } + + scanner = create_csv_identifiers_scanner(); + if (!scanner) { + goto error; + } + + g_scanner_input_text(scanner, arg, strlen(arg)); + + while (true) { + GTokenType token_type = g_scanner_get_next_token(scanner); + + switch (token_type) { + case G_TOKEN_IDENTIFIER: + { + const char *identifier = scanner->value.v_identifier; + + if (!strcmp(identifier, "trace") || + !strcmp(identifier, "trace:hostname") || + !strcmp(identifier, "trace:domain") || + !strcmp(identifier, "trace:procname") || + !strcmp(identifier, "trace:vpid") || + !strcmp(identifier, "loglevel") || + !strcmp(identifier, "emf") || + !strcmp(identifier, "callsite") || + !strcmp(identifier, "all")) { + if (bt_value_array_append_string_element(fields, + identifier)) { + goto error; + } + } else { + printf_err("Unknown field: `%s`\n", + identifier); + goto error; + } + break; + } + case G_TOKEN_COMMA: + continue; + case G_TOKEN_EOF: + goto end; + default: + goto error; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(fields); + +end: + if (scanner) { + g_scanner_destroy(scanner); + } + return fields; +} + +static +void append_param_arg(GString *params_arg, const char *key, const char *value) +{ + BT_ASSERT(params_arg); + BT_ASSERT(key); + BT_ASSERT(value); + + if (params_arg->len != 0) { + g_string_append_c(params_arg, ','); + } + + g_string_append(params_arg, key); + g_string_append_c(params_arg, '='); + g_string_append(params_arg, value); +} + +/* + * Inserts the equivalent "prefix-NAME=yes" strings into params_arg + * where the names are in names_array. + */ +static +int insert_flat_params_from_array(GString *params_arg, + const bt_value *names_array, const char *prefix) +{ + int ret = 0; + int i; + GString *tmpstr = NULL, *default_value = NULL; + bool default_set = false, non_default_set = false; + + /* + * names_array may be NULL if no CLI options were specified to + * trigger its creation. + */ + if (!names_array) { + goto end; + } + + tmpstr = g_string_new(NULL); + if (!tmpstr) { + print_err_oom(); + ret = -1; + goto end; + } + + default_value = g_string_new(NULL); + if (!default_value) { + print_err_oom(); + ret = -1; + goto end; + } + + for (i = 0; i < bt_value_array_get_size(names_array); i++) { + const bt_value *str_obj = + bt_value_array_borrow_element_by_index_const(names_array, + i); + const char *suffix; + bool is_default = false; + + if (!str_obj) { + printf_err("Unexpected error\n"); + ret = -1; + goto end; + } + + suffix = bt_value_string_get(str_obj); + + g_string_assign(tmpstr, prefix); + g_string_append(tmpstr, "-"); + + /* Special-case for "all" and "none". */ + if (!strcmp(suffix, "all")) { + is_default = true; + g_string_assign(default_value, "show"); + } else if (!strcmp(suffix, "none")) { + is_default = true; + g_string_assign(default_value, "hide"); + } + if (is_default) { + default_set = true; + g_string_append(tmpstr, "default"); + append_param_arg(params_arg, tmpstr->str, + default_value->str); + } else { + non_default_set = true; + g_string_append(tmpstr, suffix); + append_param_arg(params_arg, tmpstr->str, "yes"); + } + } + + /* Implicit field-default=hide if any non-default option is set. */ + if (non_default_set && !default_set) { + g_string_assign(tmpstr, prefix); + g_string_append(tmpstr, "-default"); + g_string_assign(default_value, "hide"); + append_param_arg(params_arg, tmpstr->str, default_value->str); + } + +end: + if (default_value) { + g_string_free(default_value, TRUE); + } + + if (tmpstr) { + g_string_free(tmpstr, TRUE); + } + + return ret; +} + +/* popt options */ +enum { + OPT_NONE = 0, + OPT_BASE_PARAMS, + OPT_BEGIN, + OPT_CLOCK_CYCLES, + OPT_CLOCK_DATE, + OPT_CLOCK_FORCE_CORRELATE, + OPT_CLOCK_GMT, + OPT_CLOCK_OFFSET, + OPT_CLOCK_OFFSET_NS, + OPT_CLOCK_SECONDS, + OPT_COLOR, + OPT_COMPONENT, + OPT_CONNECT, + OPT_DEBUG, + OPT_DEBUG_INFO, + OPT_DEBUG_INFO_DIR, + OPT_DEBUG_INFO_FULL_PATH, + OPT_DEBUG_INFO_TARGET_PREFIX, + OPT_END, + OPT_FIELDS, + OPT_HELP, + OPT_INPUT_FORMAT, + OPT_LIST, + OPT_NAME, + OPT_NAMES, + OPT_NO_DELTA, + OPT_OMIT_HOME_PLUGIN_PATH, + OPT_OMIT_SYSTEM_PLUGIN_PATH, + OPT_OUTPUT, + OPT_OUTPUT_FORMAT, + OPT_PARAMS, + OPT_PATH, + OPT_PLUGIN_PATH, + OPT_RESET_BASE_PARAMS, + OPT_RETRY_DURATION, + OPT_RUN_ARGS, + OPT_RUN_ARGS_0, + OPT_STREAM_INTERSECTION, + OPT_TIMERANGE, + OPT_URL, + OPT_VERBOSE, +}; + +enum bt_config_component_dest { + BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1, + BT_CONFIG_COMPONENT_DEST_SOURCE, + BT_CONFIG_COMPONENT_DEST_FILTER, + BT_CONFIG_COMPONENT_DEST_SINK, +}; + +/* + * Adds a configuration component to the appropriate configuration + * array depending on the destination. + */ +static +void add_run_cfg_comp(struct bt_config *cfg, + struct bt_config_component *cfg_comp, + enum bt_config_component_dest dest) +{ + bt_object_get_ref(cfg_comp); + + switch (dest) { + case BT_CONFIG_COMPONENT_DEST_SOURCE: + g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp); + break; + case BT_CONFIG_COMPONENT_DEST_FILTER: + g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp); + break; + case BT_CONFIG_COMPONENT_DEST_SINK: + g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp); + break; + default: + abort(); + } +} + +static +int add_run_cfg_comp_check_name(struct bt_config *cfg, + struct bt_config_component *cfg_comp, + enum bt_config_component_dest dest, + bt_value *instance_names) +{ + int ret = 0; + + if (cfg_comp->instance_name->len == 0) { + printf_err("Found an unnamed component\n"); + ret = -1; + goto end; + } + + if (bt_value_map_has_entry(instance_names, + cfg_comp->instance_name->str)) { + printf_err("Duplicate component instance name:\n %s\n", + cfg_comp->instance_name->str); + ret = -1; + goto end; + } + + if (bt_value_map_insert_entry(instance_names, + cfg_comp->instance_name->str, bt_value_null)) { + print_err_oom(); + ret = -1; + goto end; + } + + add_run_cfg_comp(cfg, cfg_comp, dest); + +end: + return ret; +} + +static +int append_env_var_plugin_paths(bt_value *plugin_paths) +{ + int ret = 0; + const char *envvar; + + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + goto end; + } + + envvar = getenv("BABELTRACE_PLUGIN_PATH"); + if (!envvar) { + goto end; + } + + ret = bt_config_append_plugin_paths(plugin_paths, envvar); + +end: + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + } + + return ret; +} + +static +int append_home_and_system_plugin_paths(bt_value *plugin_paths, + bool omit_system_plugin_path, bool omit_home_plugin_path) +{ + int ret; + + if (!omit_home_plugin_path) { + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + } else { + char *home_plugin_dir = + bt_common_get_home_plugin_path(); + + if (home_plugin_dir) { + ret = bt_config_append_plugin_paths( + plugin_paths, home_plugin_dir); + free(home_plugin_dir); + + if (ret) { + printf_err("Invalid home plugin path\n"); + goto error; + } + } + } + } + + if (!omit_system_plugin_path) { + if (bt_config_append_plugin_paths(plugin_paths, + bt_common_get_system_plugin_path())) { + printf_err("Invalid system plugin path\n"); + goto error; + } + } + return 0; +error: + printf_err("Cannot append home and system plugin paths\n"); + return -1; +} + +static +int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg) +{ + return append_home_and_system_plugin_paths(cfg->plugin_paths, + cfg->omit_system_plugin_path, cfg->omit_home_plugin_path); +} + +static +struct bt_config *bt_config_base_create(enum bt_config_command command, + const bt_value *initial_plugin_paths, + bool needs_plugins) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = g_new0(struct bt_config, 1); + if (!cfg) { + print_err_oom(); + goto error; + } + + bt_object_init_shared(&cfg->base, bt_config_destroy); + cfg->command = command; + cfg->command_needs_plugins = needs_plugins; + + if (initial_plugin_paths) { + bt_value *initial_plugin_paths_copy; + + (void) bt_value_copy(initial_plugin_paths, + &initial_plugin_paths_copy); + cfg->plugin_paths = initial_plugin_paths_copy; + } else { + cfg->plugin_paths = bt_value_array_create(); + if (!cfg->plugin_paths) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_run_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.sources) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.filters) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.sinks) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_config_connection_destroy); + if (!cfg->cmd_data.run.connections) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_list_plugins_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_help_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.help.cfg_component = + bt_config_component_create(-1, NULL, NULL); + if (!cfg->cmd_data.help.cfg_component) { + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_query_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.query.object = g_string_new(NULL); + if (!cfg->cmd_data.query.object) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_print_ctf_metadata_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL); + if (!cfg->cmd_data.print_ctf_metadata.path) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL); + if (!cfg->cmd_data.print_ctf_metadata.output_path) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_print_lttng_live_sessions_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL); + if (!cfg->cmd_data.print_lttng_live_sessions.url) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.print_lttng_live_sessions.output_path = + g_string_new(NULL); + if (!cfg->cmd_data.print_lttng_live_sessions.output_path) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +int bt_config_append_plugin_paths_check_setuid_setgid( + bt_value *plugin_paths, const char *arg) +{ + int ret = 0; + + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + goto end; + } + + if (bt_config_append_plugin_paths(plugin_paths, arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * Prints the expected format for a --params option. + */ +static +void print_expected_params_format(FILE *fp) +{ + fprintf(fp, "Expected format of PARAMS\n"); + fprintf(fp, "-------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); + fprintf(fp, "\n"); + fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); + fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n"); + fprintf(fp, "and VALUE can be one of:\n"); + fprintf(fp, "\n"); + fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); + fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); + fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); + fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); + fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n"); + fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); + fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); + fprintf(fp, " the null and boolean value symbols above.\n"); + fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); + fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n"); + fprintf(fp, " (as described by the current list) and a closing `]`.\n"); + fprintf(fp, "\n"); + fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Example:\n"); + fprintf(fp, "\n"); + fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); + fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); + fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n"); + fprintf(fp, " things=[1, \"2\", 3]\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); + fprintf(fp, "babeltrace2 from a shell.\n"); +} + + +/* + * Prints the help command usage. + */ +static +void print_help_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n"); + fprintf(fp, " babeltrace2 [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 list-plugins` to show the list of available plugins.\n"); +} + +static +struct poptOption help_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a help + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_help_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + char *plugin_name = NULL, *comp_cls_name = NULL; + + *retcode = 0; + cfg = bt_config_help_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + help_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_HELP: + print_help_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (leftover) { + plugin_comp_cls_names(leftover, NULL, + &plugin_name, &comp_cls_name, + &cfg->cmd_data.help.cfg_component->type); + if (plugin_name && comp_cls_name) { + /* Component class help */ + g_string_assign( + cfg->cmd_data.help.cfg_component->plugin_name, + plugin_name); + g_string_assign( + cfg->cmd_data.help.cfg_component->comp_cls_name, + comp_cls_name); + } else { + /* Fall back to plugin help */ + g_string_assign( + cfg->cmd_data.help.cfg_component->plugin_name, + leftover); + } + } else { + print_help_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + g_free(plugin_name); + g_free(comp_cls_name); + + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the help command usage. + */ +static +void print_query_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static +struct poptOption query_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a query + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_query_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + bt_value *params; + + params = bt_value_null; + bt_value_get_ref(bt_value_null); + + *retcode = 0; + cfg = bt_config_query_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + query_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_PARAMS: + { + bt_value_put_ref(params); + params = bt_value_from_arg(arg); + if (!params) { + printf_err("Invalid format for --params option's argument:\n %s\n", + arg); + goto error; + } + break; + } + case OPT_HELP: + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* + * We need exactly two leftover arguments which are the + * mandatory component class specification and query object. + */ + leftover = poptGetArg(pc); + if (leftover) { + cfg->cmd_data.query.cfg_component = + bt_config_component_from_arg(leftover); + if (!cfg->cmd_data.query.cfg_component) { + printf_err("Invalid format for component class specification:\n %s\n", + leftover); + goto error; + } + + BT_ASSERT(params); + BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, + params); + } else { + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + if (strlen(leftover) == 0) { + printf_err("Invalid empty object\n"); + goto error; + } + + g_string_assign(cfg->cmd_data.query.object, leftover); + } else { + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Unexpected argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + bt_value_put_ref(params); + free(arg); + return cfg; +} + +/* + * Prints the list-plugins command usage. + */ +static +void print_list_plugins_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] list-plugins [OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 help` to get help for a specific plugin or component class.\n"); +} + +static +struct poptOption list_plugins_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a + * list-plugins command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + + *retcode = 0; + cfg = bt_config_list_plugins_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + list_plugins_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_HELP: + print_list_plugins_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Unexpected argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the run command usage. + */ +static +void print_run_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] run [OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); + fprintf(fp, " for all the following components until\n"); + fprintf(fp, " --reset-base-params is encountered\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); + fprintf(fp, " Instantiate the component class CLS of type\n"); + fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); + fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n"); + fprintf(fp, " and optionally name it NAME (you can also\n"); + fprintf(fp, " specify the name with --name)\n"); + fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n"); + fprintf(fp, " expected format of CONNECTION below)\n"); + fprintf(fp, " -n, --name=NAME Set the name of the current component\n"); + fprintf(fp, " to NAME (must be unique amongst all the\n"); + fprintf(fp, " names of the created components)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); + fprintf(fp, " current component (see the expected format\n"); + fprintf(fp, " of PARAMS below)\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n"); + fprintf(fp, " empty map\n"); + fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); + fprintf(fp, " the graph later, retry in DUR µs\n"); + fprintf(fp, " (default: 100000)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Expected format of CONNECTION\n"); + fprintf(fp, "-----------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n"); + fprintf(fp, "\n"); + fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n"); + fprintf(fp, "components to connect together. You must escape the following characters\n\n"); + fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n"); + fprintf(fp, "component with the --name option.\n"); + fprintf(fp, "\n"); + fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n"); + fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n"); + fprintf(fp, "When the port is not specified, `*` is used.\n"); + fprintf(fp, "\n"); + fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n"); + fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n"); + fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n"); + fprintf(fp, "DOWNSTREAM.\n"); + fprintf(fp, "\n"); + fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n"); + fprintf(fp, "which matches anything. You must escape the following characters\n"); + fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\n"); + fprintf(fp, "\n"); + fprintf(fp, "You can connect a source component to a filter or sink component. You\n"); + fprintf(fp, "can connect a filter component to a sink component.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Examples:\n"); + fprintf(fp, "\n"); + fprintf(fp, " my-src:my-sink\n"); + fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); + fprintf(fp, "babeltrace2 from a shell.\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +/* + * Creates a Babeltrace config object from the arguments of a run + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_run_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + struct bt_config_component *cur_cfg_comp = NULL; + enum bt_config_component_dest cur_cfg_comp_dest = + BT_CONFIG_COMPONENT_DEST_UNKNOWN; + bt_value *cur_base_params = NULL; + int opt, ret = 0; + struct bt_config *cfg = NULL; + bt_value *instance_names = NULL; + bt_value *connection_args = NULL; + char error_buf[256] = { 0 }; + long retry_duration = -1; + bt_value_status status; + struct poptOption run_long_options[] = { + { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, + { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, + { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, + { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, + }; + + *retcode = 0; + + if (argc <= 1) { + print_run_usage(stdout); + *retcode = -1; + goto end; + } + + cfg = bt_config_run_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.retry_duration_us = 100000; + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { + print_err_oom(); + goto error; + } + + instance_names = bt_value_map_create(); + if (!instance_names) { + print_err_oom(); + goto error; + } + + connection_args = bt_value_array_create(); + if (!connection_args) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + run_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_COMPONENT: + { + enum bt_config_component_dest new_dest; + + if (cur_cfg_comp) { + ret = add_run_cfg_comp_check_name(cfg, + cur_cfg_comp, cur_cfg_comp_dest, + instance_names); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + if (ret) { + goto error; + } + } + + cur_cfg_comp = bt_config_component_from_arg(arg); + if (!cur_cfg_comp) { + printf_err("Invalid format for --component option's argument:\n %s\n", + arg); + goto error; + } + + switch (cur_cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + new_dest = BT_CONFIG_COMPONENT_DEST_SINK; + break; + default: + abort(); + } + + BT_ASSERT(cur_base_params); + bt_value_put_ref(cur_cfg_comp->params); + status = bt_value_copy(cur_base_params, + &cur_cfg_comp->params); + if (status != BT_VALUE_STATUS_OK) { + print_err_oom(); + goto error; + } + + cur_cfg_comp_dest = new_dest; + break; + } + case OPT_PARAMS: + { + bt_value *params; + bt_value *params_to_set; + + if (!cur_cfg_comp) { + printf_err("Cannot add parameters to unavailable component:\n %s\n", + arg); + goto error; + } + + params = bt_value_from_arg(arg); + if (!params) { + printf_err("Invalid format for --params option's argument:\n %s\n", + arg); + goto error; + } + + status = bt_value_map_extend(cur_cfg_comp->params, + params, ¶ms_to_set); + BT_VALUE_PUT_REF_AND_RESET(params); + if (status != BT_VALUE_STATUS_OK) { + printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", + arg); + goto error; + } + + BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set); + break; + } + case OPT_NAME: + if (!cur_cfg_comp) { + printf_err("Cannot set the name of unavailable component:\n %s\n", + arg); + goto error; + } + + g_string_assign(cur_cfg_comp->instance_name, arg); + break; + case OPT_BASE_PARAMS: + { + bt_value *params = + bt_value_from_arg(arg); + + if (!params) { + printf_err("Invalid format for --base-params option's argument:\n %s\n", + arg); + goto error; + } + + BT_OBJECT_MOVE_REF(cur_base_params, params); + break; + } + case OPT_RESET_BASE_PARAMS: + BT_VALUE_PUT_REF_AND_RESET(cur_base_params); + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { + print_err_oom(); + goto error; + } + break; + case OPT_CONNECT: + if (bt_value_array_append_string_element( + connection_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_RETRY_DURATION: + if (retry_duration < 0) { + printf_err("--retry-duration option's argument must be positive or 0: %ld\n", + retry_duration); + goto error; + } + + cfg->cmd_data.run.retry_duration_us = + (uint64_t) retry_duration; + break; + case OPT_HELP: + print_run_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* This command does not accept leftover arguments */ + if (poptPeekArg(pc)) { + printf_err("Unexpected argument: %s\n", poptPeekArg(pc)); + goto error; + } + + /* Add current component */ + if (cur_cfg_comp) { + ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp, + cur_cfg_comp_dest, instance_names); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + if (ret) { + goto error; + } + } + + if (cfg->cmd_data.run.sources->len == 0) { + printf_err("Incomplete graph: no source component\n"); + goto error; + } + + if (cfg->cmd_data.run.sinks->len == 0) { + printf_err("Incomplete graph: no sink component\n"); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + ret = bt_config_cli_args_create_connections(cfg, + connection_args, + error_buf, 256); + if (ret) { + printf_err("Cannot creation connections:\n%s", error_buf); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + BT_VALUE_PUT_REF_AND_RESET(cur_base_params); + BT_VALUE_PUT_REF_AND_RESET(instance_names); + BT_VALUE_PUT_REF_AND_RESET(connection_args); + return cfg; +} + +static +struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg = NULL; + const char **argv; + int64_t i, len; + const size_t argc = bt_value_array_get_size(run_args) + 1; + + argv = calloc(argc, sizeof(*argv)); + if (!argv) { + print_err_oom(); + goto end; + } + + argv[0] = "run"; + + len = bt_value_array_get_size(run_args); + if (len < 0) { + printf_err("Invalid executable arguments\n"); + goto end; + } + for (i = 0; i < len; i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index_const(run_args, + i); + const char *arg; + + BT_ASSERT(arg_value); + arg = bt_value_string_get(arg_value); + BT_ASSERT(arg); + argv[i + 1] = arg; + } + + cfg = bt_config_run_from_args(argc, argv, retcode, + force_omit_system_plugin_path, force_omit_home_plugin_path, + initial_plugin_paths); + +end: + free(argv); + return cfg; +} + +/* + * Prints the convert command usage. + */ +static +void print_convert_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); + fprintf(fp, " Instantiate the component class CLS of type\n"); + fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); + fprintf(fp, " in the plugin PLUGIN, add it to the\n"); + fprintf(fp, " conversion graph, and optionally name it\n"); + fprintf(fp, " NAME (you can also specify the name with\n"); + fprintf(fp, " --name)\n"); + fprintf(fp, " --name=NAME Set the name of the current component\n"); + fprintf(fp, " to NAME (must be unique amongst all the\n"); + fprintf(fp, " names of the created components)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace2/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); + fprintf(fp, " current component (see the expected format\n"); + fprintf(fp, " of PARAMS below)\n"); + fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n"); + fprintf(fp, " current component to PATH\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); + fprintf(fp, " the graph later, retry in DUR µs\n"); + fprintf(fp, " (default: 100000)\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " --run-args Print the equivalent arguments for the\n"); + fprintf(fp, " `run` command to the standard output,\n"); + fprintf(fp, " formatted for a shell, and quit\n"); + fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n"); + fprintf(fp, " `run` command to the standard output,\n"); + fprintf(fp, " formatted for `xargs -0`, and quit\n"); + fprintf(fp, " --stream-intersection Only process events when all streams\n"); + fprintf(fp, " are active\n"); + fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n"); + fprintf(fp, " current component to URL\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `source.ctf.fs` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); + fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `sink.text.pretty` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); + fprintf(fp, " --clock-date Print timestamp dates\n"); + fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n"); + fprintf(fp, " time zone instead of the local time zone\n"); + fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n"); + fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n"); + fprintf(fp, " --color=(never | auto | always)\n"); + fprintf(fp, " Never, automatically, or always emit\n"); + fprintf(fp, " console color codes\n"); + fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n"); + fprintf(fp, " `all`, `trace`, `trace:hostname`,\n"); + fprintf(fp, " `trace:domain`, `trace:procname`,\n"); + fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n"); + fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n"); + fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n"); + fprintf(fp, " `all`, `scope`, `header`, `context`\n"); + fprintf(fp, " (or `ctx`)\n"); + fprintf(fp, " --no-delta Do not print time delta between\n"); + fprintf(fp, " consecutive events\n"); + fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n"); + fprintf(fp, " the standard output\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.utils.muxer` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n"); + fprintf(fp, " correlated across traces\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n"); + fprintf(fp, " time range to BEGIN (see the format of\n"); + fprintf(fp, " BEGIN below)\n"); + fprintf(fp, " -e, --end=END Set the end time of the conversion time\n"); + fprintf(fp, " range to END (see the format of END below)\n"); + fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n"); + fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n"); + fprintf(fp, " `]`) (see the format of BEGIN/END below)\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --debug-info Create an implicit\n"); + fprintf(fp, " `filter.lttng-utils.debug-info` component\n"); + fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); + fprintf(fp, " instead of `/usr/lib/debug`\n"); + fprintf(fp, " --debug-info-full-path Show full debug info source and\n"); + fprintf(fp, " binary paths instead of just names\n"); + fprintf(fp, " --debug-info-target-prefix=DIR\n"); + fprintf(fp, " Use directory DIR as a prefix when\n"); + fprintf(fp, " looking up executables during debug\n"); + fprintf(fp, " info analysis\n"); + fprintf(fp, "\n"); + fprintf(fp, "Legacy options that still work:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n"); + fprintf(fp, " `ctf`:\n"); + fprintf(fp, " Create an implicit `source.ctf.fs`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `lttng-live`:\n"); + fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n"); + fprintf(fp, " `text`:\n"); + fprintf(fp, " Create an implicit `sink.text.pretty`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `ctf`:\n"); + fprintf(fp, " Create an implicit `sink.ctf.fs`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `dummy`:\n"); + fprintf(fp, " Create an implicit `sink.utils.dummy`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `ctf-metadata`:\n"); + fprintf(fp, " Query the `source.ctf.fs` component class\n"); + fprintf(fp, " for metadata text and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Format of BEGIN and END\n"); + fprintf(fp, "-----------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static +struct poptOption convert_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL }, + { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL }, + { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, + { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, + { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, + { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL }, + { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL }, + { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL }, + { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL }, + { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, + { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL }, + { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL }, + { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL }, + { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL }, + { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, + { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, + { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, + { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, + { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL }, + { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL }, + { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL }, + { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL }, + { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL }, + { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, + { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, + { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL }, + { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +static +GString *get_component_auto_name(const char *prefix, + const bt_value *existing_names) +{ + unsigned int i = 0; + GString *auto_name = g_string_new(NULL); + + if (!auto_name) { + print_err_oom(); + goto end; + } + + if (!bt_value_map_has_entry(existing_names, prefix)) { + g_string_assign(auto_name, prefix); + goto end; + } + + do { + g_string_printf(auto_name, "%s-%d", prefix, i); + i++; + } while (bt_value_map_has_entry(existing_names, auto_name->str)); + +end: + return auto_name; +} + +struct implicit_component_args { + bool exists; + GString *comp_arg; + GString *name_arg; + GString *params_arg; + bt_value *extra_params; +}; + +static +int assign_name_to_implicit_component(struct implicit_component_args *args, + const char *prefix, bt_value *existing_names, + GList **comp_names, bool append_to_comp_names) +{ + int ret = 0; + GString *name = NULL; + + if (!args->exists) { + goto end; + } + + name = get_component_auto_name(prefix, + existing_names); + + if (!name) { + ret = -1; + goto end; + } + + g_string_assign(args->name_arg, name->str); + + if (bt_value_map_insert_entry(existing_names, name->str, + bt_value_null)) { + print_err_oom(); + ret = -1; + goto end; + } + + if (append_to_comp_names) { + *comp_names = g_list_append(*comp_names, name); + name = NULL; + } + +end: + if (name) { + g_string_free(name, TRUE); + } + + return ret; +} + +static +int append_run_args_for_implicit_component( + struct implicit_component_args *impl_args, + bt_value *run_args) +{ + int ret = 0; + size_t i; + + if (!impl_args->exists) { + goto end; + } + + if (bt_value_array_append_string_element(run_args, "--component")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) { + print_err_oom(); + goto error; + } + + if (impl_args->params_arg->len > 0) { + if (bt_value_array_append_string_element(run_args, "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, + impl_args->params_arg->str)) { + print_err_oom(); + goto error; + } + } + + for (i = 0; i < bt_value_array_get_size(impl_args->extra_params); + i++) { + const bt_value *elem; + const char *arg; + + elem = bt_value_array_borrow_element_by_index(impl_args->extra_params, + i); + if (!elem) { + goto error; + } + + BT_ASSERT(bt_value_is_string(elem)); + arg = bt_value_string_get(elem); + ret = bt_value_array_append_string_element(run_args, arg); + if (ret) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +void finalize_implicit_component_args(struct implicit_component_args *args) +{ + BT_ASSERT(args); + + if (args->comp_arg) { + g_string_free(args->comp_arg, TRUE); + } + + if (args->name_arg) { + g_string_free(args->name_arg, TRUE); + } + + if (args->params_arg) { + g_string_free(args->params_arg, TRUE); + } + + bt_value_put_ref(args->extra_params); +} + +static +int init_implicit_component_args(struct implicit_component_args *args, + const char *comp_arg, bool exists) +{ + int ret = 0; + + args->exists = exists; + args->comp_arg = g_string_new(comp_arg); + args->name_arg = g_string_new(NULL); + args->params_arg = g_string_new(NULL); + args->extra_params = bt_value_array_create(); + + if (!args->comp_arg || !args->name_arg || + !args->params_arg || !args->extra_params) { + ret = -1; + finalize_implicit_component_args(args); + print_err_oom(); + goto end; + } + +end: + return ret; +} + +static +void append_implicit_component_param(struct implicit_component_args *args, + const char *key, const char *value) +{ + BT_ASSERT(args); + BT_ASSERT(key); + BT_ASSERT(value); + append_param_arg(args->params_arg, key, value); +} + +/* Escape value to make it suitable to use as a string parameter value. */ +static +gchar *escape_string_value(const char *value) +{ + GString *ret; + const char *in; + + ret = g_string_new(NULL); + if (!ret) { + print_err_oom(); + goto end; + } + + in = value; + while (*in) { + switch (*in) { + case '"': + case '\\': + g_string_append_c(ret, '\\'); + break; + } + + g_string_append_c(ret, *in); + + in++; + } + +end: + return g_string_free(ret, FALSE); +} + +static +int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) +{ + BT_ASSERT(buf); + + int ret = -1; + + switch (bt_value_get_type(value)) { + case BT_VALUE_TYPE_STRING: + { + const char *str_value = bt_value_string_get(value); + gchar *escaped_str_value; + + escaped_str_value = escape_string_value(str_value); + if (!escaped_str_value) { + goto end; + } + + g_string_append_printf(buf, "\"%s\"", escaped_str_value); + + g_free(escaped_str_value); + break; + } + case BT_VALUE_TYPE_ARRAY: { + g_string_append_c(buf, '['); + uint64_t sz = bt_value_array_get_size(value); + for (uint64_t i = 0; i < sz; i++) { + const bt_value *item; + int ret; + + if (i > 0) { + g_string_append(buf, ", "); + } + + item = bt_value_array_borrow_element_by_index_const( + value, i); + ret = bt_value_to_cli_param_value_append(item, buf); + + if (ret) { + goto end; + } + } + g_string_append_c(buf, ']'); + break; + } + default: + abort(); + } + + ret = 0; + +end: + return ret; +} + +/* + * Convert `value` to its equivalent representation as a command line parameter + * value. + */ + +static +gchar *bt_value_to_cli_param_value(bt_value *value) +{ + GString *buf; + gchar *result = NULL; + + buf = g_string_new(NULL); + if (!buf) { + print_err_oom(); + goto error; + } + + if (bt_value_to_cli_param_value_append(value, buf)) { + goto error; + } + + result = g_string_free(buf, FALSE); + buf = NULL; + + goto end; + +error: + if (buf) { + g_string_free(buf, TRUE); + } + +end: + return result; +} + +static +int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) +{ + BT_ASSERT(args); + BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY); + BT_ASSERT(key); + BT_ASSERT(value); + + int ret = 0; + gchar *str_value = NULL; + GString *parameter = NULL; + + if (bt_value_array_append_string_element(args, "--params")) { + print_err_oom(); + ret = -1; + goto end; + } + + str_value = bt_value_to_cli_param_value(value); + if (!str_value) { + ret = -1; + goto end; + } + + parameter = g_string_new(NULL); + if (!parameter) { + print_err_oom(); + ret = -1; + goto end; + } + + g_string_printf(parameter, "%s=%s", key, str_value); + + if (bt_value_array_append_string_element(args, parameter->str)) { + print_err_oom(); + ret = -1; + goto end; + } + +end: + if (parameter) { + g_string_free(parameter, TRUE); + parameter = NULL; + } + + if (str_value) { + g_free(str_value); + str_value = NULL; + } + + return ret; +} + +static +int append_string_parameter_to_args(bt_value *args, const char *key, const char *value) +{ + bt_value *str_value; + int ret; + + str_value = bt_value_string_create_init(value); + + if (!str_value) { + print_err_oom(); + ret = -1; + goto end; + } + + ret = append_parameter_to_args(args, key, str_value); + +end: + BT_VALUE_PUT_REF_AND_RESET(str_value); + return ret; +} + +static +int append_implicit_component_extra_param(struct implicit_component_args *args, + const char *key, const char *value) +{ + return append_string_parameter_to_args(args->extra_params, key, value); +} + +static +int convert_append_name_param(enum bt_config_component_dest dest, + GString *cur_name, GString *cur_name_prefix, + bt_value *run_args, + bt_value *all_names, + GList **source_names, GList **filter_names, + GList **sink_names) +{ + int ret = 0; + + if (cur_name_prefix->len > 0) { + /* We're after a --component option */ + GString *name = NULL; + bool append_name_opt = false; + + if (cur_name->len == 0) { + /* + * No explicit name was provided for the user + * component. + */ + name = get_component_auto_name(cur_name_prefix->str, + all_names); + append_name_opt = true; + } else { + /* + * An explicit name was provided for the user + * component. + */ + if (bt_value_map_has_entry(all_names, + cur_name->str)) { + printf_err("Duplicate component instance name:\n %s\n", + cur_name->str); + goto error; + } + + name = g_string_new(cur_name->str); + } + + if (!name) { + print_err_oom(); + goto error; + } + + /* + * Remember this name globally, for the uniqueness of + * all component names. + */ + if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) { + print_err_oom(); + goto error; + } + + /* + * Append the --name option if necessary. + */ + if (append_name_opt) { + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, name->str)) { + print_err_oom(); + goto error; + } + } + + /* + * Remember this name specifically for the type of the + * component. This is to create connection arguments. + */ + switch (dest) { + case BT_CONFIG_COMPONENT_DEST_SOURCE: + *source_names = g_list_append(*source_names, name); + break; + case BT_CONFIG_COMPONENT_DEST_FILTER: + *filter_names = g_list_append(*filter_names, name); + break; + case BT_CONFIG_COMPONENT_DEST_SINK: + *sink_names = g_list_append(*sink_names, name); + break; + default: + abort(); + } + + g_string_assign(cur_name_prefix, ""); + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +/* + * Escapes `.`, `:`, and `\` of `input` with `\`. + */ +static +GString *escape_dot_colon(const char *input) +{ + GString *output = g_string_new(NULL); + const char *ch; + + if (!output) { + print_err_oom(); + goto end; + } + + for (ch = input; *ch != '\0'; ch++) { + if (*ch == '\\' || *ch == '.' || *ch == ':') { + g_string_append_c(output, '\\'); + } + + g_string_append_c(output, *ch); + } + +end: + return output; +} + +/* + * Appends a --connect option to a list of arguments. `upstream_name` + * and `downstream_name` are escaped with escape_dot_colon() in this + * function. + */ +static +int append_connect_arg(bt_value *run_args, + const char *upstream_name, const char *downstream_name) +{ + int ret = 0; + GString *e_upstream_name = escape_dot_colon(upstream_name); + GString *e_downstream_name = escape_dot_colon(downstream_name); + GString *arg = g_string_new(NULL); + + if (!e_upstream_name || !e_downstream_name || !arg) { + print_err_oom(); + ret = -1; + goto end; + } + + ret = bt_value_array_append_string_element(run_args, "--connect"); + if (ret) { + print_err_oom(); + ret = -1; + goto end; + } + + g_string_append(arg, e_upstream_name->str); + g_string_append_c(arg, ':'); + g_string_append(arg, e_downstream_name->str); + ret = bt_value_array_append_string_element(run_args, arg->str); + if (ret) { + print_err_oom(); + ret = -1; + goto end; + } + +end: + if (arg) { + g_string_free(arg, TRUE); + } + + if (e_upstream_name) { + g_string_free(e_upstream_name, TRUE); + } + + if (e_downstream_name) { + g_string_free(e_downstream_name, TRUE); + } + + return ret; +} + +/* + * Appends the run command's --connect options for the convert command. + */ +static +int convert_auto_connect(bt_value *run_args, + GList *source_names, GList *filter_names, + GList *sink_names) +{ + int ret = 0; + GList *source_at = source_names; + GList *filter_at = filter_names; + GList *filter_prev; + GList *sink_at = sink_names; + + BT_ASSERT(source_names); + BT_ASSERT(filter_names); + BT_ASSERT(sink_names); + + /* Connect all sources to the first filter */ + for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) { + GString *source_name = source_at->data; + GString *filter_name = filter_at->data; + + ret = append_connect_arg(run_args, source_name->str, + filter_name->str); + if (ret) { + goto error; + } + } + + filter_prev = filter_at; + filter_at = g_list_next(filter_at); + + /* Connect remaining filters */ + for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { + GString *filter_name = filter_at->data; + GString *filter_prev_name = filter_prev->data; + + ret = append_connect_arg(run_args, filter_prev_name->str, + filter_name->str); + if (ret) { + goto error; + } + } + + /* Connect last filter to all sinks */ + for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) { + GString *filter_name = filter_prev->data; + GString *sink_name = sink_at->data; + + ret = append_connect_arg(run_args, filter_name->str, + sink_name->str); + if (ret) { + goto error; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int split_timerange(const char *arg, char **begin, char **end) +{ + int ret = 0; + const char *ch = arg; + size_t end_pos; + GString *g_begin = NULL; + GString *g_end = NULL; + + BT_ASSERT(arg); + + if (*ch == '[') { + ch++; + } + + g_begin = bt_common_string_until(ch, "", ",", &end_pos); + if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) { + goto error; + } + + ch += end_pos + 1; + + g_end = bt_common_string_until(ch, "", "]", &end_pos); + if (!g_end || g_end->len == 0) { + goto error; + } + + BT_ASSERT(begin); + BT_ASSERT(end); + *begin = g_begin->str; + *end = g_end->str; + g_string_free(g_begin, FALSE); + g_string_free(g_end, FALSE); + g_begin = NULL; + g_end = NULL; + goto end; + +error: + ret = -1; + +end: + if (g_begin) { + g_string_free(g_begin, TRUE); + } + + if (g_end) { + g_string_free(g_end, TRUE); + } + + return ret; +} + +static +int g_list_prepend_gstring(GList **list, const char *string) +{ + int ret = 0; + GString *gs = g_string_new(string); + + BT_ASSERT(list); + + if (!gs) { + print_err_oom(); + goto end; + } + + *list = g_list_prepend(*list, gs); + +end: + return ret; +} + +/* + * Creates a Babeltrace config object from the arguments of a convert + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths, char *log_level) +{ + poptContext pc = NULL; + char *arg = NULL; + enum bt_config_component_dest cur_comp_dest = + BT_CONFIG_COMPONENT_DEST_UNKNOWN; + int opt, ret = 0; + struct bt_config *cfg = NULL; + bool got_input_format_opt = false; + bool got_output_format_opt = false; + bool trimmer_has_begin = false; + bool trimmer_has_end = false; + bool stream_intersection_mode = false; + GString *cur_name = NULL; + GString *cur_name_prefix = NULL; + const char *leftover = NULL; + bool print_run_args = false; + bool print_run_args_0 = false; + bool print_ctf_metadata = false; + bt_value *run_args = NULL; + bt_value *all_names = NULL; + GList *source_names = NULL; + GList *filter_names = NULL; + GList *sink_names = NULL; + bt_value *leftovers = NULL; + struct implicit_component_args implicit_ctf_input_args = { 0 }; + struct implicit_component_args implicit_ctf_output_args = { 0 }; + struct implicit_component_args implicit_lttng_live_args = { 0 }; + struct implicit_component_args implicit_dummy_args = { 0 }; + struct implicit_component_args implicit_text_args = { 0 }; + struct implicit_component_args implicit_debug_info_args = { 0 }; + struct implicit_component_args implicit_muxer_args = { 0 }; + struct implicit_component_args implicit_trimmer_args = { 0 }; + bt_value *plugin_paths; + char error_buf[256] = { 0 }; + size_t i; + struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; + char *output = NULL; + + (void) bt_value_copy(initial_plugin_paths, &plugin_paths); + + *retcode = 0; + + if (argc <= 1) { + print_convert_usage(stdout); + *retcode = -1; + goto end; + } + + if (init_implicit_component_args(&implicit_ctf_input_args, + "source.ctf.fs", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_ctf_output_args, + "sink.ctf.fs", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_lttng_live_args, + "source.ctf.lttng-live", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_text_args, + "sink.text.pretty", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_dummy_args, + "sink.utils.dummy", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_debug_info_args, + "filter.lttng-utils.debug-info", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_muxer_args, + "filter.utils.muxer", true)) { + goto error; + } + + if (init_implicit_component_args(&implicit_trimmer_args, + "filter.utils.trimmer", false)) { + goto error; + } + + all_names = bt_value_map_create(); + if (!all_names) { + print_err_oom(); + goto error; + } + + run_args = bt_value_array_create(); + if (!run_args) { + print_err_oom(); + goto error; + } + + cur_name = g_string_new(NULL); + if (!cur_name) { + print_err_oom(); + goto error; + } + + cur_name_prefix = g_string_new(NULL); + if (!cur_name_prefix) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(plugin_paths); + if (ret) { + goto error; + } + + leftovers = bt_value_array_create(); + if (!leftovers) { + print_err_oom(); + goto error; + } + + /* + * First pass: collect all arguments which need to be passed + * as is to the run command. This pass can also add --name + * arguments if needed to automatically name unnamed component + * instances. Also it does the following transformations: + * + * --path=PATH -> --params=path="PATH" + * --url=URL -> --params=url="URL" + * + * Also it appends the plugin paths of --plugin-path to + * `plugin_paths`. + */ + pc = poptGetContext(NULL, argc, (const char **) argv, + convert_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + char *name = NULL; + char *plugin_name = NULL; + char *comp_cls_name = NULL; + + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_COMPONENT: + { + bt_component_class_type type; + const char *type_prefix; + + /* Append current component's name if needed */ + ret = convert_append_name_param(cur_comp_dest, cur_name, + cur_name_prefix, run_args, all_names, + &source_names, &filter_names, &sink_names); + if (ret) { + goto error; + } + + /* Parse the argument */ + plugin_comp_cls_names(arg, &name, &plugin_name, + &comp_cls_name, &type); + if (!plugin_name || !comp_cls_name) { + printf_err("Invalid format for --component option's argument:\n %s\n", + arg); + goto error; + } + + if (name) { + g_string_assign(cur_name, name); + } else { + g_string_assign(cur_name, ""); + } + + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + type_prefix = "source"; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + type_prefix = "filter"; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; + type_prefix = "sink"; + break; + default: + abort(); + } + + if (bt_value_array_append_string_element(run_args, + "--component")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name_prefix, ""); + g_string_append_printf(cur_name_prefix, "%s.%s.%s", + type_prefix, plugin_name, comp_cls_name); + free(name); + free(plugin_name); + free(comp_cls_name); + name = NULL; + plugin_name = NULL; + comp_cls_name = NULL; + break; + } + case OPT_PARAMS: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set parameters:\n %s\n", + arg); + goto error; + } + + if (bt_value_array_append_string_element(run_args, + "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_PATH: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set `path` parameter:\n %s\n", + arg); + goto error; + } + + if (append_string_parameter_to_args(run_args, "path", arg)) { + goto error; + } + break; + case OPT_URL: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set `url` parameter:\n %s\n", + arg); + goto error; + } + + + if (append_string_parameter_to_args(run_args, "url", arg)) { + goto error; + } + break; + case OPT_NAME: + if (cur_name_prefix->len == 0) { + printf_err("No current component to name:\n %s\n", + arg); + goto error; + } + + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name, arg); + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + force_omit_home_plugin_path = true; + + if (bt_value_array_append_string_element(run_args, + "--omit-home-plugin-path")) { + print_err_oom(); + goto error; + } + break; + case OPT_RETRY_DURATION: + if (bt_value_array_append_string_element(run_args, + "--retry-duration")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + force_omit_system_plugin_path = true; + + if (bt_value_array_append_string_element(run_args, + "--omit-system-plugin-path")) { + print_err_oom(); + goto error; + } + break; + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + plugin_paths, arg)) { + goto error; + } + + if (bt_value_array_append_string_element(run_args, + "--plugin-path")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_convert_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + case OPT_BEGIN: + case OPT_CLOCK_CYCLES: + case OPT_CLOCK_DATE: + case OPT_CLOCK_FORCE_CORRELATE: + case OPT_CLOCK_GMT: + case OPT_CLOCK_OFFSET: + case OPT_CLOCK_OFFSET_NS: + case OPT_CLOCK_SECONDS: + case OPT_COLOR: + case OPT_DEBUG: + case OPT_DEBUG_INFO: + case OPT_DEBUG_INFO_DIR: + case OPT_DEBUG_INFO_FULL_PATH: + case OPT_DEBUG_INFO_TARGET_PREFIX: + case OPT_END: + case OPT_FIELDS: + case OPT_INPUT_FORMAT: + case OPT_NAMES: + case OPT_NO_DELTA: + case OPT_OUTPUT_FORMAT: + case OPT_OUTPUT: + case OPT_RUN_ARGS: + case OPT_RUN_ARGS_0: + case OPT_STREAM_INTERSECTION: + case OPT_TIMERANGE: + case OPT_VERBOSE: + /* Ignore in this pass */ + break; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Append current component's name if needed */ + ret = convert_append_name_param(cur_comp_dest, cur_name, + cur_name_prefix, run_args, all_names, &source_names, + &filter_names, &sink_names); + if (ret) { + goto error; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + poptFreeContext(pc); + free(arg); + arg = NULL; + + /* + * Second pass: transform the convert-specific options and + * arguments into implicit component instances for the run + * command. + */ + pc = poptGetContext(NULL, argc, (const char **) argv, + convert_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_BEGIN: + if (trimmer_has_begin) { + printf("At --begin option: --begin or --timerange option already specified\n %s\n", + arg); + goto error; + } + + trimmer_has_begin = true; + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "begin", arg); + implicit_trimmer_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_END: + if (trimmer_has_end) { + printf("At --end option: --end or --timerange option already specified\n %s\n", + arg); + goto error; + } + + trimmer_has_end = true; + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "end", arg); + implicit_trimmer_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_TIMERANGE: + { + char *begin; + char *end; + + if (trimmer_has_begin || trimmer_has_end) { + printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", + arg); + goto error; + } + + ret = split_timerange(arg, &begin, &end); + if (ret) { + printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n", + arg); + goto error; + } + + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "begin", begin); + ret |= append_implicit_component_extra_param( + &implicit_trimmer_args, "end", end); + implicit_trimmer_args.exists = true; + free(begin); + free(end); + if (ret) { + goto error; + } + break; + } + case OPT_CLOCK_CYCLES: + append_implicit_component_param( + &implicit_text_args, "clock-cycles", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_DATE: + append_implicit_component_param( + &implicit_text_args, "clock-date", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_FORCE_CORRELATE: + append_implicit_component_param( + &implicit_muxer_args, + "assume-absolute-clock-classes", "yes"); + break; + case OPT_CLOCK_GMT: + append_implicit_component_param( + &implicit_text_args, "clock-gmt", "yes"); + append_implicit_component_param( + &implicit_trimmer_args, "gmt", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_OFFSET: + implicit_ctf_input_args.exists = true; + append_implicit_component_param( + &implicit_ctf_input_args, + "clock-class-offset-s", arg); + break; + case OPT_CLOCK_OFFSET_NS: + implicit_ctf_input_args.exists = true; + append_implicit_component_param( + &implicit_ctf_input_args, + "clock-class-offset-ns", arg); + break; + case OPT_CLOCK_SECONDS: + append_implicit_component_param( + &implicit_text_args, "clock-seconds", "yes"); + implicit_text_args.exists = true; + break; + case OPT_COLOR: + implicit_text_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_text_args, "color", arg); + if (ret) { + goto error; + } + break; + case OPT_DEBUG_INFO: + implicit_debug_info_args.exists = true; + break; + case OPT_DEBUG_INFO_DIR: + implicit_debug_info_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_debug_info_args, "debug-info-dir", arg); + if (ret) { + goto error; + } + break; + case OPT_DEBUG_INFO_FULL_PATH: + implicit_debug_info_args.exists = true; + append_implicit_component_param( + &implicit_debug_info_args, "full-path", "yes"); + break; + case OPT_DEBUG_INFO_TARGET_PREFIX: + implicit_debug_info_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_debug_info_args, + "target-prefix", arg); + if (ret) { + goto error; + } + break; + case OPT_FIELDS: + { + bt_value *fields = fields_from_arg(arg); + + if (!fields) { + goto error; + } + + implicit_text_args.exists = true; + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + fields, "field"); + bt_value_put_ref(fields); + if (ret) { + goto error; + } + break; + } + case OPT_NAMES: + { + bt_value *names = names_from_arg(arg); + + if (!names) { + goto error; + } + + implicit_text_args.exists = true; + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + names, "name"); + bt_value_put_ref(names); + if (ret) { + goto error; + } + break; + } + case OPT_NO_DELTA: + append_implicit_component_param( + &implicit_text_args, "no-delta", "yes"); + implicit_text_args.exists = true; + break; + case OPT_INPUT_FORMAT: + if (got_input_format_opt) { + printf_err("Duplicate --input-format option\n"); + goto error; + } + + got_input_format_opt = true; + + if (strcmp(arg, "ctf") == 0) { + implicit_ctf_input_args.exists = true; + } else if (strcmp(arg, "lttng-live") == 0) { + implicit_lttng_live_args.exists = true; + } else { + printf_err("Unknown legacy input format:\n %s\n", + arg); + goto error; + } + break; + case OPT_OUTPUT_FORMAT: + if (got_output_format_opt) { + printf_err("Duplicate --output-format option\n"); + goto error; + } + + got_output_format_opt = true; + + if (strcmp(arg, "text") == 0) { + implicit_text_args.exists = true; + } else if (strcmp(arg, "ctf") == 0) { + implicit_ctf_output_args.exists = true; + } else if (strcmp(arg, "dummy") == 0) { + implicit_dummy_args.exists = true; + } else if (strcmp(arg, "ctf-metadata") == 0) { + print_ctf_metadata = true; + } else { + printf_err("Unknown legacy output format:\n %s\n", + arg); + goto error; + } + break; + case OPT_OUTPUT: + if (output) { + printf_err("Duplicate --output option\n"); + goto error; + } + + output = strdup(arg); + if (!output) { + print_err_oom(); + goto error; + } + break; + case OPT_RUN_ARGS: + if (print_run_args_0) { + printf_err("Cannot specify --run-args and --run-args-0\n"); + goto error; + } + + print_run_args = true; + break; + case OPT_RUN_ARGS_0: + if (print_run_args) { + printf_err("Cannot specify --run-args and --run-args-0\n"); + goto error; + } + + print_run_args_0 = true; + break; + case OPT_STREAM_INTERSECTION: + /* + * Applies to all traces implementing the trace-info + * query. + */ + stream_intersection_mode = true; + break; + case OPT_VERBOSE: + if (*log_level != 'V' && *log_level != 'D') { + *log_level = 'I'; + } + break; + case OPT_DEBUG: + *log_level = 'V'; + break; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* + * Legacy behaviour: --verbose used to make the `text` output + * format print more information. --verbose is now equivalent to + * the INFO log level, which is why we compare to 'I' here. + */ + if (*log_level == 'I') { + append_implicit_component_param(&implicit_text_args, + "verbose", "yes"); + } + + /* + * Append home and system plugin paths now that we possibly got + * --plugin-path. + */ + if (append_home_and_system_plugin_paths(plugin_paths, + force_omit_system_plugin_path, + force_omit_home_plugin_path)) { + goto error; + } + + /* Consume and keep leftover arguments */ + while ((leftover = poptGetArg(pc))) { + bt_value_status status = bt_value_array_append_string_element(leftovers, leftover); + if (status != BT_VALUE_STATUS_OK) { + print_err_oom(); + goto error; + } + } + + /* Print CTF metadata or print LTTng live sessions */ + if (print_ctf_metadata) { + const bt_value *bt_val_leftover; + + if (bt_value_array_is_empty(leftovers)) { + printf_err("--output-format=ctf-metadata specified without a path\n"); + goto error; + } + + if (bt_value_array_get_size(leftovers) > 1) { + printf_err("Too many paths specified for --output-format=ctf-metadata\n"); + goto error; + } + + cfg = bt_config_print_ctf_metadata_create(plugin_paths); + if (!cfg) { + goto error; + } + + bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + g_string_assign(cfg->cmd_data.print_ctf_metadata.path, + bt_value_string_get(bt_val_leftover)); + + if (output) { + g_string_assign( + cfg->cmd_data.print_ctf_metadata.output_path, + output); + } + + goto end; + } + + /* + * If -o ctf was specified, make sure an output path (--output) + * was also specified. --output does not imply -o ctf because + * it's also used for the default, implicit -o text if -o ctf + * is not specified. + */ + if (implicit_ctf_output_args.exists) { + if (!output) { + printf_err("--output-format=ctf specified without --output (trace output path)\n"); + goto error; + } + + /* + * At this point we know that -o ctf AND --output were + * specified. Make sure that no options were specified + * which would imply -o text because --output would be + * ambiguous in this case. For example, this is wrong: + * + * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace + * + * because --names=all implies -o text, and --output + * could apply to both the sink.text.pretty and + * sink.ctf.fs implicit components. + */ + if (implicit_text_args.exists) { + printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n"); + goto error; + } + } + + /* + * If -o dummy and -o ctf were not specified, and if there are + * no explicit sink components, then use an implicit + * `sink.text.pretty` component. + */ + if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists && + !sink_names) { + implicit_text_args.exists = true; + } + + /* + * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's + * `path` parameter if --output was specified. + */ + if (output) { + if (implicit_text_args.exists) { + append_implicit_component_extra_param(&implicit_text_args, + "path", output); + } else if (implicit_ctf_output_args.exists) { + append_implicit_component_extra_param(&implicit_ctf_output_args, + "path", output); + } + } + + /* Decide where the leftover argument(s) go */ + if (bt_value_array_get_size(leftovers) > 0) { + if (implicit_lttng_live_args.exists) { + const bt_value *bt_val_leftover; + + if (bt_value_array_get_size(leftovers) > 1) { + printf_err("Too many URLs specified for --output-format=lttng-live\n"); + goto error; + } + + bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + lttng_live_url_parts = + bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover), + error_buf, sizeof(error_buf)); + if (!lttng_live_url_parts.proto) { + printf_err("Invalid LTTng live URL format: %s\n", + error_buf); + goto error; + } + + if (!lttng_live_url_parts.session_name) { + /* Print LTTng live sessions */ + cfg = bt_config_print_lttng_live_sessions_create( + plugin_paths); + if (!cfg) { + goto error; + } + + g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, + bt_value_string_get(bt_val_leftover)); + + if (output) { + g_string_assign( + cfg->cmd_data.print_lttng_live_sessions.output_path, + output); + } + + goto end; + } + + ret = append_implicit_component_extra_param( + &implicit_lttng_live_args, "url", + bt_value_string_get(bt_val_leftover)); + if (ret) { + goto error; + } + + ret = append_implicit_component_extra_param( + &implicit_lttng_live_args, + "session-not-found-action", "end"); + if (ret) { + goto error; + } + } else { + /* + * Create one source.ctf.fs component, pass it an array + * with the leftovers. + * Note that it still has to be named later. + */ + implicit_ctf_input_args.exists = true; + ret = append_parameter_to_args(implicit_ctf_input_args.extra_params, + "paths", leftovers); + if (ret) { + goto error; + } + } + } + + /* + * Ensure mutual exclusion between implicit `source.ctf.fs` and + * `source.ctf.lttng-live` components. + */ + if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) { + printf_err("Cannot create both implicit `%s` and `%s` components\n", + implicit_ctf_input_args.comp_arg->str, + implicit_lttng_live_args.comp_arg->str); + goto error; + } + + /* + * If the implicit `source.ctf.fs` or `source.ctf.lttng-live` + * components exists, make sure there's at least one leftover + * (which is the path or URL). + */ + if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) { + printf_err("Missing path for implicit `%s` component\n", + implicit_ctf_input_args.comp_arg->str); + goto error; + } + + if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) { + printf_err("Missing URL for implicit `%s` component\n", + implicit_lttng_live_args.comp_arg->str); + goto error; + } + + /* Assign names to implicit components */ + ret = assign_name_to_implicit_component(&implicit_ctf_input_args, + "source-ctf-fs", all_names, &source_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_lttng_live_args, + "lttng-live", all_names, &source_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_text_args, + "pretty", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_ctf_output_args, + "sink-ctf-fs", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_dummy_args, + "dummy", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_muxer_args, + "muxer", all_names, NULL, false); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_trimmer_args, + "trimmer", all_names, NULL, false); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_debug_info_args, + "debug-info", all_names, NULL, false); + if (ret) { + goto error; + } + + /* Make sure there's at least one source and one sink */ + if (!source_names) { + printf_err("No source component\n"); + goto error; + } + + if (!sink_names) { + printf_err("No sink component\n"); + goto error; + } + + /* + * Prepend the muxer, the trimmer, and the debug info to the + * filter chain so that we have: + * + * sources -> muxer -> [trimmer] -> [debug info] -> + * [user filters] -> sinks + */ + if (implicit_debug_info_args.exists) { + if (g_list_prepend_gstring(&filter_names, + implicit_debug_info_args.name_arg->str)) { + goto error; + } + } + + if (implicit_trimmer_args.exists) { + if (g_list_prepend_gstring(&filter_names, + implicit_trimmer_args.name_arg->str)) { + goto error; + } + } + + if (g_list_prepend_gstring(&filter_names, + implicit_muxer_args.name_arg->str)) { + goto error; + } + + /* + * Append the equivalent run arguments for the implicit + * components. + */ + ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_lttng_live_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_text_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_ctf_output_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_dummy_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_muxer_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_trimmer_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_debug_info_args, + run_args); + if (ret) { + goto error; + } + + /* Auto-connect components */ + ret = convert_auto_connect(run_args, source_names, filter_names, + sink_names); + if (ret) { + printf_err("Cannot auto-connect components\n"); + goto error; + } + + /* + * We have all the run command arguments now. Depending on + * --run-args, we pass this to the run command or print them + * here. + */ + if (print_run_args || print_run_args_0) { + if (stream_intersection_mode) { + printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n"); + goto error; + } + + for (i = 0; i < bt_value_array_get_size(run_args); i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index(run_args, + i); + const char *arg; + GString *quoted = NULL; + const char *arg_to_print; + + BT_ASSERT(arg_value); + arg = bt_value_string_get(arg_value); + + if (print_run_args) { + quoted = bt_common_shell_quote(arg, true); + if (!quoted) { + goto error; + } + + arg_to_print = quoted->str; + } else { + arg_to_print = arg; + } + + printf("%s", arg_to_print); + + if (quoted) { + g_string_free(quoted, TRUE); + } + + if (i < bt_value_array_get_size(run_args) - 1) { + if (print_run_args) { + putchar(' '); + } else { + putchar('\0'); + } + } + } + + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + cfg = bt_config_run_from_args_array(run_args, retcode, + force_omit_system_plugin_path, + force_omit_home_plugin_path, + initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode; + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + free(output); + + if (cur_name) { + g_string_free(cur_name, TRUE); + } + + if (cur_name_prefix) { + g_string_free(cur_name_prefix, TRUE); + } + + bt_value_put_ref(run_args); + bt_value_put_ref(all_names); + destroy_glist_of_gstring(source_names); + destroy_glist_of_gstring(filter_names); + destroy_glist_of_gstring(sink_names); + bt_value_put_ref(leftovers); + finalize_implicit_component_args(&implicit_ctf_input_args); + finalize_implicit_component_args(&implicit_ctf_output_args); + finalize_implicit_component_args(&implicit_lttng_live_args); + finalize_implicit_component_args(&implicit_dummy_args); + finalize_implicit_component_args(&implicit_text_args); + finalize_implicit_component_args(&implicit_debug_info_args); + finalize_implicit_component_args(&implicit_muxer_args); + finalize_implicit_component_args(&implicit_trimmer_args); + bt_value_put_ref(plugin_paths); + bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); + return cfg; +} + +/* + * Prints the Babeltrace 2.x general usage. + */ +static +void print_gen_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "General options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, " -l, --log-level=LVL Set all log levels to LVL (`N`, `V`, `D`,\n"); + fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); + fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Available commands:\n"); + fprintf(fp, "\n"); + fprintf(fp, " convert Convert and trim traces (default)\n"); + fprintf(fp, " help Get help for a plugin or a component class\n"); + fprintf(fp, " list-plugins List available plugins and their content\n"); + fprintf(fp, " query Query objects from a component class\n"); + fprintf(fp, " run Build a processing graph and run it\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n"); +} + +static +char log_level_from_arg(const char *arg) +{ + char level = 'U'; + + if (strcmp(arg, "VERBOSE") == 0 || + strcmp(arg, "V") == 0) { + level = 'V'; + } else if (strcmp(arg, "DEBUG") == 0 || + strcmp(arg, "D") == 0) { + level = 'D'; + } else if (strcmp(arg, "INFO") == 0 || + strcmp(arg, "I") == 0) { + level = 'I'; + } else if (strcmp(arg, "WARN") == 0 || + strcmp(arg, "WARNING") == 0 || + strcmp(arg, "W") == 0) { + level = 'W'; + } else if (strcmp(arg, "ERROR") == 0 || + strcmp(arg, "E") == 0) { + level = 'E'; + } else if (strcmp(arg, "FATAL") == 0 || + strcmp(arg, "F") == 0) { + level = 'F'; + } else if (strcmp(arg, "NONE") == 0 || + strcmp(arg, "N") == 0) { + level = 'N'; + } + + return level; +} + +struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + struct bt_config *config = NULL; + int i; + const char **command_argv = NULL; + int command_argc = -1; + const char *command_name = NULL; + char log_level = 'U'; + + enum command_type { + COMMAND_TYPE_NONE = -1, + COMMAND_TYPE_RUN = 0, + COMMAND_TYPE_CONVERT, + COMMAND_TYPE_LIST_PLUGINS, + COMMAND_TYPE_HELP, + COMMAND_TYPE_QUERY, + } command_type = COMMAND_TYPE_NONE; + + *retcode = -1; + + if (!initial_plugin_paths) { + initial_plugin_paths = bt_value_array_create(); + if (!initial_plugin_paths) { + *retcode = 1; + goto end; + } + } else { + bt_value_get_ref(initial_plugin_paths); + } + + if (argc <= 1) { + print_version(); + puts(""); + print_gen_usage(stdout); + goto end; + } + + for (i = 1; i < argc; i++) { + const char *cur_arg = argv[i]; + const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1]; + + if (strcmp(cur_arg, "-d") == 0 || + strcmp(cur_arg, "--debug") == 0) { + log_level = 'V'; + } else if (strcmp(cur_arg, "-v") == 0 || + strcmp(cur_arg, "--verbose") == 0) { + if (log_level != 'V' && log_level != 'D') { + /* + * Legacy: do not override a previous + * --debug because --verbose and --debug + * can be specified together (in this + * case we want the lowest log level to + * apply, VERBOSE). + */ + log_level = 'I'; + } + } else if (strcmp(cur_arg, "--log-level") == 0 || + strcmp(cur_arg, "-l") == 0) { + if (!next_arg) { + printf_err("Missing log level value for --log-level option\n"); + *retcode = 1; + goto end; + } + + log_level = log_level_from_arg(next_arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + next_arg); + *retcode = 1; + goto end; + } + + i++; + } else if (strncmp(cur_arg, "--log-level=", 12) == 0) { + const char *arg = &cur_arg[12]; + + log_level = log_level_from_arg(arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + arg); + *retcode = 1; + goto end; + } + } else if (strncmp(cur_arg, "-l", 2) == 0) { + const char *arg = &cur_arg[2]; + + log_level = log_level_from_arg(arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + arg); + *retcode = 1; + goto end; + } + } else if (strcmp(cur_arg, "-V") == 0 || + strcmp(cur_arg, "--version") == 0) { + print_version(); + goto end; + } else if (strcmp(cur_arg, "-h") == 0 || + strcmp(cur_arg, "--help") == 0) { + print_gen_usage(stdout); + goto end; + } else { + /* + * First unknown argument: is it a known command + * name? + */ + command_argv = &argv[i]; + command_argc = argc - i; + + if (strcmp(cur_arg, "convert") == 0) { + command_type = COMMAND_TYPE_CONVERT; + } else if (strcmp(cur_arg, "list-plugins") == 0) { + command_type = COMMAND_TYPE_LIST_PLUGINS; + } else if (strcmp(cur_arg, "help") == 0) { + command_type = COMMAND_TYPE_HELP; + } else if (strcmp(cur_arg, "query") == 0) { + command_type = COMMAND_TYPE_QUERY; + } else if (strcmp(cur_arg, "run") == 0) { + command_type = COMMAND_TYPE_RUN; + } else { + /* + * Unknown argument, but not a known + * command name: assume the default + * `convert` command. + */ + command_type = COMMAND_TYPE_CONVERT; + command_name = "convert"; + command_argv = &argv[i - 1]; + command_argc = argc - i + 1; + } + break; + } + } + + if (command_type == COMMAND_TYPE_NONE) { + /* + * We only got non-help, non-version general options + * like --verbose and --debug, without any other + * arguments, so we can't do anything useful: print the + * usage and quit. + */ + print_gen_usage(stdout); + goto end; + } + + BT_ASSERT(command_argv); + BT_ASSERT(command_argc >= 0); + + switch (command_type) { + case COMMAND_TYPE_RUN: + config = bt_config_run_from_args(command_argc, command_argv, + retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_CONVERT: + config = bt_config_convert_from_args(command_argc, command_argv, + retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, + initial_plugin_paths, &log_level); + break; + case COMMAND_TYPE_LIST_PLUGINS: + config = bt_config_list_plugins_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_HELP: + config = bt_config_help_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_QUERY: + config = bt_config_query_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + default: + abort(); + } + + if (config) { + if (log_level == 'U') { + log_level = 'W'; + } + + config->log_level = log_level; + config->command_name = command_name; + } + +end: + bt_value_put_ref(initial_plugin_paths); + return config; +} diff --git a/src/cli/babeltrace2-cfg-cli-args.h b/src/cli/babeltrace2-cfg-cli-args.h new file mode 100644 index 00000000..8a458c22 --- /dev/null +++ b/src/cli/babeltrace2-cfg-cli-args.h @@ -0,0 +1,41 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_H + +/* + * Copyright 2016-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include + +#include "babeltrace2-cfg.h" + +struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_H */ diff --git a/src/cli/babeltrace2-cfg.c b/src/cli/babeltrace2-cfg.c new file mode 100644 index 00000000..4c764a9e --- /dev/null +++ b/src/cli/babeltrace2-cfg.c @@ -0,0 +1,101 @@ +/* + * Babeltrace trace converter - parameter parsing + * + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/common.h" +#include +#include +#include "babeltrace2-cfg.h" + +static +void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + +/* + * Extracts the various paths from the string arg, delimited by ':', + * and appends them to the array value object plugin_paths. + */ +int bt_config_append_plugin_paths( + bt_value *plugin_paths, const char *arg) +{ + int ret = 0; + GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); + size_t i; + + if (!dirs) { + ret = -1; + goto end; + } + + ret = bt_common_append_plugin_path_dirs(arg, dirs); + if (ret) { + ret = -1; + goto end; + } + + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); + + ret = bt_value_array_append_string_element( + plugin_paths, dir->str); + if (ret != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + } + +end: + g_ptr_array_free(dirs, TRUE); + return ret; +} + +void bt_config_connection_destroy(struct bt_config_connection *connection) +{ + if (!connection) { + return; + } + + if (connection->upstream_comp_name) { + g_string_free(connection->upstream_comp_name, TRUE); + } + + if (connection->downstream_comp_name) { + g_string_free(connection->downstream_comp_name, TRUE); + } + + if (connection->upstream_port_glob) { + g_string_free(connection->upstream_port_glob, TRUE); + } + + if (connection->downstream_port_glob) { + g_string_free(connection->downstream_port_glob, TRUE); + } + + if (connection->arg) { + g_string_free(connection->arg, TRUE); + } + + g_free(connection); +} diff --git a/src/cli/babeltrace2-cfg.h b/src/cli/babeltrace2-cfg.h new file mode 100644 index 00000000..a5cc7fcf --- /dev/null +++ b/src/cli/babeltrace2-cfg.h @@ -0,0 +1,142 @@ +#ifndef CLI_BABELTRACE_CFG_H +#define CLI_BABELTRACE_CFG_H + +/* + * Babeltrace trace converter - CLI tool's configuration + * + * Copyright 2016-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include + +enum bt_config_command { + BT_CONFIG_COMMAND_RUN, + BT_CONFIG_COMMAND_PRINT_CTF_METADATA, + BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, + BT_CONFIG_COMMAND_LIST_PLUGINS, + BT_CONFIG_COMMAND_HELP, + BT_CONFIG_COMMAND_QUERY, +}; + +struct bt_config_component { + bt_object base; + bt_component_class_type type; + GString *plugin_name; + GString *comp_cls_name; + bt_value *params; + GString *instance_name; +}; + +struct bt_config_connection { + GString *upstream_comp_name; + GString *downstream_comp_name; + GString *upstream_port_glob; + GString *downstream_port_glob; + GString *arg; +}; + +struct bt_config { + bt_object base; + bool debug; + bool verbose; + bt_value *plugin_paths; + bool omit_system_plugin_path; + bool omit_home_plugin_path; + bool command_needs_plugins; + const char *command_name; + char log_level; + enum bt_config_command command; + union { + /* BT_CONFIG_COMMAND_RUN */ + struct { + /* Array of pointers to struct bt_config_component */ + GPtrArray *sources; + + /* Array of pointers to struct bt_config_component */ + GPtrArray *filters; + + /* Array of pointers to struct bt_config_component */ + GPtrArray *sinks; + + /* Array of pointers to struct bt_config_connection */ + GPtrArray *connections; + + /* + * Number of microseconds to sleep when we need + * to retry to run the graph. + */ + uint64_t retry_duration_us; + + /* + * Whether or not to trim the source trace to the + * intersection of its streams. + */ + bool stream_intersection_mode; + } run; + + /* BT_CONFIG_COMMAND_HELP */ + struct { + struct bt_config_component *cfg_component; + } help; + + /* BT_CONFIG_COMMAND_QUERY */ + struct { + GString *object; + struct bt_config_component *cfg_component; + } query; + + /* BT_CONFIG_COMMAND_PRINT_CTF_METADATA */ + struct { + GString *path; + GString *output_path; + } print_ctf_metadata; + + /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ + struct { + GString *url; + GString *output_path; + } print_lttng_live_sessions; + } cmd_data; +}; + +static inline +struct bt_config_component *bt_config_get_component(GPtrArray *array, + size_t index) +{ + struct bt_config_component *comp = g_ptr_array_index(array, index); + + bt_object_get_ref(comp); + return comp; +} + +int bt_config_append_plugin_paths(bt_value *plugin_paths, + const char *arg); + +void bt_config_connection_destroy(struct bt_config_connection *connection); + +#endif /* CLI_BABELTRACE_CFG_H */ diff --git a/src/cli/babeltrace2-log.c b/src/cli/babeltrace2-log.c new file mode 100644 index 00000000..59f34e5d --- /dev/null +++ b/src/cli/babeltrace2-log.c @@ -0,0 +1,168 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include + +static +void print_usage(FILE *fp) +{ + fprintf(stderr, "Usage: babeltrace2-log [OPTIONS] OUTPUT-PATH\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -t, --with-timestamps Extract timestamps from lines and map them to\n"); + fprintf(stderr, " a CTF clock class\n"); +} + +static +int parse_params(int argc, char *argv[], char **output_path, + bool *no_extract_ts) +{ + enum { + OPT_WITH_TIMESTAMPS = 1, + OPT_HELP = 2, + }; + static struct poptOption opts[] = { + { "with-timestamps", 't', POPT_ARG_NONE, NULL, OPT_WITH_TIMESTAMPS, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { NULL, '\0', 0, NULL, 0, NULL, NULL }, + }; + poptContext pc = NULL; + int opt; + int ret = 0; + const char *leftover; + + *no_extract_ts = true; + pc = poptGetContext(NULL, argc, (const char **) argv, opts, 0); + if (!pc) { + fprintf(stderr, "Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + switch (opt) { + case OPT_HELP: + print_usage(stdout); + goto end; + case OPT_WITH_TIMESTAMPS: + *no_extract_ts = false; + break; + default: + fprintf(stderr, "Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + } + + if (opt < -1) { + fprintf(stderr, "While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (!leftover) { + fprintf(stderr, "Command line error: Missing output path\n"); + print_usage(stderr); + goto error; + } + + *output_path = strdup(leftover); + BT_ASSERT(*output_path); + goto end; + +error: + ret = 1; + +end: + if (pc) { + poptFreeContext(pc); + } + + return ret; +} + +int main(int argc, char *argv[]) +{ + char *output_path = NULL; + bool no_extract_ts; + int retcode; + GError *error = NULL; + gchar *bt_argv[] = { + BT_CLI_PATH, + "run", + "--component", + "dmesg:src.text.dmesg", + "--params", + NULL, /* no-extract-timestamp=? placeholder */ + "--component", + "ctf:sink.ctf.fs", + "--key", + "path", + "--value", + NULL, /* output path placeholder */ + "--params", + "single-trace=yes", + "--connect", + "dmesg:ctf", + NULL, /* sentinel */ + }; + + retcode = parse_params(argc, argv, &output_path, &no_extract_ts); + if (retcode) { + goto end; + } + + if (no_extract_ts) { + bt_argv[5] = "no-extract-timestamp=yes"; + } else { + bt_argv[5] = "no-extract-timestamp=no"; + } + + bt_argv[11] = output_path; + (void) g_spawn_sync(NULL, bt_argv, NULL, + G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, + NULL, NULL, &retcode, &error); + + if (error) { + fprintf(stderr, "Failed to execute \"%s\": %s (%d)\n", + bt_argv[0], error->message, error->code); + } + +end: + free(output_path); + + if (error) { + g_error_free(error); + } + + return retcode; +} diff --git a/src/cli/babeltrace2.c b/src/cli/babeltrace2.c new file mode 100644 index 00000000..b4285e5b --- /dev/null +++ b/src/cli/babeltrace2.c @@ -0,0 +1,2950 @@ +/* + * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation + * + * Author: Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CLI" +#include "logging.h" + +#include +#include "common/common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-default.h" + +#define ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH "BABELTRACE_CLI_WARN_COMMAND_NAME_DIRECTORY_CLASH" +#define ENV_BABELTRACE_CLI_LOG_LEVEL "BABELTRACE_CLI_LOG_LEVEL" +#define NSEC_PER_SEC 1000000000LL + +/* + * Known environment variable names for the log levels of the project's + * modules. + */ +static const char* log_level_env_var_names[] = { + "BABELTRACE_COMMON_LOG_LEVEL", + "BABELTRACE_COMPAT_LOG_LEVEL", + "BABELTRACE_CTFSER_LOG_LEVEL", + "BABELTRACE_FD_CACHE_LOG_LEVEL", + "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL", + "BABELTRACE_PYTHON_BT2_LOG_LEVEL", + "BABELTRACE_SINK_CTF_FS_LOG_LEVEL", + "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL", + "BABELTRACE_SRC_CTF_FS_LOG_LEVEL", + "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL", + "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL", + NULL, +}; + +/* Application's processing graph (weak) */ +static bt_graph *the_graph; +static bt_query_executor *the_query_executor; +static bool canceled = false; + +GPtrArray *loaded_plugins; + +#ifdef __MINGW32__ + +#include + +static +BOOL WINAPI signal_handler(DWORD signal) { + if (the_graph) { + bt_graph_cancel(the_graph); + } + + canceled = true; + + return TRUE; +} + +static +void set_signal_handler(void) +{ + if (!SetConsoleCtrlHandler(signal_handler, TRUE)) { + BT_LOGE("Failed to set the ctrl+c handler."); + } +} + +#else /* __MINGW32__ */ + +static +void signal_handler(int signum) +{ + if (signum != SIGINT) { + return; + } + + if (the_graph) { + bt_graph_cancel(the_graph); + } + + if (the_query_executor) { + bt_query_executor_cancel(the_query_executor); + } + + canceled = true; +} + +static +void set_signal_handler(void) +{ + struct sigaction new_action, old_action; + + new_action.sa_handler = signal_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGINT, NULL, &old_action); + + if (old_action.sa_handler != SIG_IGN) { + sigaction(SIGINT, &new_action, NULL); + } +} + +#endif /* __MINGW32__ */ + +static +void init_static_data(void) +{ + loaded_plugins = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); +} + +static +void fini_static_data(void) +{ + g_ptr_array_free(loaded_plugins, TRUE); +} + +static +int create_the_query_executor(void) +{ + int ret = 0; + + the_query_executor = bt_query_executor_create(); + if (!the_query_executor) { + BT_LOGE_STR("Cannot create a query executor."); + ret = -1; + } + + return ret; +} + +static +void destroy_the_query_executor(void) +{ + BT_QUERY_EXECUTOR_PUT_REF_AND_RESET(the_query_executor); +} + +static +int query(const bt_component_class *comp_cls, const char *obj, + const bt_value *params, const bt_value **user_result, + const char **fail_reason) +{ + const bt_value *result = NULL; + bt_query_executor_status status; + *fail_reason = "unknown error"; + int ret = 0; + + BT_ASSERT(fail_reason); + BT_ASSERT(user_result); + ret = create_the_query_executor(); + if (ret) { + /* create_the_query_executor() logs errors */ + goto end; + } + + if (canceled) { + BT_LOGI("Canceled by user before executing the query: " + "comp-cls-addr=%p, comp-cls-name=\"%s\", " + "query-obj=\"%s\"", comp_cls, + bt_component_class_get_name(comp_cls), obj); + *fail_reason = "canceled by user"; + goto error; + } + + while (true) { + status = bt_query_executor_query(the_query_executor, + comp_cls, obj, params, &result); + switch (status) { + case BT_QUERY_EXECUTOR_STATUS_OK: + goto ok; + case BT_QUERY_EXECUTOR_STATUS_AGAIN: + { + const uint64_t sleep_time_us = 100000; + + /* Wait 100 ms and retry */ + BT_LOGV("Got BT_QUERY_EXECUTOR_STATUS_AGAIN: sleeping: " + "time-us=%" PRIu64, sleep_time_us); + + if (usleep(sleep_time_us)) { + if (bt_query_executor_is_canceled(the_query_executor)) { + BT_LOGI("Query was canceled by user: " + "comp-cls-addr=%p, comp-cls-name=\"%s\", " + "query-obj=\"%s\"", comp_cls, + bt_component_class_get_name(comp_cls), + obj); + *fail_reason = "canceled by user"; + goto error; + } + } + + continue; + } + case BT_QUERY_EXECUTOR_STATUS_CANCELED: + *fail_reason = "canceled by user"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_ERROR: + goto error; + case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: + *fail_reason = "invalid or unknown query object"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: + *fail_reason = "invalid query parameters"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: + *fail_reason = "unsupported action"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_NOMEM: + *fail_reason = "not enough memory"; + goto error; + default: + BT_LOGF("Unknown query status: status=%d", status); + abort(); + } + } + +ok: + *user_result = result; + result = NULL; + goto end; + +error: + ret = -1; + +end: + destroy_the_query_executor(); + bt_value_put_ref(result); + return ret; +} + +static +const bt_plugin *find_plugin(const char *name) +{ + int i; + const bt_plugin *plugin = NULL; + + BT_ASSERT(name); + BT_LOGD("Finding plugin: name=\"%s\"", name); + + for (i = 0; i < loaded_plugins->len; i++) { + plugin = g_ptr_array_index(loaded_plugins, i); + + if (strcmp(name, bt_plugin_get_name(plugin)) == 0) { + break; + } + + plugin = NULL; + } + + if (BT_LOG_ON_DEBUG) { + if (plugin) { + BT_LOGD("Found plugin: plugin-addr=%p", plugin); + } else { + BT_LOGD("Cannot find plugin."); + } + } + + bt_plugin_get_ref(plugin); + return plugin; +} + +typedef const void *(*plugin_borrow_comp_cls_func_t)( + const bt_plugin *, const char *); + +static +const void *find_component_class_from_plugin(const char *plugin_name, + const char *comp_class_name, + plugin_borrow_comp_cls_func_t plugin_borrow_comp_cls_func) +{ + const void *comp_class = NULL; + const bt_plugin *plugin; + + BT_LOGD("Finding component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\"", plugin_name, comp_class_name); + + plugin = find_plugin(plugin_name); + if (!plugin) { + goto end; + } + + comp_class = plugin_borrow_comp_cls_func(plugin, comp_class_name); + bt_object_get_ref(comp_class); + BT_PLUGIN_PUT_REF_AND_RESET(plugin); + +end: + if (BT_LOG_ON_DEBUG) { + if (comp_class) { + BT_LOGD("Found component class: comp-cls-addr=%p", + comp_class); + } else { + BT_LOGD("Cannot find source component class."); + } + } + + return comp_class; +} + +static +const bt_component_class_source *find_source_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin( + plugin_name, comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_source_component_class_by_name_const); +} + +static +const bt_component_class_filter *find_filter_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin( + plugin_name, comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_filter_component_class_by_name_const); +} + +static +const bt_component_class_sink *find_sink_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin(plugin_name, + comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_sink_component_class_by_name_const); +} + +static +const bt_component_class *find_component_class(const char *plugin_name, + const char *comp_class_name, + bt_component_class_type comp_class_type) +{ + const bt_component_class *comp_cls = NULL; + + switch (comp_class_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + comp_cls = bt_component_class_source_as_component_class_const(find_source_component_class(plugin_name, comp_class_name)); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + comp_cls = bt_component_class_filter_as_component_class_const(find_filter_component_class(plugin_name, comp_class_name)); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + comp_cls = bt_component_class_sink_as_component_class_const(find_sink_component_class(plugin_name, comp_class_name)); + break; + default: + abort(); + } + + return comp_cls; +} + +static +void print_indent(FILE *fp, size_t indent) +{ + size_t i; + + for (i = 0; i < indent; i++) { + fprintf(fp, " "); + } +} + +static +const char *component_type_str(bt_component_class_type type) +{ + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + return "source"; + case BT_COMPONENT_CLASS_TYPE_SINK: + return "sink"; + case BT_COMPONENT_CLASS_TYPE_FILTER: + return "filter"; + default: + return "(unknown)"; + } +} + +static +void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, + const char *comp_cls_name, bt_component_class_type type) +{ + GString *shell_plugin_name = NULL; + GString *shell_comp_cls_name = NULL; + + shell_plugin_name = bt_common_shell_quote(plugin_name, false); + if (!shell_plugin_name) { + goto end; + } + + shell_comp_cls_name = bt_common_shell_quote(comp_cls_name, false); + if (!shell_comp_cls_name) { + goto end; + } + + fprintf(fh, "'%s%s%s%s.%s%s%s.%s%s%s'", + bt_common_color_bold(), + bt_common_color_fg_cyan(), + component_type_str(type), + bt_common_color_fg_default(), + bt_common_color_fg_blue(), + shell_plugin_name->str, + bt_common_color_fg_default(), + bt_common_color_fg_yellow(), + shell_comp_cls_name->str, + bt_common_color_reset()); + +end: + if (shell_plugin_name) { + g_string_free(shell_plugin_name, TRUE); + } + + if (shell_comp_cls_name) { + g_string_free(shell_comp_cls_name, TRUE); + } +} + +static +void print_value(FILE *, const bt_value *, size_t); + +static +void print_value_rec(FILE *, const bt_value *, size_t); + +struct print_map_value_data { + size_t indent; + FILE *fp; +}; + +static +bt_bool print_map_value(const char *key, const bt_value *object, + void *data) +{ + struct print_map_value_data *print_map_value_data = data; + + print_indent(print_map_value_data->fp, print_map_value_data->indent); + fprintf(print_map_value_data->fp, "%s: ", key); + BT_ASSERT(object); + + if (bt_value_is_array(object) && + bt_value_array_is_empty(object)) { + fprintf(print_map_value_data->fp, "[ ]\n"); + return true; + } + + if (bt_value_is_map(object) && + bt_value_map_is_empty(object)) { + fprintf(print_map_value_data->fp, "{ }\n"); + return true; + } + + if (bt_value_is_array(object) || + bt_value_is_map(object)) { + fprintf(print_map_value_data->fp, "\n"); + } + + print_value_rec(print_map_value_data->fp, object, + print_map_value_data->indent + 2); + return BT_TRUE; +} + +static +void print_value_rec(FILE *fp, const bt_value *value, size_t indent) +{ + bt_bool bool_val; + int64_t int_val; + uint64_t uint_val; + double dbl_val; + const char *str_val; + int size; + int i; + + if (!value) { + return; + } + + switch (bt_value_get_type(value)) { + case BT_VALUE_TYPE_NULL: + fprintf(fp, "%snull%s\n", bt_common_color_bold(), + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_BOOL: + bool_val = bt_value_bool_get(value); + fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), + bt_common_color_fg_cyan(), bool_val ? "yes" : "no", + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_UNSIGNED_INTEGER: + uint_val = bt_value_unsigned_integer_get(value); + fprintf(fp, "%s%s%" PRIu64 "%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), uint_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_SIGNED_INTEGER: + int_val = bt_value_signed_integer_get(value); + fprintf(fp, "%s%s%" PRId64 "%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), int_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_REAL: + dbl_val = bt_value_real_get(value); + fprintf(fp, "%s%s%lf%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), dbl_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_STRING: + str_val = bt_value_string_get(value); + fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), + bt_common_color_fg_green(), str_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_ARRAY: + size = bt_value_array_get_size(value); + if (size < 0) { + goto error; + } + + if (size == 0) { + print_indent(fp, indent); + fprintf(fp, "[ ]\n"); + break; + } + + for (i = 0; i < size; i++) { + const bt_value *element = + bt_value_array_borrow_element_by_index_const( + value, i); + + if (!element) { + goto error; + } + print_indent(fp, indent); + fprintf(fp, "- "); + + if (bt_value_is_array(element) && + bt_value_array_is_empty(element)) { + fprintf(fp, "[ ]\n"); + continue; + } + + if (bt_value_is_map(element) && + bt_value_map_is_empty(element)) { + fprintf(fp, "{ }\n"); + continue; + } + + if (bt_value_is_array(element) || + bt_value_is_map(element)) { + fprintf(fp, "\n"); + } + + print_value_rec(fp, element, indent + 2); + } + break; + case BT_VALUE_TYPE_MAP: + { + struct print_map_value_data data = { + .indent = indent, + .fp = fp, + }; + + if (bt_value_map_is_empty(value)) { + print_indent(fp, indent); + fprintf(fp, "{ }\n"); + break; + } + + bt_value_map_foreach_entry_const(value, print_map_value, &data); + break; + } + default: + abort(); + } + return; + +error: + BT_LOGE("Error printing value of type %s.", + bt_common_value_type_string(bt_value_get_type(value))); +} + +static +void print_value(FILE *fp, const bt_value *value, size_t indent) +{ + if (!bt_value_is_array(value) && !bt_value_is_map(value)) { + print_indent(fp, indent); + } + + print_value_rec(fp, value, indent); +} + +static +void print_bt_config_component(struct bt_config_component *bt_config_component) +{ + fprintf(stderr, " "); + print_plugin_comp_cls_opt(stderr, bt_config_component->plugin_name->str, + bt_config_component->comp_cls_name->str, + bt_config_component->type); + fprintf(stderr, ":\n"); + + if (bt_config_component->instance_name->len > 0) { + fprintf(stderr, " Name: %s\n", + bt_config_component->instance_name->str); + } + + fprintf(stderr, " Parameters:\n"); + print_value(stderr, bt_config_component->params, 8); +} + +static +void print_bt_config_components(GPtrArray *array) +{ + size_t i; + + for (i = 0; i < array->len; i++) { + struct bt_config_component *cfg_component = + bt_config_get_component(array, i); + print_bt_config_component(cfg_component); + BT_OBJECT_PUT_REF_AND_RESET(cfg_component); + } +} + +static +void print_plugin_paths(const bt_value *plugin_paths) +{ + fprintf(stderr, " Plugin paths:\n"); + print_value(stderr, plugin_paths, 4); +} + +static +void print_cfg_run(struct bt_config *cfg) +{ + size_t i; + + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Source component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sources); + + if (cfg->cmd_data.run.filters->len > 0) { + fprintf(stderr, " Filter component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.filters); + } + + fprintf(stderr, " Sink component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sinks); + fprintf(stderr, " Connections:\n"); + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *cfg_connection = + g_ptr_array_index(cfg->cmd_data.run.connections, + i); + + fprintf(stderr, " %s%s%s -> %s%s%s\n", + cfg_connection->upstream_comp_name->str, + cfg_connection->upstream_port_glob->len > 0 ? "." : "", + cfg_connection->upstream_port_glob->str, + cfg_connection->downstream_comp_name->str, + cfg_connection->downstream_port_glob->len > 0 ? "." : "", + cfg_connection->downstream_port_glob->str); + } +} + +static +void print_cfg_list_plugins(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); +} + +static +void print_cfg_help(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); +} + +static +void print_cfg_print_ctf_metadata(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Path: %s\n", + cfg->cmd_data.print_ctf_metadata.path->str); +} + +static +void print_cfg_print_lttng_live_sessions(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " URL: %s\n", + cfg->cmd_data.print_lttng_live_sessions.url->str); +} + +static +void print_cfg_query(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Object: `%s`\n", cfg->cmd_data.query.object->str); + fprintf(stderr, " Component class:\n"); + print_bt_config_component(cfg->cmd_data.query.cfg_component); +} + +static +void print_cfg(struct bt_config *cfg) +{ + if (!BT_LOG_ON_INFO) { + return; + } + + BT_LOGI_STR("Configuration:"); + fprintf(stderr, " Debug mode: %s\n", cfg->debug ? "yes" : "no"); + fprintf(stderr, " Verbose mode: %s\n", cfg->verbose ? "yes" : "no"); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + print_cfg_run(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + print_cfg_list_plugins(cfg); + break; + case BT_CONFIG_COMMAND_HELP: + print_cfg_help(cfg); + break; + case BT_CONFIG_COMMAND_QUERY: + print_cfg_query(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + print_cfg_print_ctf_metadata(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + print_cfg_print_lttng_live_sessions(cfg); + break; + default: + abort(); + } +} + +static +void add_to_loaded_plugins(const bt_plugin_set *plugin_set) +{ + int64_t i; + int64_t count; + + count = bt_plugin_set_get_plugin_count(plugin_set); + BT_ASSERT(count >= 0); + + for (i = 0; i < count; i++) { + const bt_plugin *plugin = + bt_plugin_set_borrow_plugin_by_index_const(plugin_set, i); + const bt_plugin *loaded_plugin = + find_plugin(bt_plugin_get_name(plugin)); + + BT_ASSERT(plugin); + + if (loaded_plugin) { + BT_LOGI("Not using plugin: another one already exists with the same name: " + "plugin-name=\"%s\", plugin-path=\"%s\", " + "existing-plugin-path=\"%s\"", + bt_plugin_get_name(plugin), + bt_plugin_get_path(plugin), + bt_plugin_get_path(loaded_plugin)); + bt_plugin_put_ref(loaded_plugin); + } else { + /* Add to global array. */ + BT_LOGD("Adding plugin to loaded plugins: plugin-path=\"%s\"", + bt_plugin_get_name(plugin)); + bt_plugin_get_ref(plugin); + g_ptr_array_add(loaded_plugins, (void *) plugin); + } + } +} + +static +int load_dynamic_plugins(const bt_value *plugin_paths) +{ + int nr_paths, i, ret = 0; + + nr_paths = bt_value_array_get_size(plugin_paths); + if (nr_paths < 0) { + BT_LOGE_STR("Cannot load dynamic plugins: no plugin path."); + ret = -1; + goto end; + } + + BT_LOGI("Loading dynamic plugins."); + + for (i = 0; i < nr_paths; i++) { + const bt_value *plugin_path_value = NULL; + const char *plugin_path; + const bt_plugin_set *plugin_set; + + plugin_path_value = + bt_value_array_borrow_element_by_index_const( + plugin_paths, i); + plugin_path = bt_value_string_get(plugin_path_value); + + /* + * Skip this if the directory does not exist because + * bt_plugin_find_all_from_dir() expects an existing + * directory. + */ + if (!g_file_test(plugin_path, G_FILE_TEST_IS_DIR)) { + BT_LOGV("Skipping nonexistent directory path: " + "path=\"%s\"", plugin_path); + continue; + } + + plugin_set = bt_plugin_find_all_from_dir(plugin_path, false); + if (!plugin_set) { + BT_LOGD("Unable to load dynamic plugins: path=\"%s\"", + plugin_path); + continue; + } + + add_to_loaded_plugins(plugin_set); + bt_plugin_set_put_ref(plugin_set); + } +end: + return ret; +} + +static +int load_static_plugins(void) +{ + int ret = 0; + const bt_plugin_set *plugin_set; + + BT_LOGI("Loading static plugins."); + plugin_set = bt_plugin_find_all_from_static(); + if (!plugin_set) { + BT_LOGE("Unable to load static plugins."); + ret = -1; + goto end; + } + + add_to_loaded_plugins(plugin_set); + bt_plugin_set_put_ref(plugin_set); +end: + return ret; +} + +static +int load_all_plugins(const bt_value *plugin_paths) +{ + int ret = 0; + + if (load_dynamic_plugins(plugin_paths)) { + ret = -1; + goto end; + } + + if (load_static_plugins()) { + ret = -1; + goto end; + } + + BT_LOGI("Loaded all plugins: count=%u", loaded_plugins->len); + +end: + return ret; +} + +static +void print_plugin_info(const bt_plugin *plugin) +{ + unsigned int major, minor, patch; + const char *extra; + bt_property_availability version_avail; + const char *plugin_name; + const char *path; + const char *author; + const char *license; + const char *plugin_description; + + plugin_name = bt_plugin_get_name(plugin); + path = bt_plugin_get_path(plugin); + author = bt_plugin_get_author(plugin); + license = bt_plugin_get_license(plugin); + plugin_description = bt_plugin_get_description(plugin); + version_avail = bt_plugin_get_version(plugin, &major, &minor, + &patch, &extra); + printf("%s%s%s%s:\n", bt_common_color_bold(), + bt_common_color_fg_blue(), plugin_name, + bt_common_color_reset()); + if (path) { + printf(" %sPath%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), path); + } else { + puts(" Built-in"); + } + + if (version_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { + printf(" %sVersion%s: %u.%u.%u", + bt_common_color_bold(), bt_common_color_reset(), + major, minor, patch); + + if (extra) { + printf("%s", extra); + } + + printf("\n"); + } + + printf(" %sDescription%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + plugin_description ? plugin_description : "(None)"); + printf(" %sAuthor%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), author ? author : "(Unknown)"); + printf(" %sLicense%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + license ? license : "(Unknown)"); +} + +static +int cmd_query(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + const char *fail_reason = NULL; + + comp_cls = find_component_class( + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + ret = query(comp_cls, cfg->cmd_data.query.object->str, + cfg->cmd_data.query.cfg_component->params, + &results, &fail_reason); + if (ret) { + goto failed; + } + + print_value(stdout, results, 0); + goto end; + +failed: + BT_LOGE("Failed to query component class: %s: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d " + "object=\"%s\"", fail_reason, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type, + cfg->cmd_data.query.object->str); + fprintf(stderr, "%s%sFailed to query info to %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "%s%s with object `%s`: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + cfg->cmd_data.query.object->str, + fail_reason, + bt_common_color_reset()); + ret = -1; + +end: + bt_component_class_put_ref(comp_cls); + bt_value_put_ref(results); + return ret; +} + +static +void print_component_class_help(const char *plugin_name, + const bt_component_class *comp_cls) +{ + const char *comp_class_name = + bt_component_class_get_name(comp_cls); + const char *comp_class_description = + bt_component_class_get_description(comp_cls); + const char *comp_class_help = + bt_component_class_get_help(comp_cls); + bt_component_class_type type = + bt_component_class_get_type(comp_cls); + + print_plugin_comp_cls_opt(stdout, plugin_name, comp_class_name, type); + printf("\n"); + printf(" %sDescription%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + comp_class_description ? comp_class_description : "(None)"); + + if (comp_class_help) { + printf("\n%s\n", comp_class_help); + } +} + +static +int cmd_help(struct bt_config *cfg) +{ + int ret = 0; + const bt_plugin *plugin = NULL; + const bt_component_class *needed_comp_cls = NULL; + + plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); + if (!plugin) { + BT_LOGE("Cannot find plugin: plugin-name=\"%s\"", + cfg->cmd_data.help.cfg_component->plugin_name->str); + fprintf(stderr, "%s%sCannot find plugin %s%s%s\n", + bt_common_color_bold(), bt_common_color_fg_red(), + bt_common_color_fg_blue(), + cfg->cmd_data.help.cfg_component->plugin_name->str, + bt_common_color_reset()); + ret = -1; + goto end; + } + + print_plugin_info(plugin); + printf(" %sSource component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_source_component_class_count(plugin)); + printf(" %sFilter component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_filter_component_class_count(plugin)); + printf(" %sSink component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_sink_component_class_count(plugin)); + + if (strlen(cfg->cmd_data.help.cfg_component->comp_cls_name->str) == 0) { + /* Plugin help only */ + goto end; + } + + needed_comp_cls = find_component_class( + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + if (!needed_comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + fprintf(stderr, "\n%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + printf("\n"); + print_component_class_help( + cfg->cmd_data.help.cfg_component->plugin_name->str, + needed_comp_cls); + +end: + bt_component_class_put_ref(needed_comp_cls); + bt_plugin_put_ref(plugin); + return ret; +} + +typedef void *(* plugin_borrow_comp_cls_by_index_func_t)(const bt_plugin *, + uint64_t); +typedef const bt_component_class *(* spec_comp_cls_borrow_comp_cls_func_t)( + void *); + +void cmd_list_plugins_print_component_classes(const bt_plugin *plugin, + const char *cc_type_name, uint64_t count, + plugin_borrow_comp_cls_by_index_func_t borrow_comp_cls_by_index_func, + spec_comp_cls_borrow_comp_cls_func_t spec_comp_cls_borrow_comp_cls_func) +{ + uint64_t i; + + if (count == 0) { + printf(" %s%s component classes%s: (none)\n", + bt_common_color_bold(), + cc_type_name, + bt_common_color_reset()); + goto end; + } else { + printf(" %s%s component classes%s:\n", + bt_common_color_bold(), + cc_type_name, + bt_common_color_reset()); + } + + for (i = 0; i < count; i++) { + const bt_component_class *comp_class = + spec_comp_cls_borrow_comp_cls_func( + borrow_comp_cls_by_index_func(plugin, i)); + const char *comp_class_name = + bt_component_class_get_name(comp_class); + const char *comp_class_description = + bt_component_class_get_description(comp_class); + bt_component_class_type type = + bt_component_class_get_type(comp_class); + + printf(" "); + print_plugin_comp_cls_opt(stdout, + bt_plugin_get_name(plugin), comp_class_name, + type); + + if (comp_class_description) { + printf(": %s", comp_class_description); + } + + printf("\n"); + } + +end: + return; +} + +static +int cmd_list_plugins(struct bt_config *cfg) +{ + int ret = 0; + int plugins_count, component_classes_count = 0, i; + + printf("From the following plugin paths:\n\n"); + print_value(stdout, cfg->plugin_paths, 2); + printf("\n"); + plugins_count = loaded_plugins->len; + if (plugins_count == 0) { + printf("No plugins found.\n"); + goto end; + } + + for (i = 0; i < plugins_count; i++) { + const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + component_classes_count += + bt_plugin_get_source_component_class_count(plugin) + + bt_plugin_get_filter_component_class_count(plugin) + + bt_plugin_get_sink_component_class_count(plugin); + } + + printf("Found %s%d%s component classes in %s%d%s plugins.\n", + bt_common_color_bold(), + component_classes_count, + bt_common_color_reset(), + bt_common_color_bold(), + plugins_count, + bt_common_color_reset()); + + for (i = 0; i < plugins_count; i++) { + const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + printf("\n"); + print_plugin_info(plugin); + cmd_list_plugins_print_component_classes(plugin, "Source", + bt_plugin_get_source_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_source_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_source_as_component_class); + cmd_list_plugins_print_component_classes(plugin, "Filter", + bt_plugin_get_filter_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_filter_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_filter_as_component_class); + cmd_list_plugins_print_component_classes(plugin, "Sink", + bt_plugin_get_sink_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_sink_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_sink_as_component_class); + } + +end: + return ret; +} + +static +int cmd_print_lttng_live_sessions(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + bt_value *params = NULL; + const bt_value *map = NULL; + const bt_value *v = NULL; + static const char * const plugin_name = "ctf"; + static const char * const comp_cls_name = "lttng-live"; + static const bt_component_class_type comp_cls_type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + int64_t array_size, i; + const char *fail_reason = NULL; + FILE *out_stream = stdout; + + BT_ASSERT(cfg->cmd_data.print_lttng_live_sessions.url); + comp_cls = find_component_class(plugin_name, comp_cls_name, + comp_cls_type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + plugin_name, comp_cls_name, + BT_COMPONENT_CLASS_TYPE_SOURCE); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, plugin_name, + comp_cls_name, comp_cls_type); + fprintf(stderr, "\n"); + goto error; + } + + params = bt_value_map_create(); + if (!params) { + goto error; + } + + ret = bt_value_map_insert_string_entry(params, "url", + cfg->cmd_data.print_lttng_live_sessions.url->str); + if (ret) { + goto error; + } + + ret = query(comp_cls, "sessions", params, + &results, &fail_reason); + if (ret) { + goto failed; + } + + BT_ASSERT(results); + + if (!bt_value_is_array(results)) { + BT_LOGE_STR("Expecting an array for sessions query."); + fprintf(stderr, "%s%sUnexpected type returned by session query%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + goto error; + } + + if (cfg->cmd_data.print_lttng_live_sessions.output_path->len > 0) { + out_stream = + fopen(cfg->cmd_data.print_lttng_live_sessions.output_path->str, + "w"); + if (!out_stream) { + ret = -1; + BT_LOGE_ERRNO("Cannot open file for writing", + ": path=\"%s\"", + cfg->cmd_data.print_lttng_live_sessions.output_path->str); + goto end; + } + } + + array_size = bt_value_array_get_size(results); + for (i = 0; i < array_size; i++) { + const char *url_text; + int64_t timer_us, streams, clients; + + map = bt_value_array_borrow_element_by_index_const(results, i); + if (!map) { + BT_LOGE_STR("Unexpected empty array entry."); + goto error; + } + if (!bt_value_is_map(map)) { + BT_LOGE_STR("Unexpected entry type."); + goto error; + } + + v = bt_value_map_borrow_entry_value_const(map, "url"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"url\" entry."); + goto error; + } + url_text = bt_value_string_get(v); + fprintf(out_stream, "%s", url_text); + v = bt_value_map_borrow_entry_value_const(map, "timer-us"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"timer-us\" entry."); + goto error; + } + timer_us = bt_value_signed_integer_get(v); + fprintf(out_stream, " (timer = %" PRIu64 ", ", timer_us); + v = bt_value_map_borrow_entry_value_const(map, "stream-count"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"stream-count\" entry."); + goto error; + } + streams = bt_value_signed_integer_get(v); + fprintf(out_stream, "%" PRIu64 " stream(s), ", streams); + v = bt_value_map_borrow_entry_value_const(map, "client-count"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"client-count\" entry."); + goto error; + } + clients = bt_value_signed_integer_get(v); + fprintf(out_stream, "%" PRIu64 " client(s) connected)\n", clients); + } + + goto end; + +failed: + BT_LOGE("Failed to query for sessions: %s", fail_reason); + fprintf(stderr, "%s%sFailed to request sessions: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + fail_reason, + bt_common_color_reset()); + +error: + ret = -1; + +end: + bt_value_put_ref(results); + bt_value_put_ref(params); + bt_component_class_put_ref(comp_cls); + + if (out_stream && out_stream != stdout) { + int fclose_ret = fclose(out_stream); + + if (fclose_ret) { + BT_LOGE_ERRNO("Cannot close file stream", + ": path=\"%s\"", + cfg->cmd_data.print_lttng_live_sessions.output_path->str); + } + } + + return ret; +} + +static +int cmd_print_ctf_metadata(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + bt_value *params = NULL; + const bt_value *metadata_text_value = NULL; + const char *metadata_text = NULL; + static const char * const plugin_name = "ctf"; + static const char * const comp_cls_name = "fs"; + static const bt_component_class_type comp_cls_type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + const char *fail_reason = NULL; + FILE *out_stream = stdout; + + BT_ASSERT(cfg->cmd_data.print_ctf_metadata.path); + comp_cls = find_component_class(plugin_name, comp_cls_name, + comp_cls_type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + plugin_name, comp_cls_name, + BT_COMPONENT_CLASS_TYPE_SOURCE); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, plugin_name, + comp_cls_name, comp_cls_type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + params = bt_value_map_create(); + if (!params) { + ret = -1; + goto end; + } + + ret = bt_value_map_insert_string_entry(params, "path", + cfg->cmd_data.print_ctf_metadata.path->str); + if (ret) { + ret = -1; + goto end; + } + + ret = query(comp_cls, "metadata-info", + params, &results, &fail_reason); + if (ret) { + goto failed; + } + + metadata_text_value = bt_value_map_borrow_entry_value_const(results, + "text"); + if (!metadata_text_value) { + BT_LOGE_STR("Cannot find `text` string value in the resulting metadata info object."); + ret = -1; + goto end; + } + + metadata_text = bt_value_string_get(metadata_text_value); + + if (cfg->cmd_data.print_ctf_metadata.output_path->len > 0) { + out_stream = + fopen(cfg->cmd_data.print_ctf_metadata.output_path->str, + "w"); + if (!out_stream) { + ret = -1; + BT_LOGE_ERRNO("Cannot open file for writing", + ": path=\"%s\"", + cfg->cmd_data.print_ctf_metadata.output_path->str); + goto end; + } + } + + ret = fprintf(out_stream, "%s\n", metadata_text); + if (ret < 0) { + BT_LOGE("Cannot write whole metadata text to output stream: " + "ret=%d", ret); + goto end; + } + + ret = 0; + + goto end; + +failed: + ret = -1; + BT_LOGE("Failed to query for metadata info: %s", fail_reason); + fprintf(stderr, "%s%sFailed to request metadata info: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + fail_reason, + bt_common_color_reset()); + +end: + bt_value_put_ref(results); + bt_value_put_ref(params); + bt_component_class_put_ref(comp_cls); + + if (out_stream && out_stream != stdout) { + int fclose_ret = fclose(out_stream); + + if (fclose_ret) { + BT_LOGE_ERRNO("Cannot close file stream", + ": path=\"%s\"", + cfg->cmd_data.print_ctf_metadata.output_path->str); + } + } + + return ret; +} + +struct port_id { + char *instance_name; + char *port_name; +}; + +struct trace_range { + uint64_t intersection_range_begin_ns; + uint64_t intersection_range_end_ns; +}; + +static +guint port_id_hash(gconstpointer v) +{ + const struct port_id *id = v; + + BT_ASSERT(id->instance_name); + BT_ASSERT(id->port_name); + + return g_str_hash(id->instance_name) ^ g_str_hash(id->port_name); +} + +static +gboolean port_id_equal(gconstpointer v1, gconstpointer v2) +{ + const struct port_id *id1 = v1; + const struct port_id *id2 = v2; + + return !strcmp(id1->instance_name, id2->instance_name) && + !strcmp(id1->port_name, id2->port_name); +} + +static +void port_id_destroy(gpointer data) +{ + struct port_id *id = data; + + free(id->instance_name); + free(id->port_name); + free(id); +} + +static +void trace_range_destroy(gpointer data) +{ + free(data); +} + +struct cmd_run_ctx { + /* Owned by this */ + GHashTable *src_components; + + /* Owned by this */ + GHashTable *flt_components; + + /* Owned by this */ + GHashTable *sink_components; + + /* Owned by this */ + bt_graph *graph; + + /* Weak */ + struct bt_config *cfg; + + bool connect_ports; + + bool stream_intersection_mode; + + /* + * Association of struct port_id -> struct trace_range. + */ + GHashTable *intersections; +}; + +/* Returns a timestamp of the form "(-)s.ns" */ +static +char *s_from_ns(int64_t ns) +{ + int ret; + char *s_ret = NULL; + bool is_negative; + int64_t ts_sec_abs, ts_nsec_abs; + int64_t ts_sec = ns / NSEC_PER_SEC; + int64_t ts_nsec = ns % NSEC_PER_SEC; + + if (ts_sec >= 0 && ts_nsec >= 0) { + is_negative = false; + ts_sec_abs = ts_sec; + ts_nsec_abs = ts_nsec; + } else if (ts_sec > 0 && ts_nsec < 0) { + is_negative = false; + ts_sec_abs = ts_sec - 1; + ts_nsec_abs = NSEC_PER_SEC + ts_nsec; + } else if (ts_sec == 0 && ts_nsec < 0) { + is_negative = true; + ts_sec_abs = ts_sec; + ts_nsec_abs = -ts_nsec; + } else if (ts_sec < 0 && ts_nsec > 0) { + is_negative = true; + ts_sec_abs = -(ts_sec + 1); + ts_nsec_abs = NSEC_PER_SEC - ts_nsec; + } else if (ts_sec < 0 && ts_nsec == 0) { + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = ts_nsec; + } else { /* (ts_sec < 0 && ts_nsec < 0) */ + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = -ts_nsec; + } + + ret = asprintf(&s_ret, "%s%" PRId64 ".%09" PRId64, + is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); + if (ret < 0) { + s_ret = NULL; + } + return s_ret; +} + +static +int cmd_run_ctx_connect_upstream_port_to_downstream_component( + struct cmd_run_ctx *ctx, + const bt_component *upstream_comp, + const bt_port_output *out_upstream_port, + struct bt_config_connection *cfg_conn) +{ + typedef uint64_t (*input_port_count_func_t)(void *); + typedef const bt_port_input *(*borrow_input_port_by_index_func_t)( + const void *, uint64_t); + const bt_port *upstream_port = + bt_port_output_as_port_const(out_upstream_port); + + int ret = 0; + GQuark downstreamp_comp_name_quark; + void *downstream_comp; + uint64_t downstream_port_count; + uint64_t i; + input_port_count_func_t port_count_fn; + borrow_input_port_by_index_func_t port_by_index_fn; + bt_graph_status status = BT_GRAPH_STATUS_ERROR; + bool insert_trimmer = false; + bt_value *trimmer_params = NULL; + char *intersection_begin = NULL; + char *intersection_end = NULL; + const bt_component_filter *trimmer = NULL; + const bt_component_class_filter *trimmer_class = NULL; + const bt_port_input *trimmer_input = NULL; + const bt_port_output *trimmer_output = NULL; + + if (ctx->intersections && + bt_component_get_class_type(upstream_comp) == + BT_COMPONENT_CLASS_TYPE_SOURCE) { + struct trace_range *range; + struct port_id port_id = { + .instance_name = (char *) bt_component_get_name(upstream_comp), + .port_name = (char *) bt_port_get_name(upstream_port) + }; + + if (!port_id.instance_name || !port_id.port_name) { + goto error; + } + + range = (struct trace_range *) g_hash_table_lookup( + ctx->intersections, &port_id); + if (range) { + bt_value_status status; + + intersection_begin = s_from_ns( + range->intersection_range_begin_ns); + intersection_end = s_from_ns( + range->intersection_range_end_ns); + if (!intersection_begin || !intersection_end) { + BT_LOGE_STR("Cannot create trimmer argument timestamp string."); + goto error; + } + + insert_trimmer = true; + trimmer_params = bt_value_map_create(); + if (!trimmer_params) { + goto error; + } + + status = bt_value_map_insert_string_entry( + trimmer_params, "begin", intersection_begin); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + status = bt_value_map_insert_string_entry( + trimmer_params, + "end", intersection_end); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + } + + trimmer_class = find_filter_component_class("utils", "trimmer"); + if (!trimmer_class) { + goto error; + } + } + + BT_LOGI("Connecting upstream port to the next available downstream port: " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-name=\"%s\", conn-arg=\"%s\"", + upstream_port, bt_port_get_name(upstream_port), + cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + downstreamp_comp_name_quark = g_quark_from_string( + cfg_conn->downstream_comp_name->str); + BT_ASSERT(downstreamp_comp_name_quark > 0); + downstream_comp = g_hash_table_lookup(ctx->flt_components, + GUINT_TO_POINTER(downstreamp_comp_name_quark)); + port_count_fn = (input_port_count_func_t) + bt_component_filter_get_input_port_count; + port_by_index_fn = (borrow_input_port_by_index_func_t) + bt_component_filter_borrow_input_port_by_index_const; + + if (!downstream_comp) { + downstream_comp = g_hash_table_lookup(ctx->sink_components, + GUINT_TO_POINTER(downstreamp_comp_name_quark)); + port_count_fn = (input_port_count_func_t) + bt_component_sink_get_input_port_count; + port_by_index_fn = (borrow_input_port_by_index_func_t) + bt_component_sink_borrow_input_port_by_index_const; + } + + if (!downstream_comp) { + BT_LOGE("Cannot find downstream component: comp-name=\"%s\", " + "conn-arg=\"%s\"", cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + fprintf(stderr, "Cannot create connection: cannot find downstream component: %s\n", + cfg_conn->arg->str); + goto error; + } + + downstream_port_count = port_count_fn(downstream_comp); + + for (i = 0; i < downstream_port_count; i++) { + const bt_port_input *in_downstream_port = + port_by_index_fn(downstream_comp, i); + const bt_port *downstream_port = + bt_port_input_as_port_const(in_downstream_port); + const char *upstream_port_name; + const char *downstream_port_name; + + BT_ASSERT(downstream_port); + + /* Skip port if it's already connected. */ + if (bt_port_is_connected(downstream_port)) { + BT_LOGD("Skipping downstream port: already connected: " + "port-addr=%p, port-name=\"%s\"", + downstream_port, + bt_port_get_name(downstream_port)); + continue; + } + + downstream_port_name = bt_port_get_name(downstream_port); + BT_ASSERT(downstream_port_name); + upstream_port_name = bt_port_get_name(upstream_port); + BT_ASSERT(upstream_port_name); + + if (!bt_common_star_glob_match( + cfg_conn->downstream_port_glob->str, SIZE_MAX, + downstream_port_name, SIZE_MAX)) { + continue; + } + + if (insert_trimmer) { + /* + * In order to insert the trimmer between the + * two components that were being connected, we + * create a connection configuration entry which + * describes a connection from the trimmer's + * output to the original input that was being + * connected. + * + * Hence, the creation of the trimmer will cause + * the graph "new port" listener to establish + * all downstream connections as its output port + * is connected. We will then establish the + * connection between the original upstream + * source and the trimmer. + */ + char *trimmer_name = NULL; + bt_graph_status graph_status; + + ret = asprintf(&trimmer_name, + "stream-intersection-trimmer-%s", + upstream_port_name); + if (ret < 0) { + goto error; + } + ret = 0; + + ctx->connect_ports = false; + graph_status = bt_graph_add_filter_component( + ctx->graph, trimmer_class, trimmer_name, + trimmer_params, &trimmer); + free(trimmer_name); + if (graph_status != BT_GRAPH_STATUS_OK) { + goto error; + } + BT_ASSERT(trimmer); + + trimmer_input = + bt_component_filter_borrow_input_port_by_index_const( + trimmer, 0); + if (!trimmer_input) { + goto error; + } + trimmer_output = + bt_component_filter_borrow_output_port_by_index_const( + trimmer, 0); + if (!trimmer_output) { + goto error; + } + + /* + * Replace the current downstream port by the trimmer's + * upstream port. + */ + in_downstream_port = trimmer_input; + downstream_port = + bt_port_input_as_port_const(in_downstream_port); + downstream_port_name = bt_port_get_name( + downstream_port); + BT_ASSERT(downstream_port_name); + } + + /* We have a winner! */ + status = bt_graph_connect_ports(ctx->graph, + out_upstream_port, in_downstream_port, NULL); + downstream_port = NULL; + switch (status) { + case BT_GRAPH_STATUS_OK: + break; + case BT_GRAPH_STATUS_CANCELED: + BT_LOGI_STR("Graph was canceled by user."); + status = BT_GRAPH_STATUS_OK; + break; + case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + BT_LOGE("A component refused a connection to one of its ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + fprintf(stderr, + "A component refused a connection to one of its ports (`%s` to `%s`): %s\n", + bt_port_get_name(upstream_port), + downstream_port_name, + cfg_conn->arg->str); + break; + default: + BT_LOGE("Cannot create connection: graph refuses to connect ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + fprintf(stderr, + "Cannot create connection: graph refuses to connect ports (`%s` to `%s`): %s\n", + bt_port_get_name(upstream_port), + downstream_port_name, + cfg_conn->arg->str); + goto error; + } + + BT_LOGI("Connected component ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + + if (insert_trimmer) { + /* + * The first connection, from the source to the trimmer, + * has been done. We now connect the trimmer to the + * original downstream port. + */ + ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( + ctx, + bt_component_filter_as_component_const(trimmer), + trimmer_output, cfg_conn); + if (ret) { + goto error; + } + ctx->connect_ports = true; + } + + /* + * We found a matching downstream port: the search is + * over. + */ + goto end; + } + + /* No downstream port found */ + BT_LOGE("Cannot create connection: cannot find a matching downstream port for upstream port: " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-name=\"%s\", conn-arg=\"%s\"", + upstream_port, bt_port_get_name(upstream_port), + cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + fprintf(stderr, + "Cannot create connection: cannot find a matching downstream port for upstream port `%s`: %s\n", + bt_port_get_name(upstream_port), cfg_conn->arg->str); + +error: + ret = -1; + +end: + free(intersection_begin); + free(intersection_end); + BT_VALUE_PUT_REF_AND_RESET(trimmer_params); + BT_COMPONENT_CLASS_FILTER_PUT_REF_AND_RESET(trimmer_class); + BT_COMPONENT_FILTER_PUT_REF_AND_RESET(trimmer); + return ret; +} + +static +int cmd_run_ctx_connect_upstream_port(struct cmd_run_ctx *ctx, + const bt_port_output *upstream_port) +{ + int ret = 0; + const char *upstream_port_name; + const char *upstream_comp_name; + const bt_component *upstream_comp = NULL; + size_t i; + + BT_ASSERT(ctx); + BT_ASSERT(upstream_port); + upstream_port_name = bt_port_get_name( + bt_port_output_as_port_const(upstream_port)); + BT_ASSERT(upstream_port_name); + upstream_comp = bt_port_borrow_component_const( + bt_port_output_as_port_const(upstream_port)); + if (!upstream_comp) { + BT_LOGW("Upstream port to connect is not part of a component: " + "port-addr=%p, port-name=\"%s\"", + upstream_port, upstream_port_name); + ret = -1; + goto end; + } + + upstream_comp_name = bt_component_get_name(upstream_comp); + BT_ASSERT(upstream_comp_name); + BT_LOGI("Connecting upstream port: comp-addr=%p, comp-name=\"%s\", " + "port-addr=%p, port-name=\"%s\"", + upstream_comp, upstream_comp_name, + upstream_port, upstream_port_name); + + for (i = 0; i < ctx->cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *cfg_conn = + g_ptr_array_index( + ctx->cfg->cmd_data.run.connections, i); + + if (strcmp(cfg_conn->upstream_comp_name->str, + upstream_comp_name)) { + continue; + } + + if (!bt_common_star_glob_match( + cfg_conn->upstream_port_glob->str, + SIZE_MAX, upstream_port_name, SIZE_MAX)) { + continue; + } + + ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( + ctx, upstream_comp, upstream_port, cfg_conn); + if (ret) { + BT_LOGE("Cannot connect upstream port: " + "port-addr=%p, port-name=\"%s\"", + upstream_port, + upstream_port_name); + fprintf(stderr, + "Cannot connect port `%s` of component `%s` to a downstream port: %s\n", + upstream_port_name, + upstream_comp_name, + cfg_conn->arg->str); + goto error; + } + goto end; + } + + BT_LOGE("Cannot connect upstream port: port does not match any connection argument: " + "port-addr=%p, port-name=\"%s\"", upstream_port, + upstream_port_name); + fprintf(stderr, + "Cannot create connection: upstream port `%s` does not match any connection\n", + upstream_port_name); + +error: + ret = -1; + +end: + return ret; +} + +static +bt_graph_listener_status +graph_output_port_added_listener(struct cmd_run_ctx *ctx, + const bt_port_output *out_port) +{ + const bt_component *comp; + const bt_port *port = bt_port_output_as_port_const(out_port); + bt_graph_listener_status ret = BT_GRAPH_LISTENER_STATUS_OK; + + comp = bt_port_borrow_component_const(port); + BT_LOGI("Port added to a graph's component: comp-addr=%p, " + "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"", + comp, comp ? bt_component_get_name(comp) : "", + port, bt_port_get_name(port)); + + if (!ctx->connect_ports) { + goto end; + } + + if (!comp) { + BT_LOGW_STR("Port has no component."); + goto end; + } + + if (bt_port_is_connected(port)) { + BT_LOGW_STR("Port is already connected."); + goto end; + } + + if (cmd_run_ctx_connect_upstream_port(ctx, out_port)) { + BT_LOGF_STR("Cannot connect upstream port."); + fprintf(stderr, "Added port could not be connected: aborting\n"); + ret = BT_GRAPH_LISTENER_STATUS_ERROR; + goto end; + } + +end: + return ret; +} + +static +bt_graph_listener_status graph_source_output_port_added_listener( + const bt_component_source *component, + const bt_port_output *port, void *data) +{ + return graph_output_port_added_listener(data, port); +} + +static +bt_graph_listener_status graph_filter_output_port_added_listener( + const bt_component_filter *component, + const bt_port_output *port, void *data) +{ + return graph_output_port_added_listener(data, port); +} + +static +void cmd_run_ctx_destroy(struct cmd_run_ctx *ctx) +{ + if (!ctx) { + return; + } + + if (ctx->src_components) { + g_hash_table_destroy(ctx->src_components); + ctx->src_components = NULL; + } + + if (ctx->flt_components) { + g_hash_table_destroy(ctx->flt_components); + ctx->flt_components = NULL; + } + + if (ctx->sink_components) { + g_hash_table_destroy(ctx->sink_components); + ctx->sink_components = NULL; + } + + if (ctx->intersections) { + g_hash_table_destroy(ctx->intersections); + ctx->intersections = NULL; + } + + BT_GRAPH_PUT_REF_AND_RESET(ctx->graph); + the_graph = NULL; + ctx->cfg = NULL; +} + +static +int cmd_run_ctx_init(struct cmd_run_ctx *ctx, struct bt_config *cfg) +{ + int ret = 0; + bt_graph_status status; + + ctx->cfg = cfg; + ctx->connect_ports = false; + ctx->src_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->src_components) { + goto error; + } + + ctx->flt_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->flt_components) { + goto error; + } + + ctx->sink_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->sink_components) { + goto error; + } + + if (cfg->cmd_data.run.stream_intersection_mode) { + ctx->stream_intersection_mode = true; + ctx->intersections = g_hash_table_new_full(port_id_hash, + port_id_equal, port_id_destroy, trace_range_destroy); + if (!ctx->intersections) { + goto error; + } + } + + ctx->graph = bt_graph_create(); + if (!ctx->graph) { + goto error; + } + + the_graph = ctx->graph; + status = bt_graph_add_source_component_output_port_added_listener( + ctx->graph, graph_source_output_port_added_listener, NULL, ctx, + NULL); + if (status != BT_GRAPH_STATUS_OK) { + BT_LOGE_STR("Cannot add \"port added\" listener to graph."); + goto error; + } + + status = bt_graph_add_filter_component_output_port_added_listener( + ctx->graph, graph_filter_output_port_added_listener, NULL, ctx, + NULL); + if (status != BT_GRAPH_STATUS_OK) { + BT_LOGE_STR("Cannot add \"port added\" listener to graph."); + goto error; + } + + goto end; + +error: + cmd_run_ctx_destroy(ctx); + ret = -1; + +end: + return ret; +} + +static +int set_stream_intersections(struct cmd_run_ctx *ctx, + struct bt_config_component *cfg_comp, + const bt_component_class_source *src_comp_cls) +{ + int ret = 0; + uint64_t trace_idx; + int64_t trace_count; + const char *path = NULL; + const bt_value *query_result = NULL; + const bt_value *trace_info = NULL; + const bt_value *intersection_range = NULL; + const bt_value *intersection_begin = NULL; + const bt_value *intersection_end = NULL; + const bt_value *stream_infos = NULL; + const bt_value *stream_info = NULL; + struct port_id *port_id = NULL; + struct trace_range *trace_range = NULL; + const char *fail_reason = NULL; + const bt_component_class *comp_cls = + bt_component_class_source_as_component_class_const(src_comp_cls); + + ret = query(comp_cls, "trace-info", + cfg_comp->params, &query_result, + &fail_reason); + if (ret) { + BT_LOGD("Component class does not support the `trace-info` query: %s: " + "comp-class-name=\"%s\"", fail_reason, + bt_component_class_get_name(comp_cls)); + ret = -1; + goto error; + } + + BT_ASSERT(query_result); + + if (!bt_value_is_array(query_result)) { + BT_LOGD("Unexpected format of \'trace-info\' query result: " + "component-class-name=%s", + bt_component_class_get_name(comp_cls)); + ret = -1; + goto error; + } + + trace_count = bt_value_array_get_size(query_result); + if (trace_count < 0) { + ret = -1; + goto error; + } + + for (trace_idx = 0; trace_idx < trace_count; trace_idx++) { + int64_t begin, end; + uint64_t stream_idx; + int64_t stream_count; + + trace_info = bt_value_array_borrow_element_by_index_const( + query_result, trace_idx); + if (!trace_info || !bt_value_is_map(trace_info)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve trace from query result."); + goto error; + } + + intersection_range = bt_value_map_borrow_entry_value_const( + trace_info, "intersection-range-ns"); + if (!intersection_range) { + ret = -1; + BT_LOGD_STR("Cannot retrieve \'intersetion-range-ns\' field from query result."); + goto error; + } + + intersection_begin = bt_value_map_borrow_entry_value_const(intersection_range, + "begin"); + if (!intersection_begin) { + ret = -1; + BT_LOGD_STR("Cannot retrieve intersection-range-ns \'begin\' field from query result."); + goto error; + } + + intersection_end = bt_value_map_borrow_entry_value_const(intersection_range, + "end"); + if (!intersection_end) { + ret = -1; + BT_LOGD_STR("Cannot retrieve intersection-range-ns \'end\' field from query result."); + goto error; + } + + begin = bt_value_signed_integer_get(intersection_begin); + end = bt_value_signed_integer_get(intersection_end); + + if (begin < 0 || end < 0 || end < begin) { + BT_LOGW("Invalid trace stream intersection values: " + "intersection-range-ns:begin=%" PRId64 + ", intersection-range-ns:end=%" PRId64, + begin, end); + ret = -1; + goto error; + } + + stream_infos = bt_value_map_borrow_entry_value_const(trace_info, + "streams"); + if (!stream_infos || !bt_value_is_array(stream_infos)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve stream information from trace in query result."); + goto error; + } + + stream_count = bt_value_array_get_size(stream_infos); + if (stream_count < 0) { + ret = -1; + goto error; + } + + for (stream_idx = 0; stream_idx < stream_count; stream_idx++) { + const bt_value *port_name; + + port_id = g_new0(struct port_id, 1); + if (!port_id) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id structure."); + goto error; + } + port_id->instance_name = strdup(cfg_comp->instance_name->str); + if (!port_id->instance_name) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id component instance name."); + goto error; + } + + trace_range = g_new0(struct trace_range, 1); + if (!trace_range) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for trace_range structure."); + goto error; + } + trace_range->intersection_range_begin_ns = begin; + trace_range->intersection_range_end_ns = end; + + stream_info = bt_value_array_borrow_element_by_index_const( + stream_infos, stream_idx); + if (!stream_info || !bt_value_is_map(stream_info)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve stream informations from trace in query result."); + goto error; + } + + port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name"); + if (!port_name || !bt_value_is_string(port_name)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve port name in query result."); + goto error; + } + + port_id->port_name = g_strdup(bt_value_string_get(port_name)); + if (!port_id->port_name) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id port_name."); + goto error; + } + + BT_LOGD("Inserting stream intersection "); + + g_hash_table_insert(ctx->intersections, port_id, trace_range); + + port_id = NULL; + trace_range = NULL; + } + } + + goto end; + +error: + fprintf(stderr, "%s%sCannot determine stream intersection of trace at path \'%s\'.%s\n", + bt_common_color_bold(), + bt_common_color_fg_yellow(), + path ? path : "(unknown)", + bt_common_color_reset()); +end: + bt_value_put_ref(query_result); + g_free(port_id); + g_free(trace_range); + return ret; +} + +static +int cmd_run_ctx_create_components_from_config_components( + struct cmd_run_ctx *ctx, GPtrArray *cfg_components) +{ + size_t i; + const void *comp_cls = NULL; + const void *comp = NULL; + int ret = 0; + + for (i = 0; i < cfg_components->len; i++) { + struct bt_config_component *cfg_comp = + g_ptr_array_index(cfg_components, i); + GQuark quark; + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + comp_cls = find_source_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + comp_cls = find_filter_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + comp_cls = find_sink_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + default: + abort(); + } + + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type); + fprintf(stderr, "\n"); + goto error; + } + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + ret = bt_graph_add_source_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + ret = bt_graph_add_filter_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + ret = bt_graph_add_sink_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + default: + abort(); + } + + if (ret) { + BT_LOGE("Cannot create component: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d, " + "comp-name=\"%s\"", + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type, cfg_comp->instance_name->str); + fprintf(stderr, "%s%sCannot create component `%s`%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + cfg_comp->instance_name->str, + bt_common_color_reset()); + goto error; + } + + if (ctx->stream_intersection_mode && + cfg_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { + ret = set_stream_intersections(ctx, cfg_comp, comp_cls); + if (ret) { + goto error; + } + } + + BT_LOGI("Created and inserted component: comp-addr=%p, comp-name=\"%s\"", + comp, cfg_comp->instance_name->str); + quark = g_quark_from_string(cfg_comp->instance_name->str); + BT_ASSERT(quark > 0); + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + g_hash_table_insert(ctx->src_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + g_hash_table_insert(ctx->flt_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + g_hash_table_insert(ctx->sink_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + default: + abort(); + } + + comp = NULL; + BT_OBJECT_PUT_REF_AND_RESET(comp_cls); + } + + goto end; + +error: + ret = -1; + +end: + bt_object_put_ref(comp); + bt_object_put_ref(comp_cls); + return ret; +} + +static +int cmd_run_ctx_create_components(struct cmd_run_ctx *ctx) +{ + int ret = 0; + + /* + * Make sure that, during this phase, our graph's "port added" + * listener does not connect ports while we are creating the + * components because we have a special, initial phase for + * this. + */ + ctx->connect_ports = false; + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.sources); + if (ret) { + ret = -1; + goto end; + } + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.filters); + if (ret) { + ret = -1; + goto end; + } + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.sinks); + if (ret) { + ret = -1; + goto end; + } + +end: + return ret; +} + +typedef uint64_t (*output_port_count_func_t)(const void *); +typedef const bt_port_output *(*borrow_output_port_by_index_func_t)( + const void *, uint64_t); + +static +int cmd_run_ctx_connect_comp_ports(struct cmd_run_ctx *ctx, + void *comp, output_port_count_func_t port_count_fn, + borrow_output_port_by_index_func_t port_by_index_fn) +{ + int ret = 0; + uint64_t count; + uint64_t i; + + count = port_count_fn(comp); + + for (i = 0; i < count; i++) { + const bt_port_output *upstream_port = port_by_index_fn(comp, i); + + BT_ASSERT(upstream_port); + ret = cmd_run_ctx_connect_upstream_port(ctx, upstream_port); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +int cmd_run_ctx_connect_ports(struct cmd_run_ctx *ctx) +{ + int ret = 0; + GHashTableIter iter; + gpointer g_name_quark, g_comp; + + ctx->connect_ports = true; + g_hash_table_iter_init(&iter, ctx->src_components); + + while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { + ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, + (output_port_count_func_t) + bt_component_source_get_output_port_count, + (borrow_output_port_by_index_func_t) + bt_component_source_borrow_output_port_by_index_const); + if (ret) { + goto end; + } + } + + g_hash_table_iter_init(&iter, ctx->flt_components); + + while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { + ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, + (output_port_count_func_t) + bt_component_filter_get_output_port_count, + (borrow_output_port_by_index_func_t) + bt_component_filter_borrow_output_port_by_index_const); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static inline +const char *bt_graph_status_str(bt_graph_status status) +{ + switch (status) { + case BT_GRAPH_STATUS_OK: + return "BT_GRAPH_STATUS_OK"; + case BT_GRAPH_STATUS_END: + return "BT_GRAPH_STATUS_END"; + case BT_GRAPH_STATUS_AGAIN: + return "BT_GRAPH_STATUS_AGAIN"; + case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; + case BT_GRAPH_STATUS_CANCELED: + return "BT_GRAPH_STATUS_CANCELED"; + case BT_GRAPH_STATUS_ERROR: + return "BT_GRAPH_STATUS_ERROR"; + case BT_GRAPH_STATUS_NOMEM: + return "BT_GRAPH_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +static +int cmd_run(struct bt_config *cfg) +{ + int ret = 0; + struct cmd_run_ctx ctx = { 0 }; + + /* Initialize the command's context and the graph object */ + if (cmd_run_ctx_init(&ctx, cfg)) { + BT_LOGE_STR("Cannot initialize the command's context."); + fprintf(stderr, "Cannot initialize the command's context\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before creating components."); + goto error; + } + + BT_LOGI_STR("Creating components."); + + /* Create the requested component instances */ + if (cmd_run_ctx_create_components(&ctx)) { + BT_LOGE_STR("Cannot create components."); + fprintf(stderr, "Cannot create components\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before connecting components."); + goto error; + } + + BT_LOGI_STR("Connecting components."); + + /* Connect the initially visible component ports */ + if (cmd_run_ctx_connect_ports(&ctx)) { + BT_LOGE_STR("Cannot connect initial component ports."); + fprintf(stderr, "Cannot connect initial component ports\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before running the graph."); + goto error; + } + + BT_LOGI_STR("Running the graph."); + + /* Run the graph */ + while (true) { + bt_graph_status graph_status = bt_graph_run(ctx.graph); + + /* + * Reset console in case something messed with console + * codes during the graph's execution. + */ + printf("%s", bt_common_color_reset()); + fflush(stdout); + fprintf(stderr, "%s", bt_common_color_reset()); + BT_LOGV("bt_graph_run() returned: status=%s", + bt_graph_status_str(graph_status)); + + switch (graph_status) { + case BT_GRAPH_STATUS_OK: + break; + case BT_GRAPH_STATUS_CANCELED: + BT_LOGI_STR("Graph was canceled by user."); + goto error; + case BT_GRAPH_STATUS_AGAIN: + if (bt_graph_is_canceled(ctx.graph)) { + BT_LOGI_STR("Graph was canceled by user."); + goto error; + } + + if (cfg->cmd_data.run.retry_duration_us > 0) { + BT_LOGV("Got BT_GRAPH_STATUS_AGAIN: sleeping: " + "time-us=%" PRIu64, + cfg->cmd_data.run.retry_duration_us); + + if (usleep(cfg->cmd_data.run.retry_duration_us)) { + if (bt_graph_is_canceled(ctx.graph)) { + BT_LOGI_STR("Graph was canceled by user."); + goto error; + } + } + } + break; + case BT_GRAPH_STATUS_END: + goto end; + default: + BT_LOGE_STR("Graph failed to complete successfully"); + fprintf(stderr, "Graph failed to complete successfully\n"); + goto error; + } + } + + goto end; + +error: + if (ret == 0) { + ret = -1; + } + +end: + cmd_run_ctx_destroy(&ctx); + return ret; +} + +static +void warn_command_name_and_directory_clash(struct bt_config *cfg) +{ + const char *env_clash; + + if (!cfg->command_name) { + return; + } + + env_clash = getenv(ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH); + if (env_clash && strcmp(env_clash, "0") == 0) { + return; + } + + if (g_file_test(cfg->command_name, + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + fprintf(stderr, "\nNOTE: The `%s` command was executed. If you meant to convert a\n", + cfg->command_name); + fprintf(stderr, "trace located in the local `%s` directory, please use:\n", + cfg->command_name); + fprintf(stderr, "\n"); + fprintf(stderr, " babeltrace2 convert %s [OPTIONS]\n", + cfg->command_name); + } +} + +static +void init_log_level(void) +{ + bt_cli_log_level = bt_log_get_level_from_env(ENV_BABELTRACE_CLI_LOG_LEVEL); +} + +static +void set_auto_log_levels(struct bt_config *cfg) +{ + const char **env_var_name; + + /* + * Override the configuration's default log level if + * BABELTRACE_VERBOSE or BABELTRACE_DEBUG environment variables + * are found for backward compatibility with legacy Babetrace 1. + */ + if (getenv("BABELTRACE_DEBUG") && + strcmp(getenv("BABELTRACE_DEBUG"), "1") == 0) { + cfg->log_level = 'V'; + } else if (getenv("BABELTRACE_VERBOSE") && + strcmp(getenv("BABELTRACE_VERBOSE"), "1") == 0) { + cfg->log_level = 'I'; + } + + /* + * Set log levels according to --debug or --verbose. For + * backward compatibility, --debug is more verbose than + * --verbose. So: + * + * --verbose: INFO log level + * --debug: VERBOSE log level (includes DEBUG, which is + * is less verbose than VERBOSE in the internal + * logging framework) + */ + if (!getenv("BABELTRACE_LOGGING_GLOBAL_LEVEL")) { + if (cfg->verbose) { + bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); + } else if (cfg->debug) { + bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); + } else { + /* + * Set library's default log level if not + * explicitly specified. + */ + switch (cfg->log_level) { + case 'N': + bt_logging_set_global_level(BT_LOGGING_LEVEL_NONE); + break; + case 'V': + bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); + break; + case 'D': + bt_logging_set_global_level(BT_LOGGING_LEVEL_DEBUG); + break; + case 'I': + bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); + break; + case 'W': + bt_logging_set_global_level(BT_LOGGING_LEVEL_WARN); + break; + case 'E': + bt_logging_set_global_level(BT_LOGGING_LEVEL_ERROR); + break; + case 'F': + bt_logging_set_global_level(BT_LOGGING_LEVEL_FATAL); + break; + default: + abort(); + } + } + } + + if (!getenv(ENV_BABELTRACE_CLI_LOG_LEVEL)) { + if (cfg->verbose) { + bt_cli_log_level = BT_LOG_INFO; + } else if (cfg->debug) { + bt_cli_log_level = BT_LOG_VERBOSE; + } else { + /* + * Set CLI's default log level if not explicitly + * specified. + */ + switch (cfg->log_level) { + case 'N': + bt_cli_log_level = BT_LOG_NONE; + break; + case 'V': + bt_cli_log_level = BT_LOG_VERBOSE; + break; + case 'D': + bt_cli_log_level = BT_LOG_DEBUG; + break; + case 'I': + bt_cli_log_level = BT_LOG_INFO; + break; + case 'W': + bt_cli_log_level = BT_LOG_WARN; + break; + case 'E': + bt_cli_log_level = BT_LOG_ERROR; + break; + case 'F': + bt_cli_log_level = BT_LOG_FATAL; + break; + default: + abort(); + } + } + } + + env_var_name = log_level_env_var_names; + + while (*env_var_name) { + if (!getenv(*env_var_name)) { + if (cfg->verbose) { + g_setenv(*env_var_name, "I", 1); + } else if (cfg->debug) { + g_setenv(*env_var_name, "V", 1); + } else { + char val[2] = { 0 }; + + /* + * Set module's default log level if not + * explicitly specified. + */ + val[0] = cfg->log_level; + g_setenv(*env_var_name, val, 1); + } + } + + env_var_name++; + } +} + +int main(int argc, const char **argv) +{ + int ret; + int retcode; + struct bt_config *cfg; + + init_log_level(); + set_signal_handler(); + init_static_data(); + cfg = bt_config_cli_args_create_with_default(argc, argv, &retcode); + + if (retcode < 0) { + /* Quit without errors; typically usage/version */ + retcode = 0; + BT_LOGI_STR("Quitting without errors."); + goto end; + } + + if (retcode > 0) { + BT_LOGE("Command-line error: retcode=%d", retcode); + goto end; + } + + if (!cfg) { + BT_LOGE_STR("Failed to create a valid Babeltrace configuration."); + fprintf(stderr, "Failed to create Babeltrace configuration\n"); + retcode = 1; + goto end; + } + + set_auto_log_levels(cfg); + print_cfg(cfg); + + if (cfg->command_needs_plugins) { + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + BT_LOGE("Failed to load plugins: ret=%d", ret); + retcode = 1; + goto end; + } + } + + BT_LOGI("Executing command: cmd=%d, command-name=\"%s\"", + cfg->command, cfg->command_name); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + ret = cmd_run(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + ret = cmd_list_plugins(cfg); + break; + case BT_CONFIG_COMMAND_HELP: + ret = cmd_help(cfg); + break; + case BT_CONFIG_COMMAND_QUERY: + ret = cmd_query(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + ret = cmd_print_ctf_metadata(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + ret = cmd_print_lttng_live_sessions(cfg); + break; + default: + BT_LOGF("Invalid/unknown command: cmd=%d", cfg->command); + abort(); + } + + BT_LOGI("Command completed: cmd=%d, command-name=\"%s\", ret=%d", + cfg->command, cfg->command_name, ret); + warn_command_name_and_directory_clash(cfg); + retcode = ret ? 1 : 0; + +end: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + fini_static_data(); + return retcode; +} diff --git a/src/cli/logging.c b/src/cli/logging.c new file mode 100644 index 00000000..92869482 --- /dev/null +++ b/src/cli/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL"); diff --git a/src/cli/logging.h b/src/cli/logging.h new file mode 100644 index 00000000..dd6c1d09 --- /dev/null +++ b/src/cli/logging.h @@ -0,0 +1,31 @@ +#ifndef CLI_LOGGING_H +#define CLI_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_cli_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_cli_log_level); + +#endif /* CLI_LOGGING_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am new file mode 100644 index 00000000..1a73672f --- /dev/null +++ b/src/common/Makefile.am @@ -0,0 +1,89 @@ +AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" + +noinst_LTLIBRARIES = libbabeltrace2-common.la + +libbabeltrace2_common_la_SOURCES = \ + assert.h \ + assert.c \ + common.c \ + common.h \ + logging.c \ + logging.h + +noinst_HEADERS = \ + align.h \ + babeltrace.h \ + list.h \ + mmap-align.h \ + version.h \ + version.i + +## +## This target generates an include file that contains the git version +## string of the current branch, it must be continuously updated when +## we build in the git repo and shipped in dist tarballs to reflect the +## status of the tree when it was generated. If the tree is clean and +## the current commit is tag a starting with "v", consider this a +## release version and set an empty git version. +## +## Here is what the inline script does: +## +## First, delete any stale "version.i.tmp" file. +## +## If "bootstrap" and ".git" exists in the top source directory and the git +## executable is available, get the current git version string in the form: +## +## "latest_tag"(-"number_of_commits_on_top")(-g"latest_commit_hash")(-dirty) +## +## And store it in "version.i.tmp", if the current commit is tagged, the tag +## starts with "v" and the tree is clean, consider this a release version and +## overwrite the git version with an empty string in "version.i.tmp". +## +## If we don't have a "version.i.tmp" nor a "version.i", generate an empty +## string as a failover. +## +## If we don't have a "version.i" or we have both files and they are different, +## copy "version.i.tmp" over "version.i". This way the dependent targets are +## only rebuilt when the version string changes. +## + +version_verbose = $(version_verbose_@AM_V@) +version_verbose_ = $(version_verbose_@AM_DEFAULT_V@) +version_verbose_0 = @echo " GEN " $@; + +version.i: + $(version_verbose)rm -f version.i.tmp; \ + if (test -r "$(top_srcdir)/bootstrap" && test -r "$(top_srcdir)/.git") && \ + test -x "`which git 2>&1;true`"; then \ + GIT_VERSION_STR="`cd "$(top_srcdir)" && git describe --tags --dirty`"; \ + GIT_CURRENT_TAG="`cd "$(top_srcdir)" && git describe --tags --exact-match --match="v[0-9]*" HEAD 2> /dev/null`"; \ + echo "#define GIT_VERSION \"$$GIT_VERSION_STR\"" > version.i.tmp; \ + if ! $(GREP) -- "-dirty" version.i.tmp > /dev/null && \ + test "x$$GIT_CURRENT_TAG" != "x"; then \ + echo "#define GIT_VERSION \"\"" > version.i.tmp; \ + fi; \ + fi; \ + if test ! -f version.i.tmp; then \ + if test ! -f version.i; then \ + echo '#define GIT_VERSION ""' > version.i; \ + fi; \ + elif test ! -f version.i || \ + test x"`cat version.i.tmp`" != x"`cat version.i`"; then \ + mv version.i.tmp version.i; \ + fi; \ + rm -f version.i.tmp; \ + true + +## +## version.i is defined as a .PHONY target even if it's a real file, +## we want the target to be re-run on every make. +## +.PHONY: version.i + +CLEANFILES = version.i.tmp + +## +## Only clean "version.i" on dist-clean, we need to keep it on regular +## clean when it's part of a dist tarball. +## +DISTCLEANFILES = version.i diff --git a/src/common/align.h b/src/common/align.h new file mode 100644 index 00000000..d27b5b53 --- /dev/null +++ b/src/common/align.h @@ -0,0 +1,75 @@ +#ifndef _BABELTRACE_ALIGN_H +#define _BABELTRACE_ALIGN_H + +/* + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compat/compiler.h" +#include "compat/limits.h" + +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define PTR_ALIGN(p, a) ((typeof(p)) ALIGN((unsigned long) (p), a)) +#define ALIGN_FLOOR(x, a) __ALIGN_FLOOR_MASK(x, (typeof(x)) (a) - 1) +#define __ALIGN_FLOOR_MASK(x, mask) ((x) & ~(mask)) +#define PTR_ALIGN_FLOOR(p, a) \ + ((typeof(p)) ALIGN_FLOOR((unsigned long) (p), a)) +#define IS_ALIGNED(x, a) (((x) & ((typeof(x)) (a) - 1)) == 0) + +/* + * Align pointer on natural object alignment. + */ +#define object_align(obj) PTR_ALIGN(obj, __alignof__(*(obj))) +#define object_align_floor(obj) PTR_ALIGN_FLOOR(obj, __alignof__(*(obj))) + +/** + * offset_align - Calculate the offset needed to align an object on its natural + * alignment towards higher addresses. + * @align_drift: object offset from an "alignment"-aligned address. + * @alignment: natural object alignment. Must be non-zero, power of 2. + * + * Returns the offset that must be added to align towards higher + * addresses. + */ +#define offset_align(align_drift, alignment) \ + ({ \ + MAYBE_BUILD_BUG_ON((alignment) == 0 \ + || ((alignment) & ((alignment) - 1))); \ + (((alignment) - (align_drift)) & ((alignment) - 1)); \ + }) + +/** + * offset_align_floor - Calculate the offset needed to align an object + * on its natural alignment towards lower addresses. + * @align_drift: object offset from an "alignment"-aligned address. + * @alignment: natural object alignment. Must be non-zero, power of 2. + * + * Returns the offset that must be substracted to align towards lower addresses. + */ +#define offset_align_floor(align_drift, alignment) \ + ({ \ + MAYBE_BUILD_BUG_ON((alignment) == 0 \ + || ((alignment) & ((alignment) - 1))); \ + (((align_drift) - (alignment)) & ((alignment) - 1)); \ + }) + +#endif /* _BABELTRACE_ALIGN_H */ diff --git a/src/common/assert.c b/src/common/assert.c new file mode 100644 index 00000000..056e2f26 --- /dev/null +++ b/src/common/assert.c @@ -0,0 +1,54 @@ +/* + * Copyright 2019 EfficiOS Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/assert.h" +#include "common/common.h" + +void bt_common_assert_failed(const char *file, int line, const char *func, + const char *assertion) +{ + fprintf(stderr, + "%s\n%s%s%s (╯°□°)╯︵ ┻━┻ %s %s%s%s%s:%s%d%s: %s%s()%s: " + "%sAssertion %s`%s`%s%s failed.%s\n", + bt_common_color_reset(), + bt_common_color_bold(), + bt_common_color_bg_yellow(), + bt_common_color_fg_red(), + bt_common_color_reset(), + bt_common_color_bold(), + bt_common_color_fg_magenta(), + file, + bt_common_color_reset(), + bt_common_color_fg_green(), + line, + bt_common_color_reset(), + bt_common_color_fg_cyan(), + func, + bt_common_color_reset(), + bt_common_color_fg_red(), + bt_common_color_bold(), + assertion, + bt_common_color_reset(), + bt_common_color_fg_red(), + bt_common_color_reset()); + abort(); +} diff --git a/src/common/assert.h b/src/common/assert.h new file mode 100644 index 00000000..27fdeadb --- /dev/null +++ b/src/common/assert.h @@ -0,0 +1,67 @@ +#ifndef BABELTRACE_ASSERT_INTERNAL_H +#define BABELTRACE_ASSERT_INTERNAL_H + +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +#ifdef BT_DEBUG_MODE + +extern void bt_common_assert_failed(const char *file, int line, + const char *func, const char *assertion) __attribute__((noreturn)); + +/* + * Internal assertion (to detect logic errors on which the library user + * has no influence). Use BT_ASSERT_PRE() to check a precondition which + * must be directly or indirectly satisfied by the library user. + */ +#define BT_ASSERT(_cond) \ + do { \ + if (!(_cond)) { \ + bt_common_assert_failed(__FILE__, __LINE__, __func__, \ + TOSTRING(_cond)); \ + } \ + } while (0) + +/* + * Marks a function as being only used within a BT_ASSERT() context. + */ +# define BT_ASSERT_FUNC +#else +/* + * When BT_DEBUG_MODE is not defined, define BT_ASSERT() macro to the following + * to trick the compiler into thinking that the variable passed as condition to + * the assertion is used. This is to prevent set-but-not-used warnings from the + * compiler when assertions are disabled. The `sizeof` operator also makes sure + * that the `_cond` expression is not evaluated, thus preventing unwanted side + * effects. + * + * In-depth explanation: https://stackoverflow.com/questions/37411809/how-to-elegantly-fix-this-unused-variable-warning/37412551#37412551 + */ +# define BT_ASSERT(_cond) ((void) sizeof((void) (_cond), 0)) +# define BT_ASSERT_FUNC BT_UNUSED +#endif /* BT_DEBUG_MODE */ + +#endif /* BABELTRACE_ASSERT_INTERNAL_H */ diff --git a/src/common/babeltrace.h b/src/common/babeltrace.h new file mode 100644 index 00000000..49dfb1b3 --- /dev/null +++ b/src/common/babeltrace.h @@ -0,0 +1,121 @@ +#ifndef _BABELTRACE_INTERNAL_H +#define _BABELTRACE_INTERNAL_H + +/* + * Copyright 2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include "compat/string.h" +#include + +#define PERROR_BUFLEN 200 + +#ifndef likely +# ifdef __GNUC__ +# define likely(x) __builtin_expect(!!(x), 1) +# else +# define likely(x) (!!(x)) +# endif +#endif + +#ifndef unlikely +# ifdef __GNUC__ +# define unlikely(x) __builtin_expect(!!(x), 0) +# else +# define unlikely(x) (!!(x)) +# endif +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef max_t +#define max_t(type, a, b) \ + ((type) (a) > (type) (b) ? (type) (a) : (type) (b)) +#endif + +static inline +bool bt_safe_to_mul_int64(int64_t a, int64_t b) +{ + if (a == 0 || b == 0) { + return true; + } + + return a < INT64_MAX / b; +} + +static inline +bool bt_safe_to_mul_uint64(uint64_t a, uint64_t b) +{ + if (a == 0 || b == 0) { + return true; + } + + return a < UINT64_MAX / b; +} + +static inline +bool bt_safe_to_add_int64(int64_t a, int64_t b) +{ + return a <= INT64_MAX - b; +} + +static inline +bool bt_safe_to_add_uint64(uint64_t a, uint64_t b) +{ + return a <= UINT64_MAX - b; +} + +/* + * Memory allocation zeroed + */ +#define zmalloc(x) calloc(1, x) + +/* + * BT_HIDDEN: set the hidden attribute for internal functions + * On Windows, symbols are local unless explicitly exported, + * see https://gcc.gnu.org/wiki/Visibility + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define BT_HIDDEN +#else +#define BT_HIDDEN __attribute__((visibility("hidden"))) +#endif + +#ifndef __STRINGIFY +#define __STRINGIFY(x) #x +#endif + +#define TOSTRING(x) __STRINGIFY(x) + +#define BT_UNUSED __attribute__((unused)) + +#endif diff --git a/src/common/common.c b/src/common/common.c new file mode 100644 index 00000000..f37bec41 --- /dev/null +++ b/src/common/common.c @@ -0,0 +1,1569 @@ +/* + * Babeltrace common functions + * + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMMON" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include +#include +#include +#include +#include "common/babeltrace.h" +#include "common/common.h" +#include "compat/unistd.h" + +#ifndef __MINGW32__ +#include +#endif + +#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace2/plugins" +#define HOME_ENV_VAR "HOME" +#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins" + +static const char *bt_common_color_code_reset = ""; +static const char *bt_common_color_code_bold = ""; +static const char *bt_common_color_code_fg_default = ""; +static const char *bt_common_color_code_fg_red = ""; +static const char *bt_common_color_code_fg_green = ""; +static const char *bt_common_color_code_fg_yellow = ""; +static const char *bt_common_color_code_fg_blue = ""; +static const char *bt_common_color_code_fg_magenta = ""; +static const char *bt_common_color_code_fg_cyan = ""; +static const char *bt_common_color_code_fg_light_gray = ""; +static const char *bt_common_color_code_bg_default = ""; +static const char *bt_common_color_code_bg_red = ""; +static const char *bt_common_color_code_bg_green = ""; +static const char *bt_common_color_code_bg_yellow = ""; +static const char *bt_common_color_code_bg_blue = ""; +static const char *bt_common_color_code_bg_magenta = ""; +static const char *bt_common_color_code_bg_cyan = ""; +static const char *bt_common_color_code_bg_light_gray = ""; + +static +void __attribute__((constructor)) bt_common_color_ctor(void) +{ + if (bt_common_colors_supported()) { + bt_common_color_code_reset = BT_COMMON_COLOR_RESET; + bt_common_color_code_bold = BT_COMMON_COLOR_BOLD; + bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT; + bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED; + bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN; + bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW; + bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE; + bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA; + bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN; + bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY; + bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT; + bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED; + bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN; + bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW; + bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE; + bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA; + bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN; + bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY; + } +} + +BT_HIDDEN +const char *bt_common_get_system_plugin_path(void) +{ + return SYSTEM_PLUGIN_PATH; +} + +#ifdef __MINGW32__ +BT_HIDDEN +bool bt_common_is_setuid_setgid(void) +{ + return false; +} +#else /* __MINGW32__ */ +BT_HIDDEN +bool bt_common_is_setuid_setgid(void) +{ + return (geteuid() != getuid() || getegid() != getgid()); +} +#endif /* __MINGW32__ */ + +static +char *bt_secure_getenv(const char *name) +{ + if (bt_common_is_setuid_setgid()) { + BT_LOGD("Disregarding environment variable for setuid/setgid binary: " + "name=\"%s\"", name); + return NULL; + } + return getenv(name); +} + +#ifdef __MINGW32__ +static +const char *bt_get_home_dir(void) +{ + return g_get_home_dir(); +} +#else /* __MINGW32__ */ +static +const char *bt_get_home_dir(void) +{ + char *val = NULL; + struct passwd *pwd; + + val = bt_secure_getenv(HOME_ENV_VAR); + if (val) { + goto end; + } + /* Fallback on password file. */ + pwd = getpwuid(getuid()); + if (!pwd) { + goto end; + } + val = pwd->pw_dir; +end: + return val; +} +#endif /* __MINGW32__ */ + +BT_HIDDEN +char *bt_common_get_home_plugin_path(void) +{ + char *path = NULL; + const char *home_dir; + size_t length; + + home_dir = bt_get_home_dir(); + if (!home_dir) { + goto end; + } + + length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1; + + if (length >= PATH_MAX) { + BT_LOGW("Home directory path is too long: length=%zu", + length); + goto end; + } + + path = malloc(PATH_MAX); + if (!path) { + goto end; + } + + strcpy(path, home_dir); + strcat(path, HOME_PLUGIN_SUBPATH); + +end: + return path; +} + +BT_HIDDEN +int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs) +{ + int ret = 0; + const char *at; + const char *end; + size_t init_dirs_len; + + BT_ASSERT(dirs); + init_dirs_len = dirs->len; + + if (!paths) { + /* Nothing to append */ + goto end; + } + + at = paths; + end = paths + strlen(paths); + + while (at < end) { + GString *path; + const char *next_sep; + + next_sep = strchr(at, G_SEARCHPATH_SEPARATOR); + if (next_sep == at) { + /* + * Empty path: try next character (supported + * to conform to the typical parsing of $PATH). + */ + at++; + continue; + } else if (!next_sep) { + /* No more separator: use the remaining */ + next_sep = paths + strlen(paths); + } + + path = g_string_new(NULL); + if (!path) { + goto error; + } + + g_string_append_len(path, at, next_sep - at); + at = next_sep + 1; + g_ptr_array_add(dirs, path); + } + + goto end; + +error: + ret = -1; + + /* Remove the new entries in dirs */ + while (dirs->len > init_dirs_len) { + g_ptr_array_remove_index(dirs, init_dirs_len); + } + +end: + return ret; +} + +static +bool isarealtty(int fd) +{ + bool istty = false; + struct stat tty_stats; + + if (!isatty(fd)) { + /* Not a TTY */ + goto end; + } + + if (fstat(fd, &tty_stats) == 0) { + if (!S_ISCHR(tty_stats.st_mode)) { + /* Not a character device: not a TTY */ + goto end; + } + } + + istty = true; + +end: + return istty; +} + +BT_HIDDEN +bool bt_common_colors_supported(void) +{ + static bool supports_colors = false; + static bool supports_colors_set = false; + const char *term_env_var; + const char *term_color_env_var; + + if (supports_colors_set) { + goto end; + } + + supports_colors_set = true; + + /* + * `BABELTRACE_TERM_COLOR` environment variable always overrides + * the automatic color support detection. + */ + term_color_env_var = getenv("BABELTRACE_TERM_COLOR"); + if (term_color_env_var) { + if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) { + /* Force colors */ + supports_colors = true; + } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) { + /* Force no colors */ + goto end; + } + } + + /* We need a compatible, known terminal */ + term_env_var = getenv("TERM"); + if (!term_env_var) { + goto end; + } + + if (strncmp(term_env_var, "xterm", 5) != 0 && + strncmp(term_env_var, "rxvt", 4) != 0 && + strncmp(term_env_var, "konsole", 7) != 0 && + strncmp(term_env_var, "gnome", 5) != 0 && + strncmp(term_env_var, "screen", 5) != 0 && + strncmp(term_env_var, "tmux", 4) != 0 && + strncmp(term_env_var, "putty", 5) != 0) { + goto end; + } + + /* Both standard output and error streams need to be TTYs */ + if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) { + goto end; + } + + supports_colors = true; + +end: + return supports_colors; +} + +BT_HIDDEN +const char *bt_common_color_reset(void) +{ + return bt_common_color_code_reset; +} + +BT_HIDDEN +const char *bt_common_color_bold(void) +{ + return bt_common_color_code_bold; +} + +BT_HIDDEN +const char *bt_common_color_fg_default(void) +{ + return bt_common_color_code_fg_default; +} + +BT_HIDDEN +const char *bt_common_color_fg_red(void) +{ + return bt_common_color_code_fg_red; +} + +BT_HIDDEN +const char *bt_common_color_fg_green(void) +{ + return bt_common_color_code_fg_green; +} + +BT_HIDDEN +const char *bt_common_color_fg_yellow(void) +{ + return bt_common_color_code_fg_yellow; +} + +BT_HIDDEN +const char *bt_common_color_fg_blue(void) +{ + return bt_common_color_code_fg_blue; +} + +BT_HIDDEN +const char *bt_common_color_fg_magenta(void) +{ + return bt_common_color_code_fg_magenta; +} + +BT_HIDDEN +const char *bt_common_color_fg_cyan(void) +{ + return bt_common_color_code_fg_cyan; +} + +BT_HIDDEN +const char *bt_common_color_fg_light_gray(void) +{ + return bt_common_color_code_fg_light_gray; +} + +BT_HIDDEN +const char *bt_common_color_bg_default(void) +{ + return bt_common_color_code_bg_default; +} + +BT_HIDDEN +const char *bt_common_color_bg_red(void) +{ + return bt_common_color_code_bg_red; +} + +BT_HIDDEN +const char *bt_common_color_bg_green(void) +{ + return bt_common_color_code_bg_green; +} + +BT_HIDDEN +const char *bt_common_color_bg_yellow(void) +{ + return bt_common_color_code_bg_yellow; +} + +BT_HIDDEN +const char *bt_common_color_bg_blue(void) +{ + return bt_common_color_code_bg_blue; +} + +BT_HIDDEN +const char *bt_common_color_bg_magenta(void) +{ + return bt_common_color_code_bg_magenta; +} + +BT_HIDDEN +const char *bt_common_color_bg_cyan(void) +{ + return bt_common_color_code_bg_cyan; +} + +BT_HIDDEN +const char *bt_common_color_bg_light_gray(void) +{ + return bt_common_color_code_bg_light_gray; +} + +BT_HIDDEN +GString *bt_common_string_until(const char *input, const char *escapable_chars, + const char *end_chars, size_t *end_pos) +{ + GString *output = g_string_new(NULL); + const char *ch; + const char *es_char; + const char *end_char; + + if (!output) { + goto error; + } + + for (ch = input; *ch != '\0'; ch++) { + if (*ch == '\\') { + bool continue_loop = false; + + if (ch[1] == '\0') { + /* `\` at the end of the string: append `\` */ + g_string_append_c(output, *ch); + ch++; + goto set_end_pos; + } + + for (es_char = escapable_chars; *es_char != '\0'; es_char++) { + if (ch[1] == *es_char) { + /* + * `\` followed by an escapable + * character: append the escaped + * character only. + */ + g_string_append_c(output, ch[1]); + ch++; + continue_loop = true; + break; + } + } + + if (continue_loop) { + continue; + } + + /* + * `\` followed by a non-escapable character: + * append `\` and the character. + */ + g_string_append_c(output, *ch); + g_string_append_c(output, ch[1]); + ch++; + continue; + } else { + for (end_char = end_chars; *end_char != '\0'; end_char++) { + if (*ch == *end_char) { + /* + * End character found: + * terminate this loop. + */ + goto set_end_pos; + } + } + + /* Normal character: append */ + g_string_append_c(output, *ch); + } + } + +set_end_pos: + if (end_pos) { + *end_pos = ch - input; + } + + goto end; + +error: + if (output) { + g_string_free(output, TRUE); + } + +end: + return output; +} + +BT_HIDDEN +GString *bt_common_shell_quote(const char *input, bool with_single_quotes) +{ + GString *output = g_string_new(NULL); + const char *ch; + bool no_quote = true; + + if (!output) { + goto end; + } + + if (strlen(input) == 0) { + if (with_single_quotes) { + g_string_assign(output, "''"); + } + + goto end; + } + + for (ch = input; *ch != '\0'; ch++) { + const char c = *ch; + + if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' && + c != '@' && c != '%' && c != '+' && c != '=' && + c != ':' && c != ',' && c != '.' && c != '/' && + c != '-') { + no_quote = false; + break; + } + } + + if (no_quote) { + g_string_assign(output, input); + goto end; + } + + if (with_single_quotes) { + g_string_assign(output, "'"); + } + + for (ch = input; *ch != '\0'; ch++) { + if (*ch == '\'') { + g_string_append(output, "'\"'\"'"); + } else { + g_string_append_c(output, *ch); + } + } + + if (with_single_quotes) { + g_string_append_c(output, '\''); + } + +end: + return output; +} + +BT_HIDDEN +bool bt_common_string_is_printable(const char *input) +{ + const char *ch; + bool printable = true; + BT_ASSERT(input); + + for (ch = input; *ch != '\0'; ch++) { + if (!isprint(*ch) && *ch != '\n' && *ch != '\r' && + *ch != '\t' && *ch != '\v') { + printable = false; + goto end; + } + } + +end: + return printable; +} + +BT_HIDDEN +void bt_common_destroy_lttng_live_url_parts( + struct bt_common_lttng_live_url_parts *parts) +{ + if (!parts) { + goto end; + } + + if (parts->proto) { + g_string_free(parts->proto, TRUE); + parts->proto = NULL; + } + + if (parts->hostname) { + g_string_free(parts->hostname, TRUE); + parts->hostname = NULL; + } + + if (parts->target_hostname) { + g_string_free(parts->target_hostname, TRUE); + parts->target_hostname = NULL; + } + + if (parts->session_name) { + g_string_free(parts->session_name, TRUE); + parts->session_name = NULL; + } + +end: + return; +} + +BT_HIDDEN +struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url( + const char *url, char *error_buf, size_t error_buf_size) +{ + struct bt_common_lttng_live_url_parts parts; + const char *at = url; + size_t end_pos; + + BT_ASSERT(url); + memset(&parts, 0, sizeof(parts)); + parts.port = -1; + + /* Protocol */ + parts.proto = bt_common_string_until(at, "", ":", &end_pos); + if (!parts.proto || parts.proto->len == 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, "Missing protocol"); + } + + goto error; + } + + if (strcmp(parts.proto->str, "net") == 0) { + g_string_assign(parts.proto, "net4"); + } + + if (strcmp(parts.proto->str, "net4") != 0 && + strcmp(parts.proto->str, "net6") != 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Unknown protocol: `%s`", parts.proto->str); + } + + goto error; + } + + if (at[end_pos] != ':') { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Expecting `:` after `%s`", parts.proto->str); + } + + goto error; + } + + at += end_pos; + + /* :// */ + if (strncmp(at, "://", 3) != 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Expecting `://` after protocol"); + } + + goto error; + } + + at += 3; + + /* Hostname */ + parts.hostname = bt_common_string_until(at, "", ":/", &end_pos); + if (!parts.hostname || parts.hostname->len == 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, "Missing hostname"); + } + + goto error; + } + + if (at[end_pos] == ':') { + /* Port */ + GString *port; + + at += end_pos + 1; + port = bt_common_string_until(at, "", "/", &end_pos); + if (!port || port->len == 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, "Missing port"); + } + + goto error; + } + + if (sscanf(port->str, "%d", &parts.port) != 1) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Invalid port: `%s`", port->str); + } + + g_string_free(port, TRUE); + goto error; + } + + g_string_free(port, TRUE); + + if (parts.port < 0 || parts.port >= 65536) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Invalid port: %d", parts.port); + } + + goto error; + } + } + + if (at[end_pos] == '\0') { + goto end; + } + + at += end_pos; + + /* /host/ */ + if (strncmp(at, "/host/", 6) != 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Expecting `/host/` after hostname or port"); + } + + goto error; + } + + at += 6; + + /* Target hostname */ + parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos); + if (!parts.target_hostname || parts.target_hostname->len == 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Missing target hostname"); + } + + goto error; + } + + if (at[end_pos] == '\0') { + goto end; + } + + at += end_pos + 1; + + /* Session name */ + parts.session_name = bt_common_string_until(at, "", "/", &end_pos); + if (!parts.session_name || parts.session_name->len == 0) { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Missing session name"); + } + + goto error; + } + + if (at[end_pos] == '/') { + if (error_buf) { + snprintf(error_buf, error_buf_size, + "Unexpected `/` after session name (`%s`)", + parts.session_name->str); + } + + goto error; + } + + goto end; + +error: + bt_common_destroy_lttng_live_url_parts(&parts); + +end: + return parts; +} + +BT_HIDDEN +void bt_common_normalize_star_glob_pattern(char *pattern) +{ + const char *p; + char *np; + bool got_star = false; + + BT_ASSERT(pattern); + + for (p = pattern, np = pattern; *p != '\0'; p++) { + switch (*p) { + case '*': + if (got_star) { + /* Avoid consecutive stars. */ + continue; + } + + got_star = true; + break; + case '\\': + /* Copy backslash character. */ + *np = *p; + np++; + p++; + + if (*p == '\0') { + goto end; + } + + /* fall-through */ + default: + got_star = false; + break; + } + + /* Copy single character. */ + *np = *p; + np++; + } + +end: + *np = '\0'; +} + +static inline +bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len) +{ + return (p - pattern) == pattern_len || *p == '\0'; +} + +/* + * Globbing matching function with the star feature only (`?` and + * character sets are not supported). This matches `candidate` (plain + * string) against `pattern`. A literal star can be escaped with `\` in + * `pattern`. + * + * `pattern_len` or `candidate_len` can be greater than the actual + * string length of `pattern` or `candidate` if the string is + * null-terminated. + */ +BT_HIDDEN +bool bt_common_star_glob_match(const char *pattern, size_t pattern_len, + const char *candidate, size_t candidate_len) { + const char *retry_c = candidate, *retry_p = pattern, *c, *p; + bool got_a_star = false; + +retry: + c = retry_c; + p = retry_p; + + /* + * The concept here is to retry a match in the specific case + * where we already got a star. The retry position for the + * pattern is just after the most recent star, and the retry + * position for the candidate is the character following the + * last try's first character. + * + * Example: + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^^ + * pattern: hi*every*one + * ^^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^^ + * pattern: hi*every*one + * ^^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^^ + * pattern: hi*every*one + * ^^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ MISMATCH + * + * candidate: hi ev every onyx one + * ^ + * pattern: hi*every*one + * ^ + * + * candidate: hi ev every onyx one + * ^^ + * pattern: hi*every*one + * ^^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ + * + * candidate: hi ev every onyx one + * ^ ^ + * pattern: hi*every*one + * ^ ^ SUCCESS + */ + while ((c - candidate) < candidate_len && *c != '\0') { + BT_ASSERT(*c); + + if (at_end_of_pattern(p, pattern, pattern_len)) { + goto end_of_pattern; + } + + switch (*p) { + case '*': + got_a_star = true; + + /* + * Our first try starts at the current candidate + * character and after the star in the pattern. + */ + retry_c = c; + retry_p = p + 1; + + if (at_end_of_pattern(retry_p, pattern, pattern_len)) { + /* + * Star at the end of the pattern at + * this point: automatic match. + */ + return true; + } + + goto retry; + case '\\': + /* Go to escaped character. */ + p++; + + /* + * Fall through the default case which compares + * the escaped character now. + */ + /* fall-through */ + default: + if (at_end_of_pattern(p, pattern, pattern_len) || + *c != *p) { +end_of_pattern: + /* Character mismatch OR end of pattern. */ + if (!got_a_star) { + /* + * We didn't get any star yet, + * so this first mismatch + * automatically makes the whole + * test fail. + */ + return false; + } + + /* + * Next try: next candidate character, + * original pattern character (following + * the most recent star). + */ + retry_c++; + goto retry; + } + break; + } + + /* Next pattern and candidate characters. */ + c++; + p++; + } + + /* + * We checked every candidate character and we're still in a + * success state: the only pattern character allowed to remain + * is a star. + */ + if (at_end_of_pattern(p, pattern, pattern_len)) { + return true; + } + + p++; + return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len); +} + +static +void append_path_parts(const char *path, GPtrArray *parts) +{ + const char *ch = path; + const char *last = path; + + while (true) { + if (*ch == G_DIR_SEPARATOR || *ch == '\0') { + if (ch - last > 0) { + GString *part = g_string_new(NULL); + + BT_ASSERT(part); + g_string_append_len(part, last, ch - last); + g_ptr_array_add(parts, part); + } + + if (*ch == '\0') { + break; + } + + last = ch + 1; + } + + ch++; + } +} + +static +void destroy_gstring(void *gstring) +{ + (void) g_string_free(gstring, TRUE); +} + +#ifdef __MINGW32__ +BT_HIDDEN +GString *bt_common_normalize_path(const char *path, const char *wd) +{ + char *tmp; + GString *norm_path = NULL; + + BT_ASSERT(path); + + tmp = _fullpath(NULL, path, PATH_MAX); + if (!tmp) { + goto error; + } + + norm_path = g_string_new(tmp); + if (!norm_path) { + goto error; + } + + goto end; +error: + if (norm_path) { + g_string_free(norm_path, TRUE); + norm_path = NULL; + } +end: + if (tmp) { + free(tmp); + } + return norm_path; +} +#else +BT_HIDDEN +GString *bt_common_normalize_path(const char *path, const char *wd) +{ + size_t i; + GString *norm_path; + GPtrArray *parts = NULL; + + BT_ASSERT(path); + norm_path = g_string_new(G_DIR_SEPARATOR_S); + if (!norm_path) { + goto error; + } + + parts = g_ptr_array_new_with_free_func(destroy_gstring); + if (!parts) { + goto error; + } + + if (path[0] != G_DIR_SEPARATOR) { + /* Relative path: start with working directory */ + if (wd) { + append_path_parts(wd, parts); + } else { + gchar *cd = g_get_current_dir(); + + append_path_parts(cd, parts); + g_free(cd); + } + } + + /* Append parts of the path parameter */ + append_path_parts(path, parts); + + /* Resolve special `..` and `.` parts */ + for (i = 0; i < parts->len; i++) { + GString *part = g_ptr_array_index(parts, i); + + if (strcmp(part->str, "..") == 0) { + if (i == 0) { + /* + * First part of absolute path is `..`: + * this is invalid. + */ + goto error; + } + + /* Remove `..` and previous part */ + g_ptr_array_remove_index(parts, i - 1); + g_ptr_array_remove_index(parts, i - 1); + i -= 2; + } else if (strcmp(part->str, ".") == 0) { + /* Remove `.` */ + g_ptr_array_remove_index(parts, i); + i -= 1; + } + } + + /* Create normalized path with what's left */ + for (i = 0; i < parts->len; i++) { + GString *part = g_ptr_array_index(parts, i); + + g_string_append(norm_path, part->str); + + if (i < parts->len - 1) { + g_string_append_c(norm_path, G_DIR_SEPARATOR); + } + } + + goto end; + +error: + if (norm_path) { + g_string_free(norm_path, TRUE); + norm_path = NULL; + } + +end: + if (parts) { + g_ptr_array_free(parts, TRUE); + } + + return norm_path; +} +#endif + +BT_HIDDEN +size_t bt_common_get_page_size(void) +{ + int page_size; + + page_size = bt_sysconf(_SC_PAGESIZE); + if (page_size < 0) { + BT_LOGF("Cannot get system's page size: ret=%d", + page_size); + abort(); + } + + return page_size; +} + +#define BUF_STD_APPEND(...) \ + do { \ + char _tmp_fmt[64]; \ + int _count; \ + size_t _size = buf_size - (size_t) (*buf_ch - buf); \ + size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \ + strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \ + _tmp_fmt[_tmp_fmt_size] = '\0'; \ + _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \ + BT_ASSERT(_count >= 0); \ + *buf_ch += MIN(_count, _size); \ + } while (0) + +#define BUF_STD_APPEND_SINGLE_ARG(_type) \ + do { \ + _type _arg = va_arg(*args, _type); \ + BUF_STD_APPEND(_arg); \ + } while (0) + +static inline void handle_conversion_specifier_std(char *buf, char **buf_ch, + size_t buf_size, const char **out_fmt_ch, va_list *args) +{ + const char *fmt_ch = *out_fmt_ch; + enum LENGTH_MODIFIER { + LENGTH_MOD_H, + LENGTH_MOD_HH, + LENGTH_MOD_NONE, + LENGTH_MOD_LOW_L, + LENGTH_MOD_LOW_LL, + LENGTH_MOD_UP_L, + LENGTH_MOD_Z, + } length_mod = LENGTH_MOD_NONE; + + /* skip '%' */ + fmt_ch++; + + if (*fmt_ch == '%') { + fmt_ch++; + **buf_ch = '%'; + (*buf_ch)++; + goto update_rw_fmt; + } + + /* flags */ + for (;;) { + switch (*fmt_ch) { + case '-': + case '+': + case ' ': + case '#': + case '0': + case '\'': + fmt_ch++; + continue; + default: + break; + } + break; + } + + /* width */ + for (;;) { + if (*fmt_ch < '0' || *fmt_ch > '9') { + break; + } + + fmt_ch++; + } + + /* precision */ + if (*fmt_ch == '.') { + fmt_ch++; + + for (;;) { + if (*fmt_ch < '0' || *fmt_ch > '9') { + break; + } + + fmt_ch++; + } + } + + /* format (PRI*64) */ + if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) { + fmt_ch += sizeof(PRId64) - 1; + BUF_STD_APPEND_SINGLE_ARG(int64_t); + goto update_rw_fmt; + } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) { + fmt_ch += sizeof(PRIu64) - 1; + BUF_STD_APPEND_SINGLE_ARG(uint64_t); + goto update_rw_fmt; + } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) { + fmt_ch += sizeof(PRIx64) - 1; + BUF_STD_APPEND_SINGLE_ARG(uint64_t); + goto update_rw_fmt; + } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) { + fmt_ch += sizeof(PRIX64) - 1; + BUF_STD_APPEND_SINGLE_ARG(uint64_t); + goto update_rw_fmt; + } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) { + fmt_ch += sizeof(PRIo64) - 1; + BUF_STD_APPEND_SINGLE_ARG(uint64_t); + goto update_rw_fmt; + } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) { + fmt_ch += sizeof(PRIi64) - 1; + BUF_STD_APPEND_SINGLE_ARG(int64_t); + goto update_rw_fmt; + } + + // length modifier + switch (*fmt_ch) { + case 'h': + length_mod = LENGTH_MOD_H; + fmt_ch++; + + if (*fmt_ch == 'h') { + length_mod = LENGTH_MOD_HH; + fmt_ch++; + break; + } + break; + case 'l': + length_mod = LENGTH_MOD_LOW_L; + fmt_ch++; + + if (*fmt_ch == 'l') { + length_mod = LENGTH_MOD_LOW_LL; + fmt_ch++; + break; + } + break; + case 'L': + length_mod = LENGTH_MOD_UP_L; + fmt_ch++; + break; + case 'z': + length_mod = LENGTH_MOD_Z; + fmt_ch++; + break; + default: + break; + } + + // format + switch (*fmt_ch) { + case 'c': + { + fmt_ch++; + + switch (length_mod) { + case LENGTH_MOD_NONE: + BUF_STD_APPEND_SINGLE_ARG(int); + break; + case LENGTH_MOD_LOW_L: + BUF_STD_APPEND_SINGLE_ARG(wint_t); + break; + default: + abort(); + } + break; + } + case 's': + fmt_ch++; + + switch (length_mod) { + case LENGTH_MOD_NONE: + BUF_STD_APPEND_SINGLE_ARG(char *); + break; + case LENGTH_MOD_LOW_L: + BUF_STD_APPEND_SINGLE_ARG(wchar_t *); + break; + default: + abort(); + } + break; + case 'd': + case 'i': + fmt_ch++; + + switch (length_mod) { + case LENGTH_MOD_NONE: + case LENGTH_MOD_H: + case LENGTH_MOD_HH: + BUF_STD_APPEND_SINGLE_ARG(int); + break; + case LENGTH_MOD_LOW_L: + BUF_STD_APPEND_SINGLE_ARG(long); + break; + case LENGTH_MOD_LOW_LL: + BUF_STD_APPEND_SINGLE_ARG(long long); + break; + case LENGTH_MOD_Z: + BUF_STD_APPEND_SINGLE_ARG(size_t); + break; + default: + abort(); + } + break; + case 'o': + case 'x': + case 'X': + case 'u': + fmt_ch++; + + switch (length_mod) { + case LENGTH_MOD_NONE: + case LENGTH_MOD_H: + case LENGTH_MOD_HH: + BUF_STD_APPEND_SINGLE_ARG(unsigned int); + break; + case LENGTH_MOD_LOW_L: + BUF_STD_APPEND_SINGLE_ARG(unsigned long); + break; + case LENGTH_MOD_LOW_LL: + BUF_STD_APPEND_SINGLE_ARG(unsigned long long); + break; + case LENGTH_MOD_Z: + BUF_STD_APPEND_SINGLE_ARG(size_t); + break; + default: + abort(); + } + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + fmt_ch++; + + switch (length_mod) { + case LENGTH_MOD_NONE: + BUF_STD_APPEND_SINGLE_ARG(double); + break; + case LENGTH_MOD_UP_L: + BUF_STD_APPEND_SINGLE_ARG(long double); + break; + default: + abort(); + } + break; + case 'p': + fmt_ch++; + + if (length_mod == LENGTH_MOD_NONE) { + BUF_STD_APPEND_SINGLE_ARG(void *); + } else { + abort(); + } + break; + default: + abort(); + } + +update_rw_fmt: + *out_fmt_ch = fmt_ch; +} + +BT_HIDDEN +void bt_common_custom_vsnprintf(char *buf, size_t buf_size, + char intro, + bt_common_handle_custom_specifier_func handle_specifier, + void *priv_data, const char *fmt, va_list *args) +{ + const char *fmt_ch = fmt; + char *buf_ch = buf; + + BT_ASSERT(buf); + BT_ASSERT(fmt); + + while (*fmt_ch != '\0') { + switch (*fmt_ch) { + case '%': + BT_ASSERT(fmt_ch[1] != '\0'); + + if (fmt_ch[1] == intro) { + handle_specifier(priv_data, &buf_ch, + buf_size - (size_t) (buf_ch - buf), + &fmt_ch, args); + } else { + handle_conversion_specifier_std(buf, &buf_ch, + buf_size, &fmt_ch, args); + } + + if (buf_ch >= buf + buf_size - 1) { + fmt_ch = ""; + } + break; + default: + *buf_ch = *fmt_ch; + buf_ch++; + if (buf_ch >= buf + buf_size - 1) { + fmt_ch = ""; + } + + fmt_ch++; + } + } + + *buf_ch = '\0'; +} + +BT_HIDDEN +void bt_common_custom_snprintf(char *buf, size_t buf_size, + char intro, + bt_common_handle_custom_specifier_func handle_specifier, + void *priv_data, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier, + priv_data, fmt, &args); + va_end(args); +} diff --git a/src/common/common.h b/src/common/common.h new file mode 100644 index 00000000..13dbaf49 --- /dev/null +++ b/src/common/common.h @@ -0,0 +1,641 @@ +#ifndef BABELTRACE_COMMON_INTERNAL_H +#define BABELTRACE_COMMON_INTERNAL_H + +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/assert.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BT_COMMON_COLOR_RESET "\033[0m" +#define BT_COMMON_COLOR_BOLD "\033[1m" +#define BT_COMMON_COLOR_FG_DEFAULT "\033[39m" +#define BT_COMMON_COLOR_FG_RED "\033[31m" +#define BT_COMMON_COLOR_FG_GREEN "\033[32m" +#define BT_COMMON_COLOR_FG_YELLOW "\033[33m" +#define BT_COMMON_COLOR_FG_BLUE "\033[34m" +#define BT_COMMON_COLOR_FG_MAGENTA "\033[35m" +#define BT_COMMON_COLOR_FG_CYAN "\033[36m" +#define BT_COMMON_COLOR_FG_LIGHT_GRAY "\033[37m" +#define BT_COMMON_COLOR_BG_DEFAULT "\033[49m" +#define BT_COMMON_COLOR_BG_RED "\033[41m" +#define BT_COMMON_COLOR_BG_GREEN "\033[42m" +#define BT_COMMON_COLOR_BG_YELLOW "\033[43m" +#define BT_COMMON_COLOR_BG_BLUE "\033[44m" +#define BT_COMMON_COLOR_BG_MAGENTA "\033[45m" +#define BT_COMMON_COLOR_BG_CYAN "\033[46m" +#define BT_COMMON_COLOR_BG_LIGHT_GRAY "\033[47m" + +struct bt_common_lttng_live_url_parts { + GString *proto; + GString *hostname; + GString *target_hostname; + GString *session_name; + + /* -1 means default port */ + int port; +}; + +/* + * Checks if the current process has setuid or setgid access rights. + * Returns `true` if so. + */ +BT_HIDDEN +bool bt_common_is_setuid_setgid(void); + +/* + * Returns the system-wide plugin path, e.g. + * `/usr/lib/babeltrace2/plugins`. Do not free the return value. + */ +BT_HIDDEN +const char *bt_common_get_system_plugin_path(void); + +/* + * Returns the user plugin path, e.g. + * `/home/user/.local/lib/babeltrace2/plugins`. You need to free the + * return value. + */ +BT_HIDDEN +char *bt_common_get_home_plugin_path(void); + +/* + * Appends the list of directories in `paths` to the array `dirs`. + * `paths` is a list of directories separated by `:`. Returns 0 on + * success. + */ +BT_HIDDEN +int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs); + +/* + * Returns `true` if terminal color codes are supported for this + * process. + */ +BT_HIDDEN +bool bt_common_colors_supported(void); + +BT_HIDDEN +const char *bt_common_color_reset(void); + +BT_HIDDEN +const char *bt_common_color_bold(void); + +BT_HIDDEN +const char *bt_common_color_fg_default(void); + +BT_HIDDEN +const char *bt_common_color_fg_red(void); + +BT_HIDDEN +const char *bt_common_color_fg_green(void); + +BT_HIDDEN +const char *bt_common_color_fg_yellow(void); + +BT_HIDDEN +const char *bt_common_color_fg_blue(void); + +BT_HIDDEN +const char *bt_common_color_fg_magenta(void); + +BT_HIDDEN +const char *bt_common_color_fg_cyan(void); + +BT_HIDDEN +const char *bt_common_color_fg_light_gray(void); + +BT_HIDDEN +const char *bt_common_color_bg_default(void); + +BT_HIDDEN +const char *bt_common_color_bg_red(void); + +BT_HIDDEN +const char *bt_common_color_bg_green(void); + +BT_HIDDEN +const char *bt_common_color_bg_yellow(void); + +BT_HIDDEN +const char *bt_common_color_bg_blue(void); + +BT_HIDDEN +const char *bt_common_color_bg_magenta(void); + +BT_HIDDEN +const char *bt_common_color_bg_cyan(void); + +BT_HIDDEN +const char *bt_common_color_bg_light_gray(void); + +/* + * Returns the substring from `input` to the first character found + * in the list of characters `end_chars`, unescaping any character + * found in `escapable_chars`, and sets `*end_pos` to the position of + * the end (from `input`). The caller owns the returned GString. + */ +BT_HIDDEN +GString *bt_common_string_until(const char *input, const char *escapable_chars, + const char *end_chars, size_t *end_pos); + +/* + * Returns the quoted version of `input` for a shell. If + * `with_single_quotes` is `true`, prepends and appends the `'` prefix + * and suffix to the returned string; otherwise the caller should + * prepend and append them manually, although they are not always + * required. The caller owns the returned GString. + */ +BT_HIDDEN +GString *bt_common_shell_quote(const char *input, bool with_single_quotes); + +/* + * Returns `true` if `input` is a string made only of printable + * characters. + */ +BT_HIDDEN +bool bt_common_string_is_printable(const char *input); + +/* + * Destroys the parts of an LTTng live URL as returned by + * bt_common_parse_lttng_live_url(). + */ +BT_HIDDEN +void bt_common_destroy_lttng_live_url_parts( + struct bt_common_lttng_live_url_parts *parts); + +/* + * Parses the LTTng live URL `url` and returns its different parts. + * If there's an error, writes the error message into `*error_buf` + * up to `error_buf_size` bytes. You must destroy the returned value + * with bt_common_destroy_lttng_live_url_parts(). + */ +BT_HIDDEN +struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url( + const char *url, char *error_buf, size_t error_buf_size); + +/* + * Normalizes (in place) a star globbing pattern to be used with + * bt_common_star_glob_match(). This function always succeeds. + */ +BT_HIDDEN +void bt_common_normalize_star_glob_pattern(char *pattern); + +/* + * Returns `true` if `candidate` (of size `candidate_len`) matches + * the star globbing pattern `pattern` (of size `pattern_len`). + */ +BT_HIDDEN +bool bt_common_star_glob_match(const char *pattern, size_t pattern_len, + const char *candidate, size_t candidate_len); + +/* + * Normalizes the path `path`: + * + * * If it's a relative path, converts it to an absolute path using + * `wd` as the working directory (or the current working directory + * if `wd` is NULL). + * * Removes consecutive and trailing slashes. + * * Resolves `..` and `.` in the path (both in `path` and in `wd`). + * * Does NOT resolve symbolic links. + * + * The caller owns the returned GString. + */ +BT_HIDDEN +GString *bt_common_normalize_path(const char *path, const char *wd); + +typedef void (* bt_common_handle_custom_specifier_func)(void *priv_data, + char **buf, size_t avail_size, const char **fmt, va_list *args); + +/* + * This is a custom vsnprintf() which handles the standard conversion + * specifier as well as custom ones. + * + * `fmt` is a typical printf()-style format string, with the following + * limitations: + * + * * The `*` width specifier is not accepted. + * * The `*` precision specifier is not accepted. + * * The `j` and `t` length modifiers are not accepted. + * * The `n` format specifier is not accepted. + * * The format specifiers defined in are not accepted + * except for `PRId64`, `PRIu64`, `PRIx64`, `PRIX64`, `PRIo64`, and + * `PRIi64`. + * + * `intro` specifies which special character immediately following an + * introductory `%` character in `fmt` is used to indicate a custom + * conversion specifier. For example, if `intro` is '@', then any `%@` + * sequence in `fmt` is the beginning of a custom conversion specifier. + * + * When a custom conversion specifier is encountered in `fmt`, + * the function calls `handle_specifier`. This callback receives: + * + * `priv_data`: + * Custom, private data. + * + * `buf`: + * Address of the current buffer pointer. `*buf` is the position to + * append new data. The callback must update `*buf` when appending + * new data. The callback must ensure not to write passed the whole + * buffer passed to bt_common_custom_vsnprintf(). + * + * `avail_size`: + * Number of bytes left in whole buffer from the `*buf` point. + * + * `fmt`: + * Address of the current format string pointer. `*fmt` points to + * the introductory `%` character, which is followed by the + * character `intro`. The callback must update `*fmt` so that it + * points after the whole custom conversion specifier. + * + * `args`: + * Variable argument list. Use va_arg() to get new arguments from + * this list and update it at the same time. + * + * Because this is an internal utility, this function and its callback + * do not return error codes: they abort when there's any error (bad + * format string, for example). + */ +BT_HIDDEN +void bt_common_custom_vsnprintf(char *buf, size_t buf_size, + char intro, + bt_common_handle_custom_specifier_func handle_specifier, + void *priv_data, const char *fmt, va_list *args); + +/* + * Variadic form of bt_common_custom_vsnprintf(). + */ +BT_HIDDEN +void bt_common_custom_snprintf(char *buf, size_t buf_size, + char intro, + bt_common_handle_custom_specifier_func handle_specifier, + void *priv_data, const char *fmt, ...); + +/* + * Returns the system page size. + */ +BT_HIDDEN +size_t bt_common_get_page_size(void); + +/* + * Wraps read() function to handle EINTR and partial reads. + * On success, it returns `count` received as parameter. On error, it returns a + * value smaller than the requested `count`. + */ +static inline +ssize_t bt_common_read(int fd, void *buf, size_t count) +{ + size_t i = 0; + ssize_t ret; + + BT_ASSERT(buf); + + /* Never return an overflow value. */ + BT_ASSERT(count <= SSIZE_MAX); + + do { + ret = read(fd, buf + i, count - i); + if (ret < 0) { + if (errno == EINTR) { +#ifdef BT_LOGD_STR + BT_LOGD_STR("read() call interrupted. Retrying..."); +#endif + /* retry operation */ + continue; + } else { +#ifdef BT_LOGE_ERRNO + BT_LOGE_ERRNO("Error while reading", ": fd=%d", + fd); +#endif + goto end; + } + } + i += ret; + BT_ASSERT(i <= count); + } while (count - i > 0 && ret > 0); + +end: + if (ret >= 0) { + if (i == 0) { + ret = -1; + } else { + ret = i; + } + } + + return ret; +} + +static inline +const char *bt_common_field_class_type_string(enum bt_field_class_type class_type) +{ + switch (class_type) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + return "BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER"; + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + return "BT_FIELD_CLASS_TYPE_SIGNED_INTEGER"; + case BT_FIELD_CLASS_TYPE_REAL: + return "BT_FIELD_CLASS_TYPE_REAL"; + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + return "BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION"; + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + return "BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION"; + case BT_FIELD_CLASS_TYPE_STRING: + return "BT_FIELD_CLASS_TYPE_STRING"; + case BT_FIELD_CLASS_TYPE_STRUCTURE: + return "BT_FIELD_CLASS_TYPE_STRUCTURE"; + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + return "BT_FIELD_CLASS_TYPE_STATIC_ARRAY"; + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + return "BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY"; + case BT_FIELD_CLASS_TYPE_VARIANT: + return "BT_FIELD_CLASS_TYPE_VARIANT"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_common_field_class_integer_preferred_display_base_string(enum bt_field_class_integer_preferred_display_base base) +{ + switch (base) { + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: + return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY"; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: + return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL"; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL: + return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL"; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: + return "BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_common_scope_string(enum bt_scope scope) +{ + switch (scope) { + case BT_SCOPE_PACKET_CONTEXT: + return "BT_SCOPE_PACKET_CONTEXT"; + case BT_SCOPE_EVENT_COMMON_CONTEXT: + return "BT_SCOPE_EVENT_COMMON_CONTEXT"; + case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: + return "BT_SCOPE_EVENT_SPECIFIC_CONTEXT"; + case BT_SCOPE_EVENT_PAYLOAD: + return "BT_SCOPE_EVENT_PAYLOAD"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_common_event_class_log_level_string( + enum bt_event_class_log_level level) +{ + switch (level) { + case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY: + return "BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY"; + case BT_EVENT_CLASS_LOG_LEVEL_ALERT: + return "BT_EVENT_CLASS_LOG_LEVEL_ALERT"; + case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL: + return "BT_EVENT_CLASS_LOG_LEVEL_CRITICAL"; + case BT_EVENT_CLASS_LOG_LEVEL_ERROR: + return "BT_EVENT_CLASS_LOG_LEVEL_ERROR"; + case BT_EVENT_CLASS_LOG_LEVEL_WARNING: + return "BT_EVENT_CLASS_LOG_LEVEL_WARNING"; + case BT_EVENT_CLASS_LOG_LEVEL_NOTICE: + return "BT_EVENT_CLASS_LOG_LEVEL_NOTICE"; + case BT_EVENT_CLASS_LOG_LEVEL_INFO: + return "BT_EVENT_CLASS_LOG_LEVEL_INFO"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE"; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG: + return "BT_EVENT_CLASS_LOG_LEVEL_DEBUG"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_common_value_type_string(enum bt_value_type type) +{ + switch (type) { + case BT_VALUE_TYPE_NULL: + return "BT_VALUE_TYPE_NULL"; + case BT_VALUE_TYPE_BOOL: + return "BT_VALUE_TYPE_BOOL"; + case BT_VALUE_TYPE_UNSIGNED_INTEGER: + return "BT_VALUE_TYPE_UNSIGNED_INTEGER"; + case BT_VALUE_TYPE_SIGNED_INTEGER: + return "BT_VALUE_TYPE_SIGNED_INTEGER"; + case BT_VALUE_TYPE_REAL: + return "BT_VALUE_TYPE_REAL"; + case BT_VALUE_TYPE_STRING: + return "BT_VALUE_TYPE_STRING"; + case BT_VALUE_TYPE_ARRAY: + return "BT_VALUE_TYPE_ARRAY"; + case BT_VALUE_TYPE_MAP: + return "BT_VALUE_TYPE_MAP"; + default: + return "(unknown)"; + } +}; + +static inline +GString *bt_common_field_path_string(struct bt_field_path *path) +{ + GString *str = g_string_new(NULL); + uint64_t i; + + BT_ASSERT(path); + + if (!str) { + goto end; + } + + g_string_append_printf(str, "[%s", bt_common_scope_string( + bt_field_path_get_root_scope(path))); + + for (i = 0; i < bt_field_path_get_item_count(path); i++) { + const struct bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_const(path, i); + + switch (bt_field_path_item_get_type(fp_item)) { + case BT_FIELD_PATH_ITEM_TYPE_INDEX: + g_string_append_printf(str, ", %" PRIu64, + bt_field_path_item_index_get_index(fp_item)); + break; + case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: + g_string_append(str, ", "); + break; + default: + abort(); + } + } + + g_string_append(str, "]"); + +end: + return str; +} + +static inline +const char *bt_common_self_message_iterator_status_string( + enum bt_self_message_iterator_status status) +{ + switch (status) { + case BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN: + return "BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN"; + case BT_SELF_MESSAGE_ITERATOR_STATUS_END: + return "BT_SELF_MESSAGE_ITERATOR_STATUS_END"; + case BT_SELF_MESSAGE_ITERATOR_STATUS_OK: + return "BT_SELF_MESSAGE_ITERATOR_STATUS_OK"; + case BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR: + return "BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR"; + case BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM: + return "BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM"; + default: + return "(unknown)"; + } +}; + +#define NS_PER_S_I INT64_C(1000000000) +#define NS_PER_S_U UINT64_C(1000000000) + +static inline +int bt_common_clock_value_from_ns_from_origin( + int64_t cc_offset_seconds, uint64_t cc_offset_cycles, + uint64_t cc_freq, int64_t ns_from_origin, + uint64_t *raw_value) +{ + int ret = 0; + int64_t offset_in_ns; + uint64_t value_in_ns; + uint64_t rem_value_in_ns; + uint64_t value_periods; + uint64_t value_period_cycles; + int64_t ns_to_add; + + BT_ASSERT(raw_value); + + /* Compute offset part of requested value, in nanoseconds */ + if (!bt_safe_to_mul_int64(cc_offset_seconds, NS_PER_S_I)) { + ret = -1; + goto end; + } + + offset_in_ns = cc_offset_seconds * NS_PER_S_I; + + if (cc_freq == NS_PER_S_U) { + ns_to_add = (int64_t) cc_offset_cycles; + } else { + if (!bt_safe_to_mul_int64((int64_t) cc_offset_cycles, + NS_PER_S_I)) { + ret = -1; + goto end; + } + + ns_to_add = ((int64_t) cc_offset_cycles * NS_PER_S_I) / + (int64_t) cc_freq; + } + + if (!bt_safe_to_add_int64(offset_in_ns, ns_to_add)) { + ret = -1; + goto end; + } + + offset_in_ns += ns_to_add; + + /* Value part in nanoseconds */ + if (ns_from_origin < offset_in_ns) { + ret = -1; + goto end; + } + + value_in_ns = (uint64_t) (ns_from_origin - offset_in_ns); + + /* Number of whole clock periods in `value_in_ns` */ + value_periods = value_in_ns / NS_PER_S_U; + + /* Remaining nanoseconds in cycles + whole clock periods in cycles */ + rem_value_in_ns = value_in_ns - value_periods * NS_PER_S_U; + + if (value_periods > UINT64_MAX / cc_freq) { + ret = -1; + goto end; + } + + if (!bt_safe_to_mul_uint64(value_periods, cc_freq)) { + ret = -1; + goto end; + } + + value_period_cycles = value_periods * cc_freq; + + if (!bt_safe_to_mul_uint64(cc_freq, rem_value_in_ns)) { + ret = -1; + goto end; + } + + if (!bt_safe_to_add_uint64(cc_freq * rem_value_in_ns / NS_PER_S_U, + value_period_cycles)) { + ret = -1; + goto end; + } + + *raw_value = cc_freq * rem_value_in_ns / NS_PER_S_U + + value_period_cycles; + +end: + return ret; +} + +static inline +enum bt_self_message_iterator_status bt_common_message_iterator_status_to_self( + enum bt_message_iterator_status status) +{ + return (int) status; +} +#endif /* BABELTRACE_COMMON_INTERNAL_H */ diff --git a/src/common/list.h b/src/common/list.h new file mode 100644 index 00000000..48d9bbb9 --- /dev/null +++ b/src/common/list.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2002 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * Contributed by Ulrich Drepper , 2002. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; only + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _BT_LIST_H +#define _BT_LIST_H 1 + +/* The definitions of this file are adopted from those which can be + found in the Linux kernel headers to enable people familiar with + the latter find their way in these sources as well. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Basic type for the double-link list. */ +struct bt_list_head +{ + struct bt_list_head *next; + struct bt_list_head *prev; +}; + + +/* Define a variable with the head and tail of the list. */ +#define BT_LIST_HEAD(name) \ + struct bt_list_head name = { &(name), &(name) } + +/* Initialize a new list head. */ +#define BT_INIT_LIST_HEAD(ptr) \ + (ptr)->next = (ptr)->prev = (ptr) + +#define BT_LIST_HEAD_INIT(name) { .prev = &(name), .next = &(name) } + +/* Add new element at the head of the list. */ +static inline void +bt_list_add (struct bt_list_head *newp, struct bt_list_head *head) +{ + head->next->prev = newp; + newp->next = head->next; + newp->prev = head; + head->next = newp; +} + + +/* Add new element at the tail of the list. */ +static inline void +bt_list_add_tail (struct bt_list_head *newp, struct bt_list_head *head) +{ + head->prev->next = newp; + newp->next = head; + newp->prev = head->prev; + head->prev = newp; +} + + +/* Remove element from list. */ +static inline void +__bt_list_del (struct bt_list_head *prev, struct bt_list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/* Remove element from list. */ +static inline void +bt_list_del (struct bt_list_head *elem) +{ + __bt_list_del (elem->prev, elem->next); +} + +/* delete from list, add to another list as head */ +static inline void +bt_list_move (struct bt_list_head *elem, struct bt_list_head *head) +{ + __bt_list_del (elem->prev, elem->next); + bt_list_add (elem, head); +} + +/* replace an old entry. + */ +static inline void +bt_list_replace(struct bt_list_head *old, struct bt_list_head *_new) +{ + _new->next = old->next; + _new->prev = old->prev; + _new->prev->next = _new; + _new->next->prev = _new; +} + +/* Join two lists. */ +static inline void +bt_list_splice (struct bt_list_head *add, struct bt_list_head *head) +{ + /* Do nothing if the list which gets added is empty. */ + if (add != add->next) + { + add->next->prev = head; + add->prev->next = head->next; + head->next->prev = add->prev; + head->next = add->next; + } +} + + +/* Get typed element from list at a given position. */ +#define bt_list_entry(ptr, type, member) \ + ((type *) ((char *) (ptr) - (uintptr_t) (&((type *) 0)->member))) + + + +/* Iterate forward over the elements of the list. */ +#define bt_list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + + +/* Iterate forward over the elements of the list. */ +#define bt_list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + + +/* Iterate backwards over the elements list. The list elements can be + removed from the list while doing this. */ +#define bt_list_for_each_prev_safe(pos, p, head) \ + for (pos = (head)->prev, p = pos->prev; \ + pos != (head); \ + pos = p, p = pos->prev) + +#define bt_list_for_each_entry(pos, head, member) \ + for (pos = bt_list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = bt_list_entry(pos->member.next, typeof(*pos), member)) + +#define bt_list_for_each_entry_reverse(pos, head, member) \ + for (pos = bt_list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = bt_list_entry(pos->member.prev, typeof(*pos), member)) + +#define bt_list_for_each_entry_safe(pos, p, head, member) \ + for (pos = bt_list_entry((head)->next, typeof(*pos), member), \ + p = bt_list_entry(pos->member.next,typeof(*pos), member); \ + &pos->member != (head); \ + pos = p, p = bt_list_entry(pos->member.next, typeof(*pos), member)) + +static inline int bt_list_empty(struct bt_list_head *head) +{ + return head == head->next; +} + +static inline void bt_list_replace_init(struct bt_list_head *old, + struct bt_list_head *_new) +{ + struct bt_list_head *head = old->next; + bt_list_del(old); + bt_list_add_tail(_new, head); + BT_INIT_LIST_HEAD(old); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _BT_LIST_H */ diff --git a/src/common/logging.c b/src/common/logging.c new file mode 100644 index 00000000..7af804b9 --- /dev/null +++ b/src/common/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_common_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_common_log_level, "BABELTRACE_COMMON_LOG_LEVEL"); diff --git a/src/common/logging.h b/src/common/logging.h new file mode 100644 index 00000000..48e1f7b2 --- /dev/null +++ b/src/common/logging.h @@ -0,0 +1,31 @@ +#ifndef COMMON_LOGGING_H +#define COMMON_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_common_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_common_log_level); + +#endif /* COMMON_LOGGING_H */ diff --git a/src/common/mmap-align.h b/src/common/mmap-align.h new file mode 100644 index 00000000..1bb2b72f --- /dev/null +++ b/src/common/mmap-align.h @@ -0,0 +1,132 @@ +#ifndef _BABELTRACE_MMAP_ALIGN_H +#define _BABELTRACE_MMAP_ALIGN_H + +/* + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/align.h" +#include +#include +#include "compat/mman.h" +#include "common/common.h" + +/* + * This header implements a wrapper over mmap (mmap_align) that memory + * maps a file region that is not necessarily multiple of the page size. + * It returns a structure (instead of a pointer) that contains the mmap + * pointer (page-aligned) and a pointer to the offset requested within + * that page. Note: in the current implementation, the "addr" parameter + * cannot be forced, so we allocate at an address chosen by the OS. + */ + +struct mmap_align { + void *page_aligned_addr; /* mmap address, aligned to floor */ + size_t page_aligned_length; /* mmap length, containing range */ + + void *addr; /* virtual mmap address */ + size_t length; /* virtual mmap length */ +}; + +#ifdef __WIN32__ +#include + +/* + * On windows the memory mapping offset must be aligned to the memory + * allocator allocation granularity and not the page size. + */ +static inline +off_t get_page_aligned_offset(off_t offset, size_t page_size) +{ + SYSTEM_INFO sysinfo; + + GetNativeSystemInfo(&sysinfo); + + return ALIGN_FLOOR(offset, sysinfo.dwAllocationGranularity); +} +#else +static inline +off_t get_page_aligned_offset(off_t offset, size_t page_size) +{ + return ALIGN_FLOOR(offset, page_size); +} +#endif + +static inline +struct mmap_align *mmap_align(size_t length, int prot, + int flags, int fd, off_t offset) +{ + struct mmap_align *mma; + off_t page_aligned_offset; /* mmap offset, aligned to floor */ + size_t page_size; + + page_size = bt_common_get_page_size(); + + mma = malloc(sizeof(*mma)); + if (!mma) + return MAP_FAILED; + mma->length = length; + page_aligned_offset = get_page_aligned_offset(offset, page_size); + /* + * Page aligned length needs to contain the requested range. + * E.g., for a small range that fits within a single page, we might + * require a 2 pages page_aligned_length if the range crosses a page + * boundary. + */ + mma->page_aligned_length = ALIGN(length + offset - page_aligned_offset, page_size); + mma->page_aligned_addr = bt_mmap(NULL, mma->page_aligned_length, + prot, flags, fd, page_aligned_offset); + if (mma->page_aligned_addr == MAP_FAILED) { + free(mma); + return MAP_FAILED; + } + mma->addr = ((uint8_t *) mma->page_aligned_addr) + (offset - page_aligned_offset); + return mma; +} + +static inline +int munmap_align(struct mmap_align *mma) +{ + void *page_aligned_addr; + size_t page_aligned_length; + + page_aligned_addr = mma->page_aligned_addr; + page_aligned_length = mma->page_aligned_length; + free(mma); + return bt_munmap(page_aligned_addr, page_aligned_length); +} + +static inline +void *mmap_align_addr(struct mmap_align *mma) +{ + return mma->addr; +} + +/* + * Helper for special-cases, normally unused. + */ +static inline +void mmap_align_set_addr(struct mmap_align *mma, void *addr) +{ + mma->addr = addr; +} + +#endif /* _BABELTRACE_MMAP_ALIGN_H */ diff --git a/src/common/version.h b/src/common/version.h new file mode 100644 index 00000000..acae2c40 --- /dev/null +++ b/src/common/version.h @@ -0,0 +1,28 @@ +#ifndef VERSION_H +#define VERSION_H + +/* + * Copyright (C) 2018 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/version.i" + +#endif /* VERSION_H */ diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am new file mode 100644 index 00000000..040ccc8d --- /dev/null +++ b/src/compat/Makefile.am @@ -0,0 +1,32 @@ +noinst_LTLIBRARIES = libcompat.la + +libcompat_la_SOURCES = \ + logging.c \ + logging.h \ + mman.c \ + mman.h \ + uuid.c \ + uuid.h + +libcompat_la_LDFLAGS = \ + $(LD_NO_AS_NEEDED) + +if BABELTRACE_BUILD_WITH_MINGW +libcompat_la_LDFLAGS += -lrpcrt4 +endif + +noinst_HEADERS = \ + bitfield.h \ + compiler.h \ + endian.h \ + fcntl.h \ + glib.h \ + limits.h \ + memstream.h \ + socket.h \ + stdio.h \ + stdlib.h \ + string.h \ + time.h \ + unistd.h \ + utc.h diff --git a/src/compat/bitfield.h b/src/compat/bitfield.h new file mode 100644 index 00000000..a24904bd --- /dev/null +++ b/src/compat/bitfield.h @@ -0,0 +1,551 @@ +#ifndef _BABELTRACE_BITFIELD_H +#define _BABELTRACE_BITFIELD_H + +/* + * Copyright 2010-2019 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include /* C99 5.2.4.2 Numerical limits */ +#include /* C99 7.16 bool type */ +#include "compat/limits.h" /* C99 5.2.4.2 Numerical limits */ +#include "compat/endian.h" /* Non-standard BIG_ENDIAN, LITTLE_ENDIAN, BYTE_ORDER */ + +/* + * This header strictly follows the C99 standard, except for use of the + * compiler-specific __typeof__. + */ + +/* + * This bitfield header requires the compiler representation of signed + * integers to be two's complement. + */ +#if (-1 != ~0) +#error "bitfield.h requires the compiler representation of signed integers to be two's complement." +#endif + +/* + * _bt_is_signed_type() willingly generates comparison of unsigned + * expression < 0, which is always false. Silence compiler warnings. + */ +#ifdef __GNUC__ +# define _BT_DIAG_PUSH _Pragma("GCC diagnostic push") +# define _BT_DIAG_POP _Pragma("GCC diagnostic pop") + +# define _BT_DIAG_STRINGIFY_1(x) #x +# define _BT_DIAG_STRINGIFY(x) _BT_DIAG_STRINGIFY_1(x) + +# define _BT_DIAG_IGNORE(option) \ + _Pragma(_BT_DIAG_STRINGIFY(GCC diagnostic ignored option)) +# define _BT_DIAG_IGNORE_TYPE_LIMITS _BT_DIAG_IGNORE("-Wtype-limits") +#else +# define _BT_DIAG_PUSH +# define _BT_DIAG_POP +# define _BT_DIAG_IGNORE +#endif + +#define _bt_is_signed_type(type) ((type) -1 < (type) 0) + +/* + * Produce a build-time error if the condition `cond` is non-zero. + * Evaluates as a size_t expression. + */ +#define _BT_BUILD_ASSERT(cond) \ + sizeof(struct { int f:(2 * !!(cond) - 1); }) + +/* + * Cast value `v` to an unsigned integer of the same size as `v`. + */ +#define _bt_cast_value_to_unsigned(v) \ + (sizeof(v) == sizeof(uint8_t) ? (uint8_t) (v) : \ + sizeof(v) == sizeof(uint16_t) ? (uint16_t) (v) : \ + sizeof(v) == sizeof(uint32_t) ? (uint32_t) (v) : \ + sizeof(v) == sizeof(uint64_t) ? (uint64_t) (v) : \ + _BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t))) + +/* + * Cast value `v` to an unsigned integer type of the size of type `type` + * *without* sign-extension. + * + * The unsigned cast ensures that we're not shifting a negative value, + * which is undefined in C. However, this limits the maximum type size + * of `type` to 64-bit. Generate a compile-time error if the size of + * `type` is larger than 64-bit. + */ +#define _bt_cast_value_to_unsigned_type(type, v) \ + (sizeof(type) == sizeof(uint8_t) ? \ + (uint8_t) _bt_cast_value_to_unsigned(v) : \ + sizeof(type) == sizeof(uint16_t) ? \ + (uint16_t) _bt_cast_value_to_unsigned(v) : \ + sizeof(type) == sizeof(uint32_t) ? \ + (uint32_t) _bt_cast_value_to_unsigned(v) : \ + sizeof(type) == sizeof(uint64_t) ? \ + (uint64_t) _bt_cast_value_to_unsigned(v) : \ + _BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t))) + +/* + * _bt_fill_mask evaluates to a "type" integer with all bits set. + */ +#define _bt_fill_mask(type) ((type) ~(type) 0) + +/* + * Left shift a value `v` of `shift` bits. + * + * The type of `v` can be signed or unsigned integer. + * The value of `shift` must be less than the size of `v` (in bits), + * otherwise the behavior is undefined. + * Evaluates to the result of the shift operation. + * + * According to the C99 standard, left shift of a left hand-side signed + * type is undefined if it has a negative value or if the result cannot + * be represented in the result type. This bitfield header discards the + * bits that are left-shifted beyond the result type representation, + * which is the behavior of an unsigned type left shift operation. + * Therefore, always perform left shift on an unsigned type. + * + * This macro should not be used if `shift` can be greater or equal than + * the bitwidth of `v`. See `_bt_safe_lshift`. + */ +#define _bt_lshift(v, shift) \ + ((__typeof__(v)) (_bt_cast_value_to_unsigned(v) << (shift))) + +/* + * Generate a mask of type `type` with the `length` least significant bits + * cleared, and the most significant bits set. + */ +#define _bt_make_mask_complement(type, length) \ + _bt_lshift(_bt_fill_mask(type), length) + +/* + * Generate a mask of type `type` with the `length` least significant bits + * set, and the most significant bits cleared. + */ +#define _bt_make_mask(type, length) \ + ((type) ~_bt_make_mask_complement(type, length)) + +/* + * Right shift a value `v` of `shift` bits. + * + * The type of `v` can be signed or unsigned integer. + * The value of `shift` must be less than the size of `v` (in bits), + * otherwise the behavior is undefined. + * Evaluates to the result of the shift operation. + * + * According to the C99 standard, right shift of a left hand-side signed + * type which has a negative value is implementation defined. This + * bitfield header relies on the right shift implementation carrying the + * sign bit. If the compiler implementation has a different behavior, + * emulate carrying the sign bit. + * + * This macro should not be used if `shift` can be greater or equal than + * the bitwidth of `v`. See `_bt_safe_rshift`. + */ +#if ((-1 >> 1) == -1) +#define _bt_rshift(v, shift) ((v) >> (shift)) +#else +#define _bt_rshift(v, shift) \ + ((__typeof__(v)) ((_bt_cast_value_to_unsigned(v) >> (shift)) | \ + ((v) < 0 ? _bt_make_mask_complement(__typeof__(v), \ + sizeof(v) * CHAR_BIT - (shift)) : 0))) +#endif + +/* + * Right shift a signed or unsigned integer with `shift` value being an + * arbitrary number of bits. `v` is modified by this macro. The shift + * is transformed into a sequence of `_nr_partial_shifts` consecutive + * shift operations, each of a number of bits smaller than the bitwidth + * of `v`, ending with a shift of the number of left over bits. + */ +#define _bt_safe_rshift(v, shift) \ +do { \ + unsigned long _nr_partial_shifts = (shift) / (sizeof(v) * CHAR_BIT - 1); \ + unsigned long _leftover_bits = (shift) % (sizeof(v) * CHAR_BIT - 1); \ + \ + for (; _nr_partial_shifts; _nr_partial_shifts--) \ + (v) = _bt_rshift(v, sizeof(v) * CHAR_BIT - 1); \ + (v) = _bt_rshift(v, _leftover_bits); \ +} while (0) + +/* + * Left shift a signed or unsigned integer with `shift` value being an + * arbitrary number of bits. `v` is modified by this macro. The shift + * is transformed into a sequence of `_nr_partial_shifts` consecutive + * shift operations, each of a number of bits smaller than the bitwidth + * of `v`, ending with a shift of the number of left over bits. + */ +#define _bt_safe_lshift(v, shift) \ +do { \ + unsigned long _nr_partial_shifts = (shift) / (sizeof(v) * CHAR_BIT - 1); \ + unsigned long _leftover_bits = (shift) % (sizeof(v) * CHAR_BIT - 1); \ + \ + for (; _nr_partial_shifts; _nr_partial_shifts--) \ + (v) = _bt_lshift(v, sizeof(v) * CHAR_BIT - 1); \ + (v) = _bt_lshift(v, _leftover_bits); \ +} while (0) + +/* + * bt_bitfield_write - write integer to a bitfield in native endianness + * + * Save integer to the bitfield, which starts at the "start" bit, has "len" + * bits. + * The inside of a bitfield is from high bits to low bits. + * Uses native endianness. + * For unsigned "v", pad MSB with 0 if bitfield is larger than v. + * For signed "v", sign-extend v if bitfield is larger than v. + * + * On little endian, bytes are placed from the less significant to the most + * significant. Also, consecutive bitfields are placed from lower bits to higher + * bits. + * + * On big endian, bytes are places from most significant to less significant. + * Also, consecutive bitfields are placed from higher to lower bits. + */ + +#define _bt_bitfield_write_le(ptr, type, start, length, v) \ +do { \ + __typeof__(v) _v = (v); \ + type *_ptr = (void *) (ptr); \ + unsigned long _start = (start), _length = (length); \ + type _mask, _cmask; \ + unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ + unsigned long _start_unit, _end_unit, _this_unit; \ + unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ + \ + if (!_length) \ + break; \ + \ + _end = _start + _length; \ + _start_unit = _start / _ts; \ + _end_unit = (_end + (_ts - 1)) / _ts; \ + \ + /* Trim v high bits */ \ + if (_length < sizeof(_v) * CHAR_BIT) \ + _v &= _bt_make_mask(__typeof__(_v), _length); \ + \ + /* We can now append v with a simple "or", shift it piece-wise */ \ + _this_unit = _start_unit; \ + if (_start_unit == _end_unit - 1) { \ + _mask = _bt_make_mask(type, _start % _ts); \ + if (_end % _ts) \ + _mask |= _bt_make_mask_complement(type, _end % _ts); \ + _cmask = _bt_lshift((type) (_v), _start % _ts); \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + break; \ + } \ + if (_start % _ts) { \ + _cshift = _start % _ts; \ + _mask = _bt_make_mask(type, _cshift); \ + _cmask = _bt_lshift((type) (_v), _cshift); \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + _bt_safe_rshift(_v, _ts - _cshift); \ + _start += _ts - _cshift; \ + _this_unit++; \ + } \ + for (; _this_unit < _end_unit - 1; _this_unit++) { \ + _ptr[_this_unit] = (type) _v; \ + _bt_safe_rshift(_v, _ts); \ + _start += _ts; \ + } \ + if (_end % _ts) { \ + _mask = _bt_make_mask_complement(type, _end % _ts); \ + _cmask = (type) _v; \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + } else \ + _ptr[_this_unit] = (type) _v; \ +} while (0) + +#define _bt_bitfield_write_be(ptr, type, start, length, v) \ +do { \ + __typeof__(v) _v = (v); \ + type *_ptr = (void *) (ptr); \ + unsigned long _start = (start), _length = (length); \ + type _mask, _cmask; \ + unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ + unsigned long _start_unit, _end_unit, _this_unit; \ + unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ + \ + if (!_length) \ + break; \ + \ + _end = _start + _length; \ + _start_unit = _start / _ts; \ + _end_unit = (_end + (_ts - 1)) / _ts; \ + \ + /* Trim v high bits */ \ + if (_length < sizeof(_v) * CHAR_BIT) \ + _v &= _bt_make_mask(__typeof__(_v), _length); \ + \ + /* We can now append v with a simple "or", shift it piece-wise */ \ + _this_unit = _end_unit - 1; \ + if (_start_unit == _end_unit - 1) { \ + _mask = _bt_make_mask(type, (_ts - (_end % _ts)) % _ts); \ + if (_start % _ts) \ + _mask |= _bt_make_mask_complement(type, _ts - (_start % _ts)); \ + _cmask = _bt_lshift((type) (_v), (_ts - (_end % _ts)) % _ts); \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + break; \ + } \ + if (_end % _ts) { \ + _cshift = _end % _ts; \ + _mask = _bt_make_mask(type, _ts - _cshift); \ + _cmask = _bt_lshift((type) (_v), _ts - _cshift); \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + _bt_safe_rshift(_v, _cshift); \ + _end -= _cshift; \ + _this_unit--; \ + } \ + for (; (long) _this_unit >= (long) _start_unit + 1; _this_unit--) { \ + _ptr[_this_unit] = (type) _v; \ + _bt_safe_rshift(_v, _ts); \ + _end -= _ts; \ + } \ + if (_start % _ts) { \ + _mask = _bt_make_mask_complement(type, _ts - (_start % _ts)); \ + _cmask = (type) _v; \ + _cmask &= ~_mask; \ + _ptr[_this_unit] &= _mask; \ + _ptr[_this_unit] |= _cmask; \ + } else \ + _ptr[_this_unit] = (type) _v; \ +} while (0) + +/* + * bt_bitfield_write - write integer to a bitfield in native endianness + * bt_bitfield_write_le - write integer to a bitfield in little endian + * bt_bitfield_write_be - write integer to a bitfield in big endian + */ + +#if (BYTE_ORDER == LITTLE_ENDIAN) + +#define bt_bitfield_write(ptr, type, start, length, v) \ + _bt_bitfield_write_le(ptr, type, start, length, v) + +#define bt_bitfield_write_le(ptr, type, start, length, v) \ + _bt_bitfield_write_le(ptr, type, start, length, v) + +#define bt_bitfield_write_be(ptr, type, start, length, v) \ + _bt_bitfield_write_be(ptr, unsigned char, start, length, v) + +#elif (BYTE_ORDER == BIG_ENDIAN) + +#define bt_bitfield_write(ptr, type, start, length, v) \ + _bt_bitfield_write_be(ptr, type, start, length, v) + +#define bt_bitfield_write_le(ptr, type, start, length, v) \ + _bt_bitfield_write_le(ptr, unsigned char, start, length, v) + +#define bt_bitfield_write_be(ptr, type, start, length, v) \ + _bt_bitfield_write_be(ptr, type, start, length, v) + +#else /* (BYTE_ORDER == PDP_ENDIAN) */ + +#error "Byte order not supported" + +#endif + +#define _bt_bitfield_read_le(ptr, type, start, length, vptr) \ +do { \ + __typeof__(*(vptr)) *_vptr = (vptr); \ + __typeof__(*_vptr) _v; \ + type *_ptr = (void *) (ptr); \ + unsigned long _start = (start), _length = (length); \ + type _mask, _cmask; \ + unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ + unsigned long _start_unit, _end_unit, _this_unit; \ + unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ + bool _is_signed_type; \ + \ + if (!_length) { \ + *_vptr = 0; \ + break; \ + } \ + \ + _end = _start + _length; \ + _start_unit = _start / _ts; \ + _end_unit = (_end + (_ts - 1)) / _ts; \ + \ + _this_unit = _end_unit - 1; \ + _BT_DIAG_PUSH \ + _BT_DIAG_IGNORE_TYPE_LIMITS \ + _is_signed_type = _bt_is_signed_type(__typeof__(_v)); \ + _BT_DIAG_POP \ + if (_is_signed_type \ + && (_ptr[_this_unit] & _bt_lshift((type) 1, (_end % _ts ? _end % _ts : _ts) - 1))) \ + _v = ~(__typeof__(_v)) 0; \ + else \ + _v = 0; \ + if (_start_unit == _end_unit - 1) { \ + _cmask = _ptr[_this_unit]; \ + _cmask = _bt_rshift(_cmask, _start % _ts); \ + if ((_end - _start) % _ts) { \ + _mask = _bt_make_mask(type, _end - _start); \ + _cmask &= _mask; \ + } \ + _bt_safe_lshift(_v, _end - _start); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + *_vptr = _v; \ + break; \ + } \ + if (_end % _ts) { \ + _cshift = _end % _ts; \ + _mask = _bt_make_mask(type, _cshift); \ + _cmask = _ptr[_this_unit]; \ + _cmask &= _mask; \ + _bt_safe_lshift(_v, _cshift); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + _end -= _cshift; \ + _this_unit--; \ + } \ + for (; (long) _this_unit >= (long) _start_unit + 1; _this_unit--) { \ + _bt_safe_lshift(_v, _ts); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ + _end -= _ts; \ + } \ + if (_start % _ts) { \ + _mask = _bt_make_mask(type, _ts - (_start % _ts)); \ + _cmask = _ptr[_this_unit]; \ + _cmask = _bt_rshift(_cmask, _start % _ts); \ + _cmask &= _mask; \ + _bt_safe_lshift(_v, _ts - (_start % _ts)); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + } else { \ + _bt_safe_lshift(_v, _ts); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ + } \ + *_vptr = _v; \ +} while (0) + +#define _bt_bitfield_read_be(ptr, type, start, length, vptr) \ +do { \ + __typeof__(*(vptr)) *_vptr = (vptr); \ + __typeof__(*_vptr) _v; \ + type *_ptr = (void *) (ptr); \ + unsigned long _start = (start), _length = (length); \ + type _mask, _cmask; \ + unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */ \ + unsigned long _start_unit, _end_unit, _this_unit; \ + unsigned long _end, _cshift; /* _cshift is "complement shift" */ \ + bool _is_signed_type; \ + \ + if (!_length) { \ + *_vptr = 0; \ + break; \ + } \ + \ + _end = _start + _length; \ + _start_unit = _start / _ts; \ + _end_unit = (_end + (_ts - 1)) / _ts; \ + \ + _this_unit = _start_unit; \ + _BT_DIAG_PUSH \ + _BT_DIAG_IGNORE_TYPE_LIMITS \ + _is_signed_type = _bt_is_signed_type(__typeof__(_v)); \ + _BT_DIAG_POP \ + if (_is_signed_type \ + && (_ptr[_this_unit] & _bt_lshift((type) 1, _ts - (_start % _ts) - 1))) \ + _v = ~(__typeof__(_v)) 0; \ + else \ + _v = 0; \ + if (_start_unit == _end_unit - 1) { \ + _cmask = _ptr[_this_unit]; \ + _cmask = _bt_rshift(_cmask, (_ts - (_end % _ts)) % _ts); \ + if ((_end - _start) % _ts) { \ + _mask = _bt_make_mask(type, _end - _start); \ + _cmask &= _mask; \ + } \ + _bt_safe_lshift(_v, _end - _start); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + *_vptr = _v; \ + break; \ + } \ + if (_start % _ts) { \ + _cshift = _start % _ts; \ + _mask = _bt_make_mask(type, _ts - _cshift); \ + _cmask = _ptr[_this_unit]; \ + _cmask &= _mask; \ + _bt_safe_lshift(_v, _ts - _cshift); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + _start += _ts - _cshift; \ + _this_unit++; \ + } \ + for (; _this_unit < _end_unit - 1; _this_unit++) { \ + _bt_safe_lshift(_v, _ts); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ + _start += _ts; \ + } \ + if (_end % _ts) { \ + _mask = _bt_make_mask(type, _end % _ts); \ + _cmask = _ptr[_this_unit]; \ + _cmask = _bt_rshift(_cmask, _ts - (_end % _ts)); \ + _cmask &= _mask; \ + _bt_safe_lshift(_v, _end % _ts); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _cmask); \ + } else { \ + _bt_safe_lshift(_v, _ts); \ + _v |= _bt_cast_value_to_unsigned_type(__typeof__(_v), _ptr[_this_unit]); \ + } \ + *_vptr = _v; \ +} while (0) + +/* + * bt_bitfield_read - read integer from a bitfield in native endianness + * bt_bitfield_read_le - read integer from a bitfield in little endian + * bt_bitfield_read_be - read integer from a bitfield in big endian + */ + +#if (BYTE_ORDER == LITTLE_ENDIAN) + +#define bt_bitfield_read(ptr, type, start, length, vptr) \ + _bt_bitfield_read_le(ptr, type, start, length, vptr) + +#define bt_bitfield_read_le(ptr, type, start, length, vptr) \ + _bt_bitfield_read_le(ptr, type, start, length, vptr) + +#define bt_bitfield_read_be(ptr, type, start, length, vptr) \ + _bt_bitfield_read_be(ptr, unsigned char, start, length, vptr) + +#elif (BYTE_ORDER == BIG_ENDIAN) + +#define bt_bitfield_read(ptr, type, start, length, vptr) \ + _bt_bitfield_read_be(ptr, type, start, length, vptr) + +#define bt_bitfield_read_le(ptr, type, start, length, vptr) \ + _bt_bitfield_read_le(ptr, unsigned char, start, length, vptr) + +#define bt_bitfield_read_be(ptr, type, start, length, vptr) \ + _bt_bitfield_read_be(ptr, type, start, length, vptr) + +#else /* (BYTE_ORDER == PDP_ENDIAN) */ + +#error "Byte order not supported" + +#endif + +#endif /* _BABELTRACE_BITFIELD_H */ diff --git a/src/compat/compiler.h b/src/compat/compiler.h new file mode 100644 index 00000000..c4c72cc4 --- /dev/null +++ b/src/compat/compiler.h @@ -0,0 +1,40 @@ +#ifndef _BABELTRACE_COMPILER_H +#define _BABELTRACE_COMPILER_H + +/* + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include /* for offsetof */ + +#define MAYBE_BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) + +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type *)NULL)->member) * __ptr = (ptr); \ + (type *)((char *)__ptr - offsetof(type, member)); \ + }) +#endif + +#define BT_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#endif /* _BABELTRACE_COMPILER_H */ diff --git a/src/compat/endian.h b/src/compat/endian.h new file mode 100644 index 00000000..5c74c35d --- /dev/null +++ b/src/compat/endian.h @@ -0,0 +1,240 @@ +#ifndef _BABELTRACE_ENDIAN_H +#define _BABELTRACE_ENDIAN_H + +/* + * Copyright 2012 (c) - Mathieu Desnoyers + * + * endian.h compatibility layer. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef __FreeBSD__ +#include + +#elif defined(__sun__) +#include + +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif + +#ifdef _LITTLE_ENDIAN +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#ifdef _BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#endif + +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#define BYTE_ORDER __BYTE_ORDER + +#define betoh16(x) BE_16(x) +#define letoh16(x) LE_16(x) +#define betoh32(x) BE_32(x) +#define letoh32(x) LE_32(x) +#define betoh64(x) BE_64(x) +#define letoh64(x) LE_64(x) +#define htobe16(x) BE_16(x) +#define be16toh(x) BE_16(x) +#define htobe32(x) BE_32(x) +#define be32toh(x) BE_32(x) +#define htobe64(x) BE_64(x) +#define be64toh(x) BE_64(x) + +#elif defined(__MINGW32__) +#include + +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#define PDP_ENDIAN __PDP_ENDIAN +#define BYTE_ORDER __BYTE_ORDER + +#define htobe16(x) (uint16_t) _byteswap_ushort(x) +#define htole16(x) (x) +#define be16toh(x) (uint16_t) _byteswap_ushort(x) +#define le16toh(x) (x) + +#define htobe32(x) (uint32_t) _byteswap_ulong(x) +#define htole32(x) (x) +#define be32toh(x) (uint32_t) _byteswap_ulong(x) +#define le32toh(x) (x) + +#define htobe64(x) (uint64_t) _byteswap_uint64(x) +#define htole64(x) (x) +#define be64toh(x) (uint64_t) _byteswap_uint64(x) +#define le64toh(x) (x) + +#elif defined(__APPLE__) +# include +# include + +# if BYTE_ORDER == LITTLE_ENDIAN +# define htobe16(x) OSSwapConstInt16(x) +# define htole16(x) (x) +# define be16toh(x) OSSwapConstInt16(x) +# define le16toh(x) (x) + +# define htobe32(x) OSSwapConstInt32(x) +# define htole32(x) (x) +# define be32toh(x) OSSwapConstInt32(x) +# define le32toh(x) (x) + +# define htobe64(x) OSSwapConstInt64(x) +# define htole64(x) (x) +# define be64toh(x) OSSwapConstInt64(x) +# define le64toh(x) (x) + +# else /* BYTE_ORDER == LITTLE_ENDIAN */ +# define htobe16(x) (x) +# define htole16(x) OSSwapConstInt16(x) +# define be16toh(x) (x) +# define le16toh(x) OSSwapConstInt16(x) + +# define htobe32(x) (x) +# define htole32(x) OSSwapConstInt32(x) +# define be32toh(x) (x) +# define le32toh(x) OSSwapConstInt32(x) + +# define htobe64(x) (x) +# define htole64(x) OSSwapConstInt64(x) +# define be64toh(x) (x) +# define le64toh(x) OSSwapConstInt64(x) +# endif + +#else +#include + +/* + * htobe/betoh are not defined for glibc < 2.9, so add them explicitly + * if they are missing. + */ +# ifdef __USE_BSD +/* Conversion interfaces. */ +# include + +# if __BYTE_ORDER == __LITTLE_ENDIAN +# ifndef htobe16 +# define htobe16(x) __bswap_16(x) +# endif +# ifndef htole16 +# define htole16(x) (x) +# endif +# ifndef be16toh +# define be16toh(x) __bswap_16(x) +# endif +# ifndef le16toh +# define le16toh(x) (x) +# endif + +# ifndef htobe32 +# define htobe32(x) __bswap_32(x) +# endif +# ifndef htole32 +# define htole32(x) (x) +# endif +# ifndef be32toh +# define be32toh(x) __bswap_32(x) +# endif +# ifndef le32toh +# define le32toh(x) (x) +# endif + +# ifndef htobe64 +# define htobe64(x) __bswap_64(x) +# endif +# ifndef htole64 +# define htole64(x) (x) +# endif +# ifndef be64toh +# define be64toh(x) __bswap_64(x) +# endif +# ifndef le64toh +# define le64toh(x) (x) +# endif + +# else /* __BYTE_ORDER == __LITTLE_ENDIAN */ +# ifndef htobe16 +# define htobe16(x) (x) +# endif +# ifndef htole16 +# define htole16(x) __bswap_16(x) +# endif +# ifndef be16toh +# define be16toh(x) (x) +# endif +# ifndef le16toh +# define le16toh(x) __bswap_16(x) +# endif + +# ifndef htobe32 +# define htobe32(x) (x) +# endif +# ifndef htole32 +# define htole32(x) __bswap_32(x) +# endif +# ifndef be32toh +# define be32toh(x) (x) +# endif +# ifndef le32toh +# define le32toh(x) __bswap_32(x) +# endif + +# ifndef htobe64 +# define htobe64(x) (x) +# endif +# ifndef htole64 +# define htole64(x) __bswap_64(x) +# endif +# ifndef be64toh +# define be64toh(x) (x) +# endif +# ifndef le64toh +# define le64toh(x) __bswap_64(x) +# endif + +# endif /* __BYTE_ORDER == __LITTLE_ENDIAN */ +# endif /* __USE_BSD */ +#endif /* else -- __FreeBSD__ */ + +#ifndef FLOAT_WORD_ORDER +#ifdef __FLOAT_WORD_ORDER +#define FLOAT_WORD_ORDER __FLOAT_WORD_ORDER +#else /* __FLOAT_WORD_ORDER */ +#define FLOAT_WORD_ORDER BYTE_ORDER +#endif /* __FLOAT_WORD_ORDER */ +#endif /* FLOAT_WORD_ORDER */ + +#endif /* _BABELTRACE_ENDIAN_H */ diff --git a/src/compat/fcntl.h b/src/compat/fcntl.h new file mode 100644 index 00000000..c5289430 --- /dev/null +++ b/src/compat/fcntl.h @@ -0,0 +1,233 @@ +#ifndef _BABELTRACE_COMPAT_FCNTL_H +#define _BABELTRACE_COMPAT_FCNTL_H + +/* + * Copyright 2015 (c) - Jérémie Galarneau + * + * fcntl compatibility layer. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef BABELTRACE_HAVE_POSIX_FALLOCATE + +#include + +static inline +int bt_posix_fallocate(int fd, off_t offset, off_t len) +{ + return posix_fallocate(fd, offset, len); +} + +#elif defined(__MINGW32__) /* #ifdef BABELTRACE_HAVE_POSIX_FALLOCATE */ + +#include +#include +#include + +static inline +int bt_posix_fallocate(int fd, off_t offset, off_t len) +{ + HANDLE handle; + LARGE_INTEGER pos, file_pos, orig_end_offset, range_end; + int ret = 0; + char zero = 0; + DWORD byteswritten; + + if (offset < 0 || len <= 0) { + ret = EINVAL; + goto end; + } + + range_end.QuadPart = (__int64) offset + (__int64) len; + + /* Get a handle from the fd */ + handle = (HANDLE) _get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + ret = EBADF; + goto end; + } + + /* Get the file original end offset */ + ret = GetFileSizeEx(handle, &orig_end_offset); + if (ret == 0) { + ret = EBADF; + goto end; + } + + /* Make sure we don't truncate the file */ + if (orig_end_offset.QuadPart >= range_end.QuadPart) { + ret = 0; + goto end; + } + + /* Get the current file pointer position */ + pos.QuadPart = 0; + ret = SetFilePointerEx(handle, pos, &file_pos, FILE_CURRENT); + if (ret == 0) { + ret = EBADF; + goto end; + } + + /* Move the file pointer to the new end offset */ + ret = SetFilePointerEx(handle, range_end, NULL, FILE_BEGIN); + if (ret == 0) { + ret = EBADF; + goto end; + } + + /* Sets the physical file size to the current position */ + ret = SetEndOfFile(handle); + if (ret == 0) { + ret = EINVAL; + goto restore; + } + + /* + * Move the file pointer back 1 byte, and write a single 0 at the + * last byte of the new end offset, the operating system will zero + * fill the file. + */ + pos.QuadPart = -1; + ret = SetFilePointerEx(handle, pos, NULL, FILE_END); + if (ret == 0) { + ret = EBADF; + goto end; + } + + ret = WriteFile(handle, &zero, 1, &byteswritten, NULL); + if (ret == 0 || byteswritten != 1) { + ret = ENOSPC; + } else { + ret = 0; + } + +restore: + /* Restore the original file pointer position */ + if (!SetFilePointerEx(handle, file_pos, NULL, FILE_BEGIN)) { + /* We moved the file pointer but failed to restore it. */ + abort(); + } + +end: + return ret; +} + +#else + +#include +#include +#include + +#define BABELTRACE_FALLOCATE_BUFLEN 256 + +#ifndef min_t +#define min_t(type, a, b) \ + ((type) (a) < (type) (b) ? (type) (a) : (type) (b)) +#endif + +static inline +int bt_posix_fallocate(int fd, off_t offset, off_t len) +{ + int ret = 0; + ssize_t copy_len; + char buf[BABELTRACE_FALLOCATE_BUFLEN]; + off_t i, file_pos, orig_end_offset, range_end; + + if (offset < 0 || len < 0) { + ret = EINVAL; + goto end; + } + + range_end = offset + len; + if (range_end < 0) { + ret = EFBIG; + goto end; + } + + file_pos = lseek(fd, 0, SEEK_CUR); + if (file_pos < 0) { + ret = errno; + goto end; + } + + orig_end_offset = lseek(fd, 0, SEEK_END); + if (orig_end_offset < 0) { + ret = errno; + goto end; + } + + /* Seek back to original position. */ + ret = lseek(fd, file_pos, SEEK_SET); + if (ret) { + ret = errno; + goto end; + } + + /* + * The file may not need to grow, but we want to ensure the + * space has actually been reserved by the file system. First, copy + * the "existing" region of the file, then grow the file if needed. + */ + for (i = file_pos; i < min_t(off_t, range_end, orig_end_offset); + i += copy_len) { + ssize_t copy_ret; + + copy_len = min_t(size_t, BABELTRACE_FALLOCATE_BUFLEN, + min_t(off_t, range_end - i, + orig_end_offset - i)); + copy_ret = pread(fd, &buf, copy_len, i); + if (copy_ret < copy_len) { + /* + * The caller must handle any EINTR. + * POSIX_FALLOCATE(3) does not mention EINTR. + * However, glibc does forward to fallocate() + * directly on Linux, which may be interrupted. + */ + ret = errno; + goto end; + } + + copy_ret = pwrite(fd, &buf, copy_len, i); + if (copy_ret < copy_len) { + /* Same caveat as noted at pread() */ + ret = errno; + goto end; + } + } + + /* Grow file, as necessary. */ + memset(&buf, 0, BABELTRACE_FALLOCATE_BUFLEN); + for (i = orig_end_offset; i < range_end; i += copy_len) { + ssize_t write_ret; + + copy_len = min_t(size_t, BABELTRACE_FALLOCATE_BUFLEN, + range_end - i); + write_ret = pwrite(fd, &buf, copy_len, i); + if (write_ret < copy_len) { + ret = errno; + goto end; + } + } +end: + return ret; +} +#endif /* #else #ifdef BABELTRACE_HAVE_POSIX_FALLOCATE */ + +#endif /* _BABELTRACE_COMPAT_FCNTL_H */ diff --git a/src/compat/glib.h b/src/compat/glib.h new file mode 100644 index 00000000..e956280d --- /dev/null +++ b/src/compat/glib.h @@ -0,0 +1,78 @@ +#ifndef _BABELTRACE_COMPAT_GLIB_H +#define _BABELTRACE_COMPAT_GLIB_H + +/* + * Copyright (C) 2015 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#if GLIB_CHECK_VERSION(2,31,8) + +static inline gboolean +bt_g_hash_table_contains(GHashTable *hash_table, gconstpointer key) +{ + return g_hash_table_contains(hash_table, key); +} + +#else + +static inline gboolean +bt_g_hash_table_contains(GHashTable *hash_table, gconstpointer key) +{ + gpointer orig_key; + gpointer value; + + return g_hash_table_lookup_extended(hash_table, key, &orig_key, + &value); +} + +#endif + + +#if GLIB_CHECK_VERSION(2,29,16) + +static inline GPtrArray * +bt_g_ptr_array_new_full(guint reserved_size, + GDestroyNotify element_free_func) +{ + return g_ptr_array_new_full(reserved_size, element_free_func); +} + +#else + +static inline GPtrArray * +bt_g_ptr_array_new_full(guint reserved_size, + GDestroyNotify element_free_func) +{ + GPtrArray *array; + + array = g_ptr_array_sized_new(reserved_size); + if (!array) { + goto end; + } + g_ptr_array_set_free_func(array, element_free_func); +end: + return array; +} +#endif + +#endif /* _BABELTRACE_COMPAT_GLIB_H */ diff --git a/src/compat/limits.h b/src/compat/limits.h new file mode 100644 index 00000000..b2761837 --- /dev/null +++ b/src/compat/limits.h @@ -0,0 +1,46 @@ +#ifndef _BABELTRACE_LIMITS_H +#define _BABELTRACE_LIMITS_H + +/* + * Copyright (C) 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#ifdef __linux__ + +#define BABELTRACE_HOST_NAME_MAX HOST_NAME_MAX + +#elif defined(__FreeBSD__) + +#define BABELTRACE_HOST_NAME_MAX MAXHOSTNAMELEN + +#elif defined(_POSIX_HOST_NAME_MAX) + +#define BABELTRACE_HOST_NAME_MAX _POSIX_HOST_NAME_MAX + +#else + +#define BABELTRACE_HOST_NAME_MAX 256 + +#endif /* __linux__, __FreeBSD__, _POSIX_HOST_NAME_MAX */ + +#endif /* _BABELTRACE_LIMITS_H */ diff --git a/src/compat/logging.c b/src/compat/logging.c new file mode 100644 index 00000000..a79cc10f --- /dev/null +++ b/src/compat/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_compat_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_compat_log_level, "BABELTRACE_COMPAT_LOG_LEVEL"); diff --git a/src/compat/logging.h b/src/compat/logging.h new file mode 100644 index 00000000..ccb77a9d --- /dev/null +++ b/src/compat/logging.h @@ -0,0 +1,31 @@ +#ifndef COMPAT_LOGGING_H +#define COMPAT_LOGGING_H + +/* + * Copyright (c) 2017 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_compat_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_compat_log_level); + +#endif /* COMPAT_LOGGING_H */ diff --git a/src/compat/memstream.h b/src/compat/memstream.h new file mode 100644 index 00000000..8dea7a44 --- /dev/null +++ b/src/compat/memstream.h @@ -0,0 +1,356 @@ +#ifndef _BABELTRACE_FORMAT_CTF_MEMSTREAM_H +#define _BABELTRACE_FORMAT_CTF_MEMSTREAM_H + +/* + * Copyright 2012 (c) - Mathieu Desnoyers + * + * memstream compatibility layer. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef BABELTRACE_HAVE_FMEMOPEN +#include + +static inline +FILE *bt_fmemopen(void *buf, size_t size, const char *mode) +{ + return fmemopen(buf, size, mode); +} + +#else /* BABELTRACE_HAVE_FMEMOPEN */ + +#include +#include +#include +#include +#include +#include "compat/endian.h" + +#ifdef __MINGW32__ + +#include +#include + +/* + * Fallback for systems which don't have fmemopen. Copy buffer to a + * temporary file, and use that file as FILE * input. + */ +static inline +FILE *bt_fmemopen(void *buf, size_t size, const char *mode) +{ + char *tmpname; + size_t len; + FILE *fp; + int ret; + + /* + * Support reading only. + */ + if (strcmp(mode, "rb") != 0) { + return NULL; + } + + /* Build a temporary filename */ + tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); + if (_mktemp(tmpname) == NULL) { + goto error_free; + } + + /* + * Open as a read/write binary temporary deleted on close file. + * Will be deleted when the last file pointer is closed. + */ + fp = fopen(tmpname, "w+bTD"); + if (!fp) { + goto error_free; + } + + /* Copy the entire buffer to the file */ + len = fwrite(buf, sizeof(char), size, fp); + if (len != size) { + goto error_close; + } + + /* Set the file pointer to the start of file */ + ret = fseek(fp, 0L, SEEK_SET); + if (ret < 0) { + perror("fseek"); + goto error_close; + } + + g_free(tmpname); + return fp; + +error_close: + ret = fclose(fp); + if (ret < 0) { + perror("close"); + } +error_free: + g_free(tmpname); + return NULL; +} + +#else /* __MINGW32__ */ + +/* + * Fallback for systems which don't have fmemopen. Copy buffer to a + * temporary file, and use that file as FILE * input. + */ +static inline +FILE *bt_fmemopen(void *buf, size_t size, const char *mode) +{ + char *tmpname; + size_t len; + FILE *fp; + int ret; + + /* + * Support reading only. + */ + if (strcmp(mode, "rb") != 0) { + return NULL; + } + + tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); + ret = mkstemp(tmpname); + if (ret < 0) { + g_free(tmpname); + return NULL; + } + /* + * We need to write to the file. + */ + fp = fdopen(ret, "wb+"); + if (!fp) { + goto error_unlink; + } + /* Copy the entire buffer to the file */ + len = fwrite(buf, sizeof(char), size, fp); + if (len != size) { + goto error_close; + } + ret = fseek(fp, 0L, SEEK_SET); + if (ret < 0) { + perror("fseek"); + goto error_close; + } + /* We keep the handle open, but can unlink the file on the VFS. */ + ret = unlink(tmpname); + if (ret < 0) { + perror("unlink"); + } + g_free(tmpname); + return fp; + +error_close: + ret = fclose(fp); + if (ret < 0) { + perror("close"); + } +error_unlink: + ret = unlink(tmpname); + if (ret < 0) { + perror("unlink"); + } + g_free(tmpname); + return NULL; +} + +#endif /* __MINGW32__ */ + +#endif /* BABELTRACE_HAVE_FMEMOPEN */ + + +#ifdef BABELTRACE_HAVE_OPEN_MEMSTREAM + +#include + +static inline +FILE *bt_open_memstream(char **ptr, size_t *sizeloc) +{ + return open_memstream(ptr, sizeloc); +} + +static inline +int bt_close_memstream(char **buf, size_t *size, FILE *fp) +{ + return fclose(fp); +} + +#else /* BABELTRACE_HAVE_OPEN_MEMSTREAM */ + +#include +#include +#include + +#ifdef __MINGW32__ + +/* + * Fallback for systems which don't have open_memstream. Create FILE * + * with bt_open_memstream, but require call to + * bt_close_memstream to flush all data written to the FILE * + * into the buffer (which we allocate). + */ +static inline +FILE *bt_open_memstream(char **ptr, size_t *sizeloc) +{ + char *tmpname; + FILE *fp; + + tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); + + if (_mktemp(tmpname) == NULL) { + goto error_free; + } + + /* + * Open as a read/write binary temporary deleted on close file. + * Will be deleted when the last file pointer is closed. + */ + fp = fopen(tmpname, "w+bTD"); + if (!fp) { + goto error_free; + } + + g_free(tmpname); + return fp; + +error_free: + g_free(tmpname); + return NULL; +} + +#else /* __MINGW32__ */ + +/* + * Fallback for systems which don't have open_memstream. Create FILE * + * with bt_open_memstream, but require call to + * bt_close_memstream to flush all data written to the FILE * + * into the buffer (which we allocate). + */ +static inline +FILE *bt_open_memstream(char **ptr, size_t *sizeloc) +{ + char *tmpname; + int ret; + FILE *fp; + + tmpname = g_build_filename(g_get_tmp_dir(), "babeltrace-tmp-XXXXXX", NULL); + + ret = mkstemp(tmpname); + if (ret < 0) { + perror("mkstemp"); + g_free(tmpname); + return NULL; + } + fp = fdopen(ret, "wb+"); + if (!fp) { + goto error_unlink; + } + /* + * babeltrace_flush_memstream will update the buffer content + * with read from fp. No need to keep the file around, just the + * handle. + */ + ret = unlink(tmpname); + if (ret < 0) { + perror("unlink"); + } + g_free(tmpname); + return fp; + +error_unlink: + ret = unlink(tmpname); + if (ret < 0) { + perror("unlink"); + } + g_free(tmpname); + return NULL; +} + +#endif /* __MINGW32__ */ + +/* Get file size, allocate buffer, copy. */ +static inline +int bt_close_memstream(char **buf, size_t *size, FILE *fp) +{ + size_t len, n; + long pos; + int ret; + + ret = fflush(fp); + if (ret < 0) { + perror("fflush"); + return ret; + } + ret = fseek(fp, 0L, SEEK_END); + if (ret < 0) { + perror("fseek"); + return ret; + } + pos = ftell(fp); + if (ret < 0) { + perror("ftell"); + return ret; + } + *size = pos; + /* add final \0 */ + *buf = calloc(pos + 1, sizeof(char)); + if (!*buf) { + return -ENOMEM; + } + ret = fseek(fp, 0L, SEEK_SET); + if (ret < 0) { + perror("fseek"); + goto error_free; + } + /* Copy the entire file into the buffer */ + n = 0; + clearerr(fp); + while (!feof(fp) && !ferror(fp) && (*size - n > 0)) { + len = fread(*buf, sizeof(char), *size - n, fp); + n += len; + } + if (n != *size) { + ret = -1; + goto error_close; + } + ret = fclose(fp); + if (ret < 0) { + perror("fclose"); + return ret; + } + return 0; + +error_close: + ret = fclose(fp); + if (ret < 0) { + perror("fclose"); + } +error_free: + free(*buf); + *buf = NULL; + return ret; +} + +#endif /* BABELTRACE_HAVE_OPEN_MEMSTREAM */ + +#endif /* _BABELTRACE_FORMAT_CTF_MEMSTREAM_H */ diff --git a/src/compat/mman.c b/src/compat/mman.c new file mode 100644 index 00000000..d0cea9df --- /dev/null +++ b/src/compat/mman.c @@ -0,0 +1,285 @@ +/* + * compat/compat_mman.h + * + * Copyright (C) 2013 JP Ikaheimonen + * 2016 Michael Jeanson + * + * These sources are based on ftp://g.oswego.edu/pub/misc/malloc.c + * file by Doug Lea, released to the public domain. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMPAT-MMAN" +#include "logging.h" + +#ifdef __APPLE__ +/* + * On macOS, we need a dummy symbol so that the linker won't + * complain of an empty table of contents. + */ +BT_HIDDEN +int bt_mman_dummy_symbol; +#endif /* __APPLE__ */ + +#ifdef __MINGW32__ + +#include +#include +#include +#include +#include +#include "compat/mman.h" + +struct mmap_mapping { + /* The duplicated handle. */ + HANDLE file_handle; + /* Handle returned by CreateFileMapping. */ + HANDLE map_handle; +}; + +static +GHashTable *mmap_mappings = NULL; + +/* + * This mutex protects the hashtable of memory mappings. + */ +static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER; + +static +struct mmap_mapping *mapping_create(void) +{ + struct mmap_mapping *mapping; + + mapping = malloc(sizeof(struct mmap_mapping)); + if (mapping != NULL) { + mapping->file_handle = NULL; + mapping->map_handle = NULL; + } + + return mapping; +} + +static +void mapping_clean(struct mmap_mapping *mapping) +{ + if (mapping) { + if (!CloseHandle(mapping->map_handle)) { + BT_LOGF_STR("Failed to close mmap map_handle."); + abort(); + } + if (!CloseHandle(mapping->file_handle)) { + BT_LOGF_STR("Failed to close mmap file_handle."); + abort(); + } + free(mapping); + mapping = NULL; + } +} + +static +void addr_clean(void *addr) +{ + /* Cleanup of handles should never fail. */ + if (!UnmapViewOfFile(addr)) { + BT_LOGF_STR("Failed to unmap mmap mapping."); + abort(); + } +} + +static +void mmap_lock(void) +{ + if (pthread_mutex_lock(&mmap_mutex)) { + BT_LOGF_STR("Failed to acquire mmap_mutex."); + abort(); + } +} + +static +void mmap_unlock(void) +{ + if (pthread_mutex_unlock(&mmap_mutex)) { + BT_LOGF_STR("Failed to release mmap_mutex."); + abort(); + } +} + +/* + * Convert mmap memory protection flags to CreateFileMapping page protection + * flag and MapViewOfFile desired access flag. + */ +static +DWORD map_prot_flags(int prot, DWORD *dwDesiredAccess) +{ + if (prot & PROT_READ) { + if (prot & PROT_WRITE) { + *dwDesiredAccess = FILE_MAP_WRITE; + if (prot & PROT_EXEC) { + return PAGE_EXECUTE_READWRITE; + } + return PAGE_READWRITE; + } + if (prot & PROT_EXEC) { + *dwDesiredAccess = FILE_MAP_EXECUTE; + return PAGE_EXECUTE_READ; + } + *dwDesiredAccess = FILE_MAP_READ; + return PAGE_READONLY; + } + if (prot & PROT_WRITE) { + *dwDesiredAccess = FILE_MAP_COPY; + return PAGE_WRITECOPY; + } + if (prot & PROT_EXEC) { + *dwDesiredAccess = FILE_MAP_EXECUTE; + return PAGE_EXECUTE_READ; + } + + /* Mapping failed. */ + *dwDesiredAccess = 0; + return 0; +} + +BT_HIDDEN +void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset) +{ + struct mmap_mapping *mapping = NULL; + void *mapping_addr; + DWORD dwDesiredAccess; + DWORD flProtect; + HANDLE handle; + + /* Check for a valid fd. */ + if (fd == -1) { + _set_errno(EBADF); + goto error; + } + + /* We don't support this at the moment. */ + if (flags == MAP_FIXED) { + _set_errno(ENOTSUP); + goto error; + } + + /* Map mmap flags to those of the Windows API. */ + flProtect = map_prot_flags(prot, &dwDesiredAccess); + if (flProtect == 0) { + _set_errno(EINVAL); + goto error; + } + + /* Allocate the mapping struct. */ + mapping = mapping_create(); + if (!mapping) { + BT_LOGE_STR("Failed to allocate mmap mapping."); + _set_errno(ENOMEM); + goto error; + } + + /* Get a handle from the fd. */ + handle = (HANDLE) _get_osfhandle(fd); + + /* Duplicate the handle and store it in 'mapping.file_handle'. */ + if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), + &mapping->file_handle, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + _set_errno(ENOMEM); + goto error; + } + + /* + * Create a file mapping object with a maximum size + * of 'offset' + 'length'. + */ + mapping->map_handle = CreateFileMapping(mapping->file_handle, NULL, + flProtect, 0, offset + length, NULL); + if (mapping->map_handle == 0) { + _set_errno(EACCES); + goto error; + } + + /* Map the requested block starting at 'offset' for 'length' bytes. */ + mapping_addr = MapViewOfFile(mapping->map_handle, dwDesiredAccess, 0, + offset, length); + if (mapping_addr == 0) { + DWORD dwLastErr = GetLastError(); + if (dwLastErr == ERROR_MAPPED_ALIGNMENT) { + _set_errno(EINVAL); + } else { + _set_errno(EACCES); + } + goto error; + } + + mmap_lock(); + + /* If we have never done any mappings, allocate the hashtable. */ + if (!mmap_mappings) { + mmap_mappings = g_hash_table_new_full(g_direct_hash, + g_direct_equal, (GDestroyNotify) addr_clean, + (GDestroyNotify) mapping_clean); + if (!mmap_mappings) { + BT_LOGE_STR("Failed to allocate mmap hashtable."); + _set_errno(ENOMEM); + goto error_mutex_unlock; + } + } + + /* Add the new mapping to the hashtable. */ + g_hash_table_insert(mmap_mappings, mapping_addr, mapping); + + mmap_unlock(); + + return mapping_addr; + +error_mutex_unlock: + mmap_unlock(); +error: + mapping_clean(mapping); + return MAP_FAILED; +} + +BT_HIDDEN +int bt_munmap(void *addr, size_t length) +{ + int ret = 0; + + mmap_lock(); + + /* Check if the mapping exists in the hashtable. */ + if (g_hash_table_lookup(mmap_mappings, addr) == NULL) { + _set_errno(EINVAL); + ret = -1; + goto end; + } + + /* Remove it. */ + if (!g_hash_table_remove(mmap_mappings, addr)) { + BT_LOGF_STR("Failed to remove mapping from hashtable."); + abort(); + } + +end: + mmap_unlock(); + return ret; +} + +#endif diff --git a/src/compat/mman.h b/src/compat/mman.h new file mode 100644 index 00000000..9f494526 --- /dev/null +++ b/src/compat/mman.h @@ -0,0 +1,77 @@ +#ifndef _BABELTRACE_COMPAT_MMAN_H +#define _BABELTRACE_COMPAT_MMAN_H + +/* + * Copyright (C) 2015-2016 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef __MINGW32__ + +#include + +#define PROT_NONE 0x0 +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 + +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xF +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +/* + * Note that some platforms (e.g. Windows) do not allow read-only + * mappings to exceed the file's size (even within a page). + */ +void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset); + +int bt_munmap(void *addr, size_t length); + +#else /* __MINGW32__ */ + +#include + +static inline +void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset) +{ + return (void *) mmap(addr, length, prot, flags, fd, offset); +} + +static inline +int bt_munmap(void *addr, size_t length) +{ + return munmap(addr, length); +} +#endif /* __MINGW32__ */ + +#ifndef MAP_ANONYMOUS +# ifdef MAP_ANON +# define MAP_ANONYMOUS MAP_ANON +# endif +#endif + +#endif /* _BABELTRACE_COMPAT_MMAN_H */ diff --git a/src/compat/socket.h b/src/compat/socket.h new file mode 100644 index 00000000..b1fe1597 --- /dev/null +++ b/src/compat/socket.h @@ -0,0 +1,419 @@ +#ifndef _BABELTRACE_COMPAT_SOCKET_H +#define _BABELTRACE_COMPAT_SOCKET_H + +/* + * Copyright (C) 2015-2017 Michael Jeanson + * 2015 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef __MINGW32__ + +#include + +#define BT_INVALID_SOCKET INVALID_SOCKET +#define BT_SOCKET_ERROR SOCKET_ERROR +#define BT_SOCKET SOCKET + +static inline +int bt_socket_init(void) +{ + WORD verreq; + WSADATA wsa; + int ret; + + /* Request winsock 2.2 support */ + verreq = MAKEWORD(2, 2); + + ret = WSAStartup(verreq, &wsa); + if (ret != 0) { +#ifdef BT_LOGE + BT_LOGE("Winsock init failed with error: %d", ret); +#endif + goto end; + } + + if (LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2) { +#ifdef BT_LOGE_STR + BT_LOGE_STR("Could not init winsock 2.2 support"); +#endif + WSACleanup(); + ret = -1; + } + +end: + return ret; +} + +static inline +int bt_socket_fini(void) +{ + return WSACleanup(); +} + +static inline +int bt_socket_send(int sockfd, const void *buf, size_t len, int flags) +{ + return send(sockfd, buf, len, flags); +} + +static inline +int bt_socket_recv(int sockfd, void *buf, size_t len, int flags) +{ + return recv(sockfd, buf, len, flags); +} + +static inline +int bt_socket_close(int fd) +{ + return closesocket(fd); +} + +static inline +bool bt_socket_interrupted(void) +{ + /* There is no equivalent to EINTR in winsock 2.2 */ + return false; +} + +static inline +const char *bt_socket_errormsg(void) +{ + const char *errstr; + int error = WSAGetLastError(); + + switch (error) { + case WSAEINTR: + errstr = "Call interrupted"; + break; + case WSAEBADF: + errstr = "Bad file"; + break; + case WSAEACCES: + errstr = "Bad access"; + break; + case WSAEFAULT: + errstr = "Bad argument"; + break; + case WSAEINVAL: + errstr = "Invalid arguments"; + break; + case WSAEMFILE: + errstr = "Out of file descriptors"; + break; + case WSAEWOULDBLOCK: + errstr = "Call would block"; + break; + case WSAEINPROGRESS: + case WSAEALREADY: + errstr = "Blocking call in progress"; + break; + case WSAENOTSOCK: + errstr = "Descriptor is not a socket"; + break; + case WSAEDESTADDRREQ: + errstr = "Need destination address"; + break; + case WSAEMSGSIZE: + errstr = "Bad message size"; + break; + case WSAEPROTOTYPE: + errstr = "Bad protocol"; + break; + case WSAENOPROTOOPT: + errstr = "Protocol option is unsupported"; + break; + case WSAEPROTONOSUPPORT: + errstr = "Protocol is unsupported"; + break; + case WSAESOCKTNOSUPPORT: + errstr = "Socket is unsupported"; + break; + case WSAEOPNOTSUPP: + errstr = "Operation not supported"; + break; + case WSAEAFNOSUPPORT: + errstr = "Address family not supported"; + break; + case WSAEPFNOSUPPORT: + errstr = "Protocol family not supported"; + break; + case WSAEADDRINUSE: + errstr = "Address already in use"; + break; + case WSAEADDRNOTAVAIL: + errstr = "Address not available"; + break; + case WSAENETDOWN: + errstr = "Network down"; + break; + case WSAENETUNREACH: + errstr = "Network unreachable"; + break; + case WSAENETRESET: + errstr = "Network has been reset"; + break; + case WSAECONNABORTED: + errstr = "Connection was aborted"; + break; + case WSAECONNRESET: + errstr = "Connection was reset"; + break; + case WSAENOBUFS: + errstr = "No buffer space"; + break; + case WSAEISCONN: + errstr = "Socket is already connected"; + break; + case WSAENOTCONN: + errstr = "Socket is not connected"; + break; + case WSAESHUTDOWN: + errstr = "Socket has been shut down"; + break; + case WSAETOOMANYREFS: + errstr = "Too many references"; + break; + case WSAETIMEDOUT: + errstr = "Timed out"; + break; + case WSAECONNREFUSED: + errstr = "Connection refused"; + break; + case WSAELOOP: + errstr = "Loop??"; + break; + case WSAENAMETOOLONG: + errstr = "Name too long"; + break; + case WSAEHOSTDOWN: + errstr = "Host down"; + break; + case WSAEHOSTUNREACH: + errstr = "Host unreachable"; + break; + case WSAENOTEMPTY: + errstr = "Not empty"; + break; + case WSAEPROCLIM: + errstr = "Process limit reached"; + break; + case WSAEUSERS: + errstr = "Too many users"; + break; + case WSAEDQUOT: + errstr = "Bad quota"; + break; + case WSAESTALE: + errstr = "Something is stale"; + break; + case WSAEREMOTE: + errstr = "Remote error"; + break; + case WSAEDISCON: + errstr = "Disconnected"; + break; + + /* Extended Winsock errors */ + case WSASYSNOTREADY: + errstr = "Winsock library is not ready"; + break; + case WSANOTINITIALISED: + errstr = "Winsock library not initialised"; + break; + case WSAVERNOTSUPPORTED: + errstr = "Winsock version not supported"; + break; + + /* getXbyY() errors (already handled in herrmsg): + * Authoritative Answer: Host not found */ + case WSAHOST_NOT_FOUND: + errstr = "Host not found"; + break; + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + case WSATRY_AGAIN: + errstr = "Host not found, try again"; + break; + + /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + case WSANO_RECOVERY: + errstr = "Unrecoverable error in call to nameserver"; + break; + + /* Valid name, no data record of requested type */ + case WSANO_DATA: + errstr = "No data record of requested type"; + break; + + default: + errstr = "Unknown error"; + } + + return errstr; +} + +#else /* __MINGW32__ */ + +#include +#include +#include +#include + +#define BT_INVALID_SOCKET -1 +#define BT_SOCKET_ERROR -1 +#define BT_SOCKET int + +static inline +int bt_socket_init(void) +{ + return 0; +} + +static inline +int bt_socket_fini(void) +{ + return 0; +} + +static inline +int bt_socket_send(int sockfd, const void *buf, size_t len, int flags) +{ + return send(sockfd, buf, len, flags); +} + +static inline +int bt_socket_recv(int sockfd, void *buf, size_t len, int flags) +{ + return recv(sockfd, buf, len, flags); +} + +static inline +int bt_socket_close(int fd) +{ + return close(fd); +} + +static inline +bool bt_socket_interrupted(void) +{ + return (errno == EINTR); +} + +static inline +const char *bt_socket_errormsg(void) +{ + return g_strerror(errno); +} +#endif + + +/* + * This wrapper is used on platforms that have no way of ignoring SIGPIPE + * during a send(). + */ + +#ifndef MSG_NOSIGNAL +# ifdef SO_NOSIGPIPE +# define MSG_NOSIGNAL SO_NOSIGPIPE +# elif defined(__MINGW32__) +# define MSG_NOSIGNAL 0 +# endif +#endif + +#if defined(MSG_NOSIGNAL) +static inline +ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size) +{ + return bt_socket_send(fd, buffer, size, MSG_NOSIGNAL); +} +#else + +#include + +static inline +ssize_t bt_socket_send_nosigpipe(int fd, const void *buffer, size_t size) +{ + ssize_t sent; + int saved_err; + sigset_t sigpipe_set, pending_set, old_set; + int sigpipe_was_pending; + + /* + * Discard the SIGPIPE from send(), not disturbing any SIGPIPE + * that might be already pending. If a bogus SIGPIPE is sent to + * the entire process concurrently by a malicious user, it may + * be simply discarded. + */ + if (sigemptyset(&pending_set)) { + return -1; + } + /* + * sigpending returns the mask of signals that are _both_ + * blocked for the thread _and_ pending for either the thread or + * the entire process. + */ + if (sigpending(&pending_set)) { + return -1; + } + sigpipe_was_pending = sigismember(&pending_set, SIGPIPE); + /* + * If sigpipe was pending, it means it was already blocked, so + * no need to block it. + */ + if (!sigpipe_was_pending) { + if (sigemptyset(&sigpipe_set)) { + return -1; + } + if (sigaddset(&sigpipe_set, SIGPIPE)) { + return -1; + } + if (pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set)) { + return -1; + } + } + + /* Send and save errno. */ + sent = bt_socket_send(fd, buffer, size, 0); + saved_err = errno; + + if (sent == -1 && errno == EPIPE && !sigpipe_was_pending) { + struct timespec timeout = { 0, 0 }; + int ret; + + do { + ret = sigtimedwait(&sigpipe_set, NULL, + &timeout); + } while (ret == -1 && errno == EINTR); + } + if (!sigpipe_was_pending) { + if (pthread_sigmask(SIG_SETMASK, &old_set, NULL)) { + return -1; + } + } + /* Restore send() errno */ + errno = saved_err; + + return sent; +} +#endif + + +#endif /* _BABELTRACE_COMPAT_SOCKET_H */ diff --git a/src/compat/stdio.h b/src/compat/stdio.h new file mode 100644 index 00000000..906e695d --- /dev/null +++ b/src/compat/stdio.h @@ -0,0 +1,128 @@ +#ifndef _BABELTRACE_COMPAT_STDIO_H +#define _BABELTRACE_COMPAT_STDIO_H + +/* + * Copyright (C) 2015 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/assert.h" + +#define BT_GETLINE_MINBUFLEN 64 + +static inline +char * _bt_getline_bufalloc(char **lineptr, size_t *n, size_t linelen) +{ + size_t buflen = *n; + char *buf = *lineptr; + + if (buflen >= linelen && buf != NULL) { + return buf; + } + if (buf == NULL) { + buflen = BT_GETLINE_MINBUFLEN; + } else { + buflen = buflen << 1; + if (buflen < BT_GETLINE_MINBUFLEN) { + buflen = BT_GETLINE_MINBUFLEN; + } + } + /* Check below not strictly needed, extra safety. */ + if (buflen < linelen) { + buflen = linelen; + } + buf = realloc(buf, buflen); + if (!buf) { + errno = ENOMEM; + return NULL; + } + *n = buflen; + *lineptr = buf; + return buf; +} + +/* + * Returns line length (including possible final \n, excluding final + * \0). On end of file, returns -1 with nonzero feof(stream) and errno + * set to 0. On error, returns -1 with errno set. + * + * This interface is similar to the getline(3) man page part of the + * Linux man-pages project, release 3.74. One major difference from the + * Open Group POSIX specification is that this implementation does not + * necessarily set the ferror() flag on error (because it is internal to + * libc). + */ +static inline +ssize_t bt_getline(char **lineptr, size_t *n, FILE *stream) +{ + size_t linelen = 0; + char *buf; + int found_eof = 0; + + if (lineptr == NULL || n == NULL) { + errno = EINVAL; + return -1; + } + for (;;) { + char c; + int ret; + + ret = fgetc(stream); + if (ret == EOF) { + if (ferror(stream)) { + /* ferror() is set, errno set by fgetc(). */ + return -1; + } + BT_ASSERT(feof(stream)); + found_eof = 1; + break; + } + c = (char) ret; + if (linelen == SSIZE_MAX) { + errno = EOVERFLOW; + return -1; + } + buf = _bt_getline_bufalloc(lineptr, n, ++linelen); + if (!buf) { + return -1; + } + buf[linelen - 1] = c; + if (c == '\n') { + break; + } + } + if (!linelen && found_eof) { + /* feof() is set. */ + errno = 0; + return -1; + } + buf = _bt_getline_bufalloc(lineptr, n, ++linelen); + if (!buf) { + return -1; + } + buf[linelen - 1] = '\0'; + return linelen - 1; /* Count don't include final \0. */ +} + +#endif /* _BABELTRACE_COMPAT_STDIO_H */ diff --git a/src/compat/stdlib.h b/src/compat/stdlib.h new file mode 100644 index 00000000..bd2a2c95 --- /dev/null +++ b/src/compat/stdlib.h @@ -0,0 +1,77 @@ +#ifndef _BABELTRACE_COMPAT_STDLIB_H +#define _BABELTRACE_COMPAT_STDLIB_H + +/* + * Copyright (C) 2015 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This compat wrapper can be removed and replaced by g_mkdtemp() when we bump + * the requirement on glib to version 2.30. + */ + +#include +#include +#include +#include + +#ifdef HAVE_MKDTEMP + +static inline +char *bt_mkdtemp(char *template) +{ + return mkdtemp(template); +} + +#elif GLIB_CHECK_VERSION(2,30,0) + +#include +static inline +char *bt_mkdtemp(char *template) +{ + return g_mkdtemp(template); +} + +#else + +static inline +char *bt_mkdtemp(char *template) +{ + char *ret; + + ret = mktemp(template); + if (!ret) { + goto end; + } + + if(mkdir(template, 0700)) { + ret = NULL; + goto end; + } + + ret = template; +end: + return ret; +} + +#endif + +#endif /* _BABELTRACE_COMPAT_STDLIB_H */ diff --git a/src/compat/string.h b/src/compat/string.h new file mode 100644 index 00000000..c3325a3b --- /dev/null +++ b/src/compat/string.h @@ -0,0 +1,90 @@ +#ifndef _BABELTRACE_COMPAT_STRING_H +#define _BABELTRACE_COMPAT_STRING_H + +/* + * Copyright (C) 2013 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#ifdef HAVE_STRNLEN +static inline +size_t bt_strnlen(const char *str, size_t max) +{ + return strnlen(str, max); +} +#else +static inline +size_t bt_strnlen(const char *str, size_t max) +{ + size_t ret; + const char *end; + + end = memchr(str, 0, max); + + if (end) { + ret = (size_t) (end - str); + } else { + ret = max; + } + + return ret; +} +#endif /* HAVE_STRNLEN */ + +#ifdef HAVE_STRNDUP +static inline +char *bt_strndup(const char *s, size_t n) +{ + return strndup(s, n); +} +#else +static inline +char *bt_strndup(const char *s, size_t n) +{ + char *ret; + size_t navail; + + if (!s) { + ret = NULL; + goto end; + } + + /* min() */ + navail = strlen(s) + 1; + if ((n + 1) < navail) { + navail = n + 1; + } + + ret = malloc(navail); + if (!ret) { + goto end; + } + + memcpy(ret, s, navail); + ret[navail - 1] = '\0'; +end: + return ret; +} +#endif /* HAVE_STRNDUP */ + +#endif /* _BABELTRACE_COMPAT_STRING_H */ diff --git a/src/compat/time.h b/src/compat/time.h new file mode 100644 index 00000000..f5c09a94 --- /dev/null +++ b/src/compat/time.h @@ -0,0 +1,98 @@ +#ifndef _BABELTRACE_INCLUDE_COMPAT_TIME_H +#define _BABELTRACE_INCLUDE_COMPAT_TIME_H + +/* + * Copyright (C) 2013 JP Ikaheimonen + * 2016 Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include +#include + +#ifdef __MINGW32__ + +#include + +/* + * The Windows version of the time functions use one common tm structure per + * thread which makes them thread-safe. Implement the POSIX _r variants by + * copying this to a user supplied struct. + */ + +static inline +struct tm *bt_gmtime_r(const time_t *timep, struct tm *result) +{ + struct tm *local_res; + + if (!result) { + goto error; + } + + local_res = gmtime(timep); + if (!local_res) { + result = NULL; + goto error; + } + + memcpy(result, local_res, sizeof(struct tm)); + +error: + return result; +} + +static inline +struct tm *bt_localtime_r(const time_t *timep, struct tm *result) +{ + struct tm *local_res; + + if (!result) { + goto error; + } + + local_res = localtime(timep); + if (!local_res) { + result = NULL; + goto error; + } + + memcpy(result, local_res, sizeof(struct tm)); + +error: + return result; +} + +#else /* __MINGW32__ */ + +static inline +struct tm *bt_gmtime_r(const time_t *timep, struct tm *result) +{ + return gmtime_r(timep, result); +} + +static inline +struct tm *bt_localtime_r(const time_t *timep, struct tm *result) +{ + return localtime_r(timep, result); +} + +#endif /* __MINGW32__ */ +#endif /* _BABELTRACE_INCLUDE_COMPAT_TIME_H */ diff --git a/src/compat/unistd.h b/src/compat/unistd.h new file mode 100644 index 00000000..502e87a5 --- /dev/null +++ b/src/compat/unistd.h @@ -0,0 +1,59 @@ +#ifndef _BABELTRACE_COMPAT_UNISTD_H +#define _BABELTRACE_COMPAT_UNISTD_H + +/* + * (C) Copyright 2016 - Michael Jeanson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include + +#ifdef __MINGW32__ +#include +#include + +#define _SC_PAGESIZE 30 + +static inline +long bt_sysconf(int name) +{ + SYSTEM_INFO si; + + switch(name) { + case _SC_PAGESIZE: + GetNativeSystemInfo(&si); + return si.dwPageSize; + default: + errno = EINVAL; + return -1; + } +} + +#else + +static inline +long bt_sysconf(int name) +{ + return sysconf(name); +} + +#endif +#endif /* _BABELTRACE_COMPAT_UNISTD_H */ diff --git a/src/compat/utc.h b/src/compat/utc.h new file mode 100644 index 00000000..3660e08e --- /dev/null +++ b/src/compat/utc.h @@ -0,0 +1,105 @@ +#ifndef _BABELTRACE_UTC_H +#define _BABELTRACE_UTC_H + +/* + * Copyright (C) 2011-2013 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +/* If set, use GNU or BSD timegm(3) */ +#if defined(_BSD_SOURCE) || defined(_SVID_SOURCE) + +static inline +time_t bt_timegm(struct tm *tm) +{ + return timegm(tm); +} + +#elif defined(__MINGW32__) + +static inline +time_t bt_timegm(struct tm *tm) +{ + return _mkgmtime(tm); +} + +#else + +#include + +/* + * This is a simple implementation of timegm() it just turns the "struct tm" into + * a GMT time_t. It does not normalize any of the fields of the "struct tm", nor + * does it set tm_wday or tm_yday. + */ + +static inline +int bt_leapyear(int year) +{ + return ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)); +} + +static inline +time_t bt_timegm(struct tm *tm) +{ + int year, month, total_days; + + int monthlen[2][12] = { + /* Days per month for a regular year */ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + /* Days per month for a leap year */ + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + }; + + if ((tm->tm_mon >= 12) || + (tm->tm_mday >= 32) || + (tm->tm_hour >= 24) || + (tm->tm_min >= 60) || + (tm->tm_sec >= 61)) { + errno = EOVERFLOW; + return (time_t) -1; + } + + /* Add 365 days for each year since 1970 */ + total_days = 365 * (tm->tm_year - 70); + + /* Add one day for each leap year since 1970 */ + for (year = 70; year < tm->tm_year; year++) { + if (bt_leapyear(1900 + year)) { + total_days++; + } + } + + /* Add days for each remaining month */ + for (month = 0; month < tm->tm_mon; month++) { + total_days += monthlen[bt_leapyear(1900 + year)][month]; + } + + /* Add remaining days */ + total_days += tm->tm_mday - 1; + + return ((((total_days * 24) + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec); +} + +#endif + +#endif /* _BABELTRACE_UTC_H */ diff --git a/src/compat/uuid.c b/src/compat/uuid.c new file mode 100644 index 00000000..0b050e3d --- /dev/null +++ b/src/compat/uuid.c @@ -0,0 +1,120 @@ +/* + * compat/compat_uuid.h + * + * Copyright (C) 2013 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMPAT-UUID" +#include "logging.h" + +#ifdef __APPLE__ +/* + * On macOS, we need a dummy symbol so that the linker won't + * complain of an empty table of contents. + */ +BT_HIDDEN +int bt_uuid_dummy_symbol; +#endif /* __APPLE__ */ + +#ifdef __MINGW32__ + +#include +#include +#include "compat/uuid.h" + +/* MinGW does not provide byteswap - implement our own version. */ +static +void swap(unsigned char *ptr, unsigned int i, unsigned int j) +{ + unsigned char tmp; + + tmp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = tmp; +} + +static +void fix_uuid_endian(unsigned char * ptr) +{ + swap(ptr, 0, 3); + swap(ptr, 1, 2); + swap(ptr, 4, 5); + swap(ptr, 6, 7); +} + +int bt_uuid_generate(unsigned char *uuid_out) +{ + RPC_STATUS status; + + status = UuidCreate((UUID *) uuid_out); + if (status == RPC_S_OK) + return 0; + else + return -1; +} + +int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) +{ + RPC_STATUS status; + unsigned char *alloc_str; + int ret; + unsigned char copy_of_uuid_in[BABELTRACE_UUID_LEN]; + + /* make a modifyable copy of uuid_in */ + memcpy(copy_of_uuid_in, uuid_in, BABELTRACE_UUID_LEN); + + fix_uuid_endian(copy_of_uuid_in); + status = UuidToString((UUID *) copy_of_uuid_in, &alloc_str); + + if (status == RPC_S_OK) { + strncpy(str_out, (char *) alloc_str, BABELTRACE_UUID_STR_LEN); + str_out[BABELTRACE_UUID_STR_LEN - 1] = '\0'; + ret = 0; + } else { + ret = -1; + } + RpcStringFree(&alloc_str); + return ret; +} + +int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) +{ + RPC_STATUS status; + + status = UuidFromString((unsigned char *) str_in, + (UUID *) uuid_out); + fix_uuid_endian(uuid_out); + + if (status == RPC_S_OK) + return 0; + else + return -1; +} + +int bt_uuid_compare(const unsigned char *uuid_a, + const unsigned char *uuid_b) +{ + RPC_STATUS status; + + return !UuidCompare((UUID *) uuid_a, (UUID *) uuid_b, &status) ? 0 : -1; +} + +#endif diff --git a/src/compat/uuid.h b/src/compat/uuid.h new file mode 100644 index 00000000..2c2e9295 --- /dev/null +++ b/src/compat/uuid.h @@ -0,0 +1,157 @@ +#ifndef _BABELTRACE_COMPAT_UUID_H +#define _BABELTRACE_COMPAT_UUID_H + +/* + * Copyright (C) 2011 Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Includes final \0. */ +#define BABELTRACE_UUID_STR_LEN 37 +#define BABELTRACE_UUID_LEN 16 + +#ifdef BABELTRACE_HAVE_LIBUUID +#include + +static inline +int bt_uuid_generate(unsigned char *uuid_out) +{ + uuid_generate(uuid_out); + return 0; +} + +/* Sun's libuuid lacks const qualifiers */ +#if defined(__sun__) +static inline +int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) +{ + uuid_unparse((unsigned char *) uuid_in, str_out); + return 0; +} + +static inline +int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) +{ + return uuid_parse((char *) str_in, uuid_out); +} + +static inline +int bt_uuid_compare(const unsigned char *uuid_a, + const unsigned char *uuid_b) +{ + return uuid_compare((unsigned char *) uuid_a, + (unsigned char *) uuid_b); +} +#else +static inline +int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) +{ + uuid_unparse(uuid_in, str_out); + return 0; +} + +static inline +int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) +{ + return uuid_parse(str_in, uuid_out); +} + +static inline +int bt_uuid_compare(const unsigned char *uuid_a, + const unsigned char *uuid_b) +{ + return uuid_compare(uuid_a, uuid_b); +} +#endif + +#elif defined(BABELTRACE_HAVE_LIBC_UUID) +#include +#include +#include +#include + +static inline +int bt_uuid_generate(unsigned char *uuid_out) +{ + uint32_t status; + + uuid_create((uuid_t *) uuid_out, &status); + if (status == uuid_s_ok) + return 0; + else + return -1; +} + +static inline +int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out) +{ + uint32_t status; + char *alloc_str; + int ret; + + uuid_to_string((uuid_t *) uuid_in, &alloc_str, &status); + if (status == uuid_s_ok) { + strcpy(str_out, alloc_str); + ret = 0; + } else { + ret = -1; + } + free(alloc_str); + return ret; +} + +static inline +int bt_uuid_parse(const char *str_in, unsigned char *uuid_out) +{ + uint32_t status; + + uuid_from_string(str_in, (uuid_t *) uuid_out, &status); + if (status == uuid_s_ok) + return 0; + else + return -1; +} + +static inline +int bt_uuid_compare(const unsigned char *uuid_a, + const unsigned char *uuid_b) +{ + uint32_t status; + + uuid_compare((uuid_t *) uuid_a, (uuid_t *) uuid_b, &status); + if (status == uuid_s_ok) + return 0; + else + return -1; +} + +#elif defined(__MINGW32__) + +int bt_uuid_generate(unsigned char *uuid_out); +int bt_uuid_unparse(const unsigned char *uuid_in, char *str_out); +int bt_uuid_parse(const char *str_in, unsigned char *uuid_out); +int bt_uuid_compare(const unsigned char *uuid_a, + const unsigned char *uuid_b); + +#else +#error "Babeltrace needs to have a UUID generator configured." +#endif + +#endif /* _BABELTRACE_COMPAT_UUID_H */ diff --git a/src/ctf-writer/Makefile.am b/src/ctf-writer/Makefile.am new file mode 100644 index 00000000..be8d1e1c --- /dev/null +++ b/src/ctf-writer/Makefile.am @@ -0,0 +1,58 @@ +lib_LTLIBRARIES = libbabeltrace2-ctf-writer.la + +libbabeltrace2_ctf_writer_la_SOURCES = \ + assert-pre.h \ + attributes.c \ + attributes.h \ + clock.c \ + clock-class.c \ + clock-class.h \ + clock.h \ + event.c \ + event-class.c \ + event-class.h \ + event.h \ + field-path.c \ + field-path.h \ + fields.c \ + fields.h \ + field-types.c \ + field-types.h \ + field-wrapper.c \ + field-wrapper.h \ + functor.c \ + functor.h \ + logging.c \ + logging.h \ + object.c \ + object.h \ + object-pool.c \ + object-pool.h \ + resolve.c \ + resolve.h \ + stream.c \ + stream-class.c \ + stream-class.h \ + stream.h \ + trace.c \ + trace.h \ + utils.c \ + utils.h \ + validation.c \ + validation.h \ + values.c \ + values.h \ + visitor.c \ + visitor.h \ + writer.c \ + writer.h + +libbabeltrace2_ctf_writer_la_LDFLAGS = $(LT_NO_UNDEFINED) \ + -version-info $(BABELTRACE_LIBRARY_VERSION) + +libbabeltrace2_ctf_writer_la_LIBADD = \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/ctfser/libbabeltrace2-ctfser.la \ + $(top_builddir)/src/compat/libcompat.la \ + $(UUID_LIBS) diff --git a/src/ctf-writer/assert-pre.h b/src/ctf-writer/assert-pre.h new file mode 100644 index 00000000..8f66d514 --- /dev/null +++ b/src/ctf-writer/assert-pre.h @@ -0,0 +1,129 @@ +#ifndef BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H +#define BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H + +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * The macros in this header use macros defined in + * . We don't want this header to + * automatically include because you + * need to manually define BT_LOG_TAG before including + * and it is unexpected that you + * also need to define it before including this header. + * + * This is a reminder that in order to use + * , you also need to use logging + * explicitly. + */ + +#ifndef BABELTRACE_LOGGING_INTERNAL_H +# error Include before this header. +#endif + +#include +#include +#include "common/babeltrace.h" + +#ifdef BT_DEV_MODE +/* + * Asserts that the library precondition _cond is satisfied. + * + * If _cond is false, log a fatal statement using _fmt and the optional + * arguments using BT_LOGF(), and abort. + * + * To assert that a postcondition is satisfied or that some internal + * object/context/value is in the expected state, use BT_ASSERT(). + */ +# define BT_CTF_ASSERT_PRE(_cond, _fmt, ...) \ + do { \ + if (!(_cond)) { \ + BT_LOGF_STR("Library precondition not satisfied; error is:"); \ + BT_LOGF((_fmt), ##__VA_ARGS__); \ + BT_LOGF_STR("Aborting..."); \ + abort(); \ + } \ + } while (0) + +/* + * Marks a function as being only used within a BT_CTF_ASSERT_PRE() context. + */ +# define BT_CTF_ASSERT_PRE_FUNC + +/* + * Prints the details of an unsatisfied precondition without immediately + * aborting. You should use this within a function which checks + * preconditions, but which is called from a BT_CTF_ASSERT_PRE() context, so + * that the function can still return its result for BT_CTF_ASSERT_PRE() to + * evaluate it. + * + * Example: + * + * BT_CTF_ASSERT_PRE_FUNC + * static inline bool check_complex_precond(...) + * { + * ... + * + * if (...) { + * BT_CTF_ASSERT_PRE_MSG("Invalid object: ...", ...); + * return false; + * } + * + * ... + * } + * + * ... + * + * BT_CTF_ASSERT_PRE(check_complex_precond(...), + * "Precondition is not satisfied: ...", ...); + */ +# define BT_CTF_ASSERT_PRE_MSG BT_LOGF +#else +# define BT_CTF_ASSERT_PRE(_cond, _fmt, ...) ((void) sizeof((void) (_cond), 0)) +# define BT_CTF_ASSERT_PRE_FUNC BT_UNUSED +# define BT_CTF_ASSERT_PRE_MSG(_fmt, ...) +#endif /* BT_DEV_MODE */ + +/* + * Developer mode: asserts that a given variable is not NULL. + */ +#define BT_CTF_ASSERT_PRE_NON_NULL(_obj, _obj_name) \ + BT_CTF_ASSERT_PRE((_obj) != NULL, "%s is NULL: ", _obj_name) + +/* + * Developer mode: asserts that a given object is NOT frozen. This macro + * checks the `frozen` field of _obj. + */ +#define BT_CTF_ASSERT_PRE_HOT(_obj, _obj_name, _fmt, ...) \ + BT_CTF_ASSERT_PRE(!(_obj)->frozen, "%s is frozen" _fmt, _obj_name, \ + ##__VA_ARGS__) + +/* + * Developer mode: asserts that a given index is less than a given size. + */ +#define BT_CTF_ASSERT_PRE_VALID_INDEX(_index, _length) \ + BT_CTF_ASSERT_PRE((_index) < (_length), \ + "Index is out of bounds: index=%" PRIu64 ", " \ + "count=%" PRIu64, (uint64_t) (_index), (uint64_t) (_length)) + +#endif /* BABELTRACE_CTF_WRITER_ASSERT_PRE_INTERNAL_H */ diff --git a/src/ctf-writer/attributes.c b/src/ctf-writer/attributes.c new file mode 100644 index 00000000..e79dc58f --- /dev/null +++ b/src/ctf-writer/attributes.c @@ -0,0 +1,346 @@ +/* + * attributes.c + * + * Babeltrace CTF writer - Attributes + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-ATTRS" +#include "logging.h" + +#include "common/assert.h" +#include "common/babeltrace.h" +#include "compat/string.h" +#include +#include + +#include "values.h" + +#define BT_CTF_ATTR_NAME_INDEX 0 +#define BT_CTF_ATTR_VALUE_INDEX 1 + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_create(void) +{ + struct bt_ctf_private_value *attr_obj; + + /* + * Attributes: array value object of array value objects, each one + * containing two entries: a string value object (attributes + * field name), and a value object (attributes field value). + * + * Example (JSON representation): + * + * [ + * ["hostname", "eeppdesk"], + * ["sysname", "Linux"], + * ["tracer_major", 2], + * ["tracer_minor", 5] + * ] + */ + BT_LOGD_STR("Creating attributes object."); + attr_obj = bt_ctf_private_value_array_create(); + if (!attr_obj) { + BT_LOGE_STR("Failed to create array value."); + } else { + BT_LOGD("Created attributes object: addr=%p", + attr_obj); + } + + return attr_obj; +} + +BT_HIDDEN +void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj) +{ + BT_LOGD("Destroying attributes object: addr=%p", attr_obj); + bt_ctf_object_put_ref(attr_obj); +} + +BT_HIDDEN +int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj) +{ + return bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); +} + +BT_HIDDEN +const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj, + uint64_t index) +{ + const char *ret = NULL; + struct bt_ctf_private_value *attr_field_obj = NULL; + struct bt_ctf_private_value *attr_field_name_obj = NULL; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + goto end; + } + + if (index >= bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "index=%" PRIu64 ", count=%" PRId64, + index, bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))); + goto end; + } + + attr_field_obj = bt_ctf_private_value_array_borrow_element_by_index( + attr_obj, index); + if (!attr_field_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, index); + goto end; + } + + attr_field_name_obj = + bt_ctf_private_value_array_borrow_element_by_index( + attr_field_obj, BT_CTF_ATTR_NAME_INDEX); + if (!attr_field_name_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_CTF_ATTR_NAME_INDEX); + goto end; + } + + ret = bt_ctf_value_string_get( + bt_ctf_private_value_as_value(attr_field_name_obj)); + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj, + uint64_t index) +{ + struct bt_ctf_private_value *value_obj = NULL; + struct bt_ctf_private_value *attr_field_obj = NULL; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + goto end; + } + + if (index >= bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "index=%" PRIu64 ", count=%" PRId64, + index, bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj))); + goto end; + } + + attr_field_obj = bt_ctf_private_value_array_borrow_element_by_index( + attr_obj, index); + if (!attr_field_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, index); + goto end; + } + + value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_field_obj, + BT_CTF_ATTR_VALUE_INDEX); + if (!value_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_CTF_ATTR_VALUE_INDEX); + } + +end: + return value_obj; +} + +static +struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_by_name( + struct bt_ctf_private_value *attr_obj, const char *name) +{ + uint64_t i; + int64_t attr_size; + struct bt_ctf_private_value *value_obj = NULL; + struct bt_ctf_private_value *attr_field_name_obj = NULL; + + attr_size = bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); + if (attr_size < 0) { + BT_LOGE("Cannot get array value's size: value-addr=%p", + attr_obj); + goto error; + } + + for (i = 0; i < attr_size; ++i) { + const char *field_name; + + value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_obj, i); + if (!value_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, i); + goto error; + } + + attr_field_name_obj = bt_ctf_private_value_array_borrow_element_by_index(value_obj, + BT_CTF_ATTR_NAME_INDEX); + if (!attr_field_name_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, + value_obj, (int64_t) BT_CTF_ATTR_NAME_INDEX); + goto error; + } + + field_name = bt_ctf_value_string_get( + bt_ctf_private_value_as_value(attr_field_name_obj)); + + if (!strcmp(field_name, name)) { + break; + } + + value_obj = NULL; + } + + return value_obj; + +error: + value_obj = NULL; + return value_obj; +} + +BT_HIDDEN +int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj, + const char *name, struct bt_ctf_private_value *value_obj) +{ + int ret = 0; + struct bt_ctf_private_value *attr_field_obj = NULL; + + if (!attr_obj || !name || !value_obj) { + BT_LOGW("Invalid parameter: attributes object, name, or value object is NULL: " + "attr-value-addr=%p, name-addr=%p, value-addr=%p", + attr_obj, name, value_obj); + ret = -1; + goto end; + } + + attr_field_obj = bt_ctf_attributes_borrow_field_by_name(attr_obj, name); + if (attr_field_obj) { + ret = bt_ctf_private_value_array_set_element_by_index( + attr_field_obj, BT_CTF_ATTR_VALUE_INDEX, + bt_ctf_private_value_as_value(value_obj)); + attr_field_obj = NULL; + goto end; + } + + attr_field_obj = bt_ctf_private_value_array_create(); + if (!attr_field_obj) { + BT_LOGE_STR("Failed to create empty array value."); + ret = -1; + goto end; + } + + ret = bt_ctf_private_value_array_append_string_element(attr_field_obj, name); + ret |= bt_ctf_private_value_array_append_element(attr_field_obj, + bt_ctf_private_value_as_value(value_obj)); + if (ret) { + BT_LOGE("Cannot append elements to array value: addr=%p", + attr_field_obj); + goto end; + } + + ret = bt_ctf_private_value_array_append_element(attr_obj, + bt_ctf_private_value_as_value(attr_field_obj)); + if (ret) { + BT_LOGE("Cannot append element to array value: " + "array-value-addr=%p, element-value-addr=%p", + attr_obj, attr_field_obj); + } + +end: + bt_ctf_object_put_ref(attr_field_obj); + return ret; +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name( + struct bt_ctf_private_value *attr_obj, const char *name) +{ + struct bt_ctf_private_value *value_obj = NULL; + struct bt_ctf_private_value *attr_field_obj = NULL; + + if (!attr_obj || !name) { + BT_LOGW("Invalid parameter: attributes object or name is NULL: " + "value-addr=%p, name-addr=%p", attr_obj, name); + goto end; + } + + attr_field_obj = bt_ctf_attributes_borrow_field_by_name(attr_obj, name); + if (!attr_field_obj) { + BT_LOGD("Cannot find attributes object's field by name: " + "value-addr=%p, name=\"%s\"", attr_obj, name); + goto end; + } + + value_obj = bt_ctf_private_value_array_borrow_element_by_index(attr_field_obj, + BT_CTF_ATTR_VALUE_INDEX); + if (!value_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_CTF_ATTR_VALUE_INDEX); + } + +end: + return value_obj; +} + +BT_HIDDEN +int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj) +{ + uint64_t i; + int64_t count; + int ret = 0; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + ret = -1; + goto end; + } + + BT_LOGD("Freezing attributes object: value-addr=%p", attr_obj); + count = bt_ctf_value_array_get_size(bt_ctf_private_value_as_value(attr_obj)); + BT_ASSERT(count >= 0); + + /* + * We do not freeze the array value object itself here, since + * internal stuff could need to modify/add attributes. Each + * attribute is frozen one by one. + */ + for (i = 0; i < count; ++i) { + struct bt_ctf_private_value *obj = NULL; + + obj = bt_ctf_attributes_borrow_field_value(attr_obj, i); + if (!obj) { + BT_LOGE("Cannot get attributes object's field value by index: " + "value-addr=%p, index=%" PRIu64, + attr_obj, i); + ret = -1; + goto end; + } + + bt_ctf_value_freeze(bt_ctf_private_value_as_value(obj)); + } + +end: + return ret; +} diff --git a/src/ctf-writer/attributes.h b/src/ctf-writer/attributes.h new file mode 100644 index 00000000..a9906533 --- /dev/null +++ b/src/ctf-writer/attributes.h @@ -0,0 +1,68 @@ +#ifndef BABELTRACE_CTF_WRITER_ATTRIBUTES_H +#define BABELTRACE_CTF_WRITER_ATTRIBUTES_H + +/* + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "common/babeltrace.h" + +#include "values.h" + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_create(void); + +BT_HIDDEN +void bt_ctf_attributes_destroy(struct bt_ctf_private_value *attr_obj); + +BT_HIDDEN +int64_t bt_ctf_attributes_get_count(struct bt_ctf_private_value *attr_obj); + +BT_HIDDEN +const char *bt_ctf_attributes_get_field_name(struct bt_ctf_private_value *attr_obj, + uint64_t index); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value(struct bt_ctf_private_value *attr_obj, + uint64_t index); + +BT_HIDDEN +int bt_ctf_attributes_set_field_value(struct bt_ctf_private_value *attr_obj, + const char *name, struct bt_ctf_private_value *value_obj); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_attributes_borrow_field_value_by_name( + struct bt_ctf_private_value *attr_obj, const char *name); + +BT_HIDDEN +int bt_ctf_attributes_freeze(struct bt_ctf_private_value *attr_obj); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_CTF_WRITER_ATTRIBUTES_H */ diff --git a/src/ctf-writer/clock-class.c b/src/ctf-writer/clock-class.c new file mode 100644 index 00000000..258d4a6c --- /dev/null +++ b/src/ctf-writer/clock-class.c @@ -0,0 +1,705 @@ +/* + * clock-class.c + * + * Babeltrace CTF writer - Clock class + * + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-CLOCK-CLASS" +#include "logging.h" + +#include "compat/uuid.h" +#include +#include +#include "compat/compiler.h" +#include +#include "compat/string.h" +#include +#include "common/assert.h" + +#include "assert-pre.h" +#include "clock-class.h" +#include "object.h" + +static +void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj); + +BT_HIDDEN +bt_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class) +{ + return clock_class && clock_class->name; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, + const char *name) +{ + int ret = 0; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + if (!bt_ctf_identifier_is_valid(name)) { + BT_LOGW("Clock class's name is not a valid CTF identifier: " + "addr=%p, name=\"%s\"", + clock_class, name); + ret = -1; + goto end; + } + + if (clock_class->name) { + g_string_assign(clock_class->name, name); + } else { + clock_class->name = g_string_new(name); + if (!clock_class->name) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = -1; + goto end; + } + } + + BT_LOGV("Set clock class's name: addr=%p, name=\"%s\"", + clock_class, name); + +end: + return ret; +} + +static +bool validate_freq(struct bt_ctf_clock_class *clock_class, + const char *name, uint64_t freq) +{ + bool is_valid = true; + + if (freq == -1ULL || freq == 0) { + BT_LOGW("Invalid parameter: frequency is invalid: " + "addr=%p, name=\"%s\", freq=%" PRIu64, + clock_class, name, freq); + is_valid = false; + goto end; + } + +end: + return is_valid; +} + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name, + uint64_t freq) +{ + int ret; + struct bt_ctf_clock_class *clock_class = NULL; + + BT_LOGD("Creating default clock class object: name=\"%s\"", + name); + + if (!validate_freq(NULL, name, freq)) { + /* validate_freq() logs errors */ + goto error; + } + + clock_class = g_new0(struct bt_ctf_clock_class, 1); + if (!clock_class) { + BT_LOGE_STR("Failed to allocate one clock class."); + goto error; + } + + clock_class->precision = 1; + clock_class->frequency = freq; + bt_ctf_object_init_shared(&clock_class->base, bt_ctf_clock_class_destroy); + + if (name) { + ret = bt_ctf_clock_class_set_name(clock_class, name); + if (ret) { + /* bt_ctf_clock_class_set_name() logs errors */ + goto error; + } + } + + BT_LOGD("Created clock class object: addr=%p, name=\"%s\"", + clock_class, name); + return clock_class; +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(clock_class); + return clock_class; +} + +BT_HIDDEN +const char *bt_ctf_clock_class_get_name(struct bt_ctf_clock_class *clock_class) +{ + const char *ret = NULL; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + goto end; + } + + if (clock_class->name) { + ret = clock_class->name->str; + } + +end: + return ret; +} + +BT_HIDDEN +const char *bt_ctf_clock_class_get_description( + struct bt_ctf_clock_class *clock_class) +{ + const char *ret = NULL; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + goto end; + } + + if (clock_class->description) { + ret = clock_class->description->str; + } +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_description(struct bt_ctf_clock_class *clock_class, + const char *desc) +{ + int ret = 0; + + if (!clock_class || !desc) { + BT_LOGW("Invalid parameter: clock class or description is NULL: " + "clock-class-addr=%p, name=\"%s\", desc-addr=%p", + clock_class, bt_ctf_clock_class_get_name(clock_class), + desc); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->description = g_string_new(desc); + ret = clock_class->description ? 0 : -1; + BT_LOGV("Set clock class's description: addr=%p, " + "name=\"%s\", desc=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class), desc); +end: + return ret; +} + +BT_HIDDEN +uint64_t bt_ctf_clock_class_get_frequency( + struct bt_ctf_clock_class *clock_class) +{ + uint64_t ret = -1ULL; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + goto end; + } + + ret = clock_class->frequency; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_frequency(struct bt_ctf_clock_class *clock_class, + uint64_t freq) +{ + int ret = 0; + + if (!clock_class) { + BT_LOGW("Invalid parameter: clock class is NULL or frequency is invalid: " + "addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + if (!validate_freq(clock_class, bt_ctf_clock_class_get_name(clock_class), + freq)) { + /* validate_freq() logs errors */ + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->frequency = freq; + BT_LOGV("Set clock class's frequency: addr=%p, name=\"%s\", freq=%" PRIu64, + clock_class, bt_ctf_clock_class_get_name(clock_class), freq); +end: + return ret; +} + +BT_HIDDEN +uint64_t bt_ctf_clock_class_get_precision(struct bt_ctf_clock_class *clock_class) +{ + uint64_t ret = -1ULL; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + goto end; + } + + ret = clock_class->precision; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_precision(struct bt_ctf_clock_class *clock_class, + uint64_t precision) +{ + int ret = 0; + + if (!clock_class || precision == -1ULL) { + BT_LOGW("Invalid parameter: clock class is NULL or precision is invalid: " + "addr=%p, name=\"%s\", precision=%" PRIu64, + clock_class, bt_ctf_clock_class_get_name(clock_class), + precision); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->precision = precision; + BT_LOGV("Set clock class's precision: addr=%p, name=\"%s\", precision=%" PRIu64, + clock_class, bt_ctf_clock_class_get_name(clock_class), + precision); +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_get_offset_s(struct bt_ctf_clock_class *clock_class, + int64_t *offset_s) +{ + int ret = 0; + + if (!clock_class || !offset_s) { + BT_LOGW("Invalid parameter: clock class or offset pointer is NULL: " + "clock-class-addr=%p, name=\"%s\", offset-addr=%p", + clock_class, bt_ctf_clock_class_get_name(clock_class), + offset_s); + ret = -1; + goto end; + } + + *offset_s = clock_class->offset_s; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_offset_s(struct bt_ctf_clock_class *clock_class, + int64_t offset_s) +{ + int ret = 0; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->offset_s = offset_s; + BT_LOGV("Set clock class's offset (seconds): " + "addr=%p, name=\"%s\", offset-s=%" PRId64, + clock_class, bt_ctf_clock_class_get_name(clock_class), + offset_s); +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_get_offset_cycles(struct bt_ctf_clock_class *clock_class, + int64_t *offset) +{ + int ret = 0; + + if (!clock_class || !offset) { + BT_LOGW("Invalid parameter: clock class or offset pointer is NULL: " + "clock-class-addr=%p, name=\"%s\", offset-addr=%p", + clock_class, bt_ctf_clock_class_get_name(clock_class), + offset); + ret = -1; + goto end; + } + + *offset = clock_class->offset; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_offset_cycles(struct bt_ctf_clock_class *clock_class, + int64_t offset) +{ + int ret = 0; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->offset = offset; + BT_LOGV("Set clock class's offset (cycles): addr=%p, name=\"%s\", offset-cycles=%" PRId64, + clock_class, bt_ctf_clock_class_get_name(clock_class), offset); +end: + return ret; +} + +BT_HIDDEN +bt_bool bt_ctf_clock_class_is_absolute(struct bt_ctf_clock_class *clock_class) +{ + int ret = -1; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + goto end; + } + + ret = clock_class->absolute; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_is_absolute(struct bt_ctf_clock_class *clock_class, + bt_bool is_absolute) +{ + int ret = 0; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + clock_class->absolute = !!is_absolute; + BT_LOGV("Set clock class's absolute flag: addr=%p, name=\"%s\", is-absolute=%d", + clock_class, bt_ctf_clock_class_get_name(clock_class), + is_absolute); +end: + return ret; +} + +BT_HIDDEN +const unsigned char *bt_ctf_clock_class_get_uuid( + struct bt_ctf_clock_class *clock_class) +{ + const unsigned char *ret; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = NULL; + goto end; + } + + if (!clock_class->uuid_set) { + BT_LOGV("Clock class's UUID is not set: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = NULL; + goto end; + } + + ret = clock_class->uuid; +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class, + const unsigned char *uuid) +{ + int ret = 0; + + if (!clock_class || !uuid) { + BT_LOGW("Invalid parameter: clock class or UUID is NULL: " + "clock-class-addr=%p, name=\"%s\", uuid-addr=%p", + clock_class, bt_ctf_clock_class_get_name(clock_class), + uuid); + ret = -1; + goto end; + } + + if (clock_class->frozen) { + BT_LOGW("Invalid parameter: clock class is frozen: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + memcpy(clock_class->uuid, uuid, BABELTRACE_UUID_LEN); + clock_class->uuid_set = 1; + BT_LOGV("Set clock class's UUID: addr=%p, name=\"%s\", " + "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + clock_class, bt_ctf_clock_class_get_name(clock_class), + (unsigned int) uuid[0], + (unsigned int) uuid[1], + (unsigned int) uuid[2], + (unsigned int) uuid[3], + (unsigned int) uuid[4], + (unsigned int) uuid[5], + (unsigned int) uuid[6], + (unsigned int) uuid[7], + (unsigned int) uuid[8], + (unsigned int) uuid[9], + (unsigned int) uuid[10], + (unsigned int) uuid[11], + (unsigned int) uuid[12], + (unsigned int) uuid[13], + (unsigned int) uuid[14], + (unsigned int) uuid[15]); +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class) +{ + if (!clock_class || clock_class->frozen) { + return; + } + + BT_LOGD("Freezing clock class: addr=%p, name=\"%s\"", + clock_class, bt_ctf_clock_class_get_name(clock_class)); + clock_class->frozen = 1; +} + +static +void bt_ctf_clock_class_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_clock_class *clock_class; + + clock_class = container_of(obj, struct bt_ctf_clock_class, base); + BT_LOGD("Destroying clock class: addr=%p, name=\"%s\"", + obj, bt_ctf_clock_class_get_name(clock_class)); + + if (clock_class->name) { + g_string_free(clock_class->name, TRUE); + } + + if (clock_class->description) { + g_string_free(clock_class->description, TRUE); + } + + g_free(clock_class); +} + +BT_HIDDEN +int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a, + struct bt_ctf_clock_class *clock_class_b) +{ + int ret = 1; + BT_ASSERT(clock_class_a); + BT_ASSERT(clock_class_b); + + /* Name */ + if (strcmp(clock_class_a->name->str, clock_class_b->name->str) != 0) { + BT_LOGV("Clock classes differ: different names: " + "cc-a-name=\"%s\", cc-b-name=\"%s\"", + clock_class_a->name->str, + clock_class_b->name->str); + goto end; + } + + /* Description */ + if (clock_class_a->description) { + if (!clock_class_b->description) { + BT_LOGV_STR("Clock classes differ: clock class A has a " + "description, but clock class B does not."); + goto end; + } + + if (strcmp(clock_class_a->name->str, clock_class_b->name->str) + != 0) { + BT_LOGV("Clock classes differ: different descriptions: " + "cc-a-descr=\"%s\", cc-b-descr=\"%s\"", + clock_class_a->description->str, + clock_class_b->description->str); + goto end; + } + } else { + if (clock_class_b->description) { + BT_LOGV_STR("Clock classes differ: clock class A has " + "no description, but clock class B has one."); + goto end; + } + } + + /* Frequency */ + if (clock_class_a->frequency != clock_class_b->frequency) { + BT_LOGV("Clock classes differ: different frequencies: " + "cc-a-freq=%" PRIu64 ", cc-b-freq=%" PRIu64, + clock_class_a->frequency, + clock_class_b->frequency); + goto end; + } + + /* Precision */ + if (clock_class_a->precision != clock_class_b->precision) { + BT_LOGV("Clock classes differ: different precisions: " + "cc-a-freq=%" PRIu64 ", cc-b-freq=%" PRIu64, + clock_class_a->precision, + clock_class_b->precision); + goto end; + } + + /* Offset (seconds) */ + if (clock_class_a->offset_s != clock_class_b->offset_s) { + BT_LOGV("Clock classes differ: different offsets (seconds): " + "cc-a-offset-s=%" PRId64 ", cc-b-offset-s=%" PRId64, + clock_class_a->offset_s, + clock_class_b->offset_s); + goto end; + } + + /* Offset (cycles) */ + if (clock_class_a->offset != clock_class_b->offset) { + BT_LOGV("Clock classes differ: different offsets (cycles): " + "cc-a-offset-s=%" PRId64 ", cc-b-offset-s=%" PRId64, + clock_class_a->offset, + clock_class_b->offset); + goto end; + } + + /* UUIDs */ + if (clock_class_a->uuid_set) { + if (!clock_class_b->uuid_set) { + BT_LOGV_STR("Clock classes differ: clock class A has a " + "UUID, but clock class B does not."); + goto end; + } + + if (memcmp(clock_class_a->uuid, clock_class_b->uuid, + BABELTRACE_UUID_LEN) != 0) { + BT_LOGV("Clock classes differ: different UUIDs: " + "cc-a-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " + "cc-b-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + (unsigned int) clock_class_a->uuid[0], + (unsigned int) clock_class_a->uuid[1], + (unsigned int) clock_class_a->uuid[2], + (unsigned int) clock_class_a->uuid[3], + (unsigned int) clock_class_a->uuid[4], + (unsigned int) clock_class_a->uuid[5], + (unsigned int) clock_class_a->uuid[6], + (unsigned int) clock_class_a->uuid[7], + (unsigned int) clock_class_a->uuid[8], + (unsigned int) clock_class_a->uuid[9], + (unsigned int) clock_class_a->uuid[10], + (unsigned int) clock_class_a->uuid[11], + (unsigned int) clock_class_a->uuid[12], + (unsigned int) clock_class_a->uuid[13], + (unsigned int) clock_class_a->uuid[14], + (unsigned int) clock_class_a->uuid[15], + (unsigned int) clock_class_b->uuid[0], + (unsigned int) clock_class_b->uuid[1], + (unsigned int) clock_class_b->uuid[2], + (unsigned int) clock_class_b->uuid[3], + (unsigned int) clock_class_b->uuid[4], + (unsigned int) clock_class_b->uuid[5], + (unsigned int) clock_class_b->uuid[6], + (unsigned int) clock_class_b->uuid[7], + (unsigned int) clock_class_b->uuid[8], + (unsigned int) clock_class_b->uuid[9], + (unsigned int) clock_class_b->uuid[10], + (unsigned int) clock_class_b->uuid[11], + (unsigned int) clock_class_b->uuid[12], + (unsigned int) clock_class_b->uuid[13], + (unsigned int) clock_class_b->uuid[14], + (unsigned int) clock_class_b->uuid[15]); + goto end; + } + } else { + if (clock_class_b->uuid_set) { + BT_LOGV_STR("Clock classes differ: clock class A has " + "no UUID, but clock class B has one."); + goto end; + } + } + + /* Absolute */ + if (!!clock_class_a->absolute != !!clock_class_b->absolute) { + BT_LOGV("Clock classes differ: one is absolute, the other " + "is not: cc-a-is-absolute=%d, cc-b-is-absolute=%d", + !!clock_class_a->absolute, + !!clock_class_b->absolute); + goto end; + } + + /* Equal */ + ret = 0; + +end: + return ret; +} diff --git a/src/ctf-writer/clock-class.h b/src/ctf-writer/clock-class.h new file mode 100644 index 00000000..d3175085 --- /dev/null +++ b/src/ctf-writer/clock-class.h @@ -0,0 +1,120 @@ +#ifndef BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H +#define BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "lib/object-pool.h" +#include "compat/uuid.h" +#include +#include +#include +#include + +#include "object.h" + +struct bt_ctf_clock_class { + struct bt_ctf_object base; + GString *name; + GString *description; + uint64_t frequency; + uint64_t precision; + int64_t offset_s; /* Offset in seconds */ + int64_t offset; /* Offset in ticks */ + unsigned char uuid[BABELTRACE_UUID_LEN]; + int uuid_set; + int absolute; + + /* + * A clock's properties can't be modified once it is added to a stream + * class. + */ + int frozen; +}; + +BT_HIDDEN +void bt_ctf_clock_class_freeze(struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +bt_bool bt_ctf_clock_class_is_valid(struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +int bt_ctf_clock_class_compare(struct bt_ctf_clock_class *clock_class_a, + struct bt_ctf_clock_class *clock_class_b); + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_clock_class_create(const char *name, + uint64_t freq); +BT_HIDDEN +const char *bt_ctf_clock_class_get_name( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_name(struct bt_ctf_clock_class *clock_class, + const char *name); +BT_HIDDEN +const char *bt_ctf_clock_class_get_description( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_description( + struct bt_ctf_clock_class *clock_class, + const char *desc); +BT_HIDDEN +uint64_t bt_ctf_clock_class_get_frequency( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_frequency( + struct bt_ctf_clock_class *clock_class, uint64_t freq); +BT_HIDDEN +uint64_t bt_ctf_clock_class_get_precision( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_precision( + struct bt_ctf_clock_class *clock_class, uint64_t precision); +BT_HIDDEN +int bt_ctf_clock_class_get_offset_s( + struct bt_ctf_clock_class *clock_class, int64_t *seconds); +BT_HIDDEN +int bt_ctf_clock_class_set_offset_s( + struct bt_ctf_clock_class *clock_class, int64_t seconds); +BT_HIDDEN +int bt_ctf_clock_class_get_offset_cycles( + struct bt_ctf_clock_class *clock_class, int64_t *cycles); +BT_HIDDEN +int bt_ctf_clock_class_set_offset_cycles( + struct bt_ctf_clock_class *clock_class, int64_t cycles); +BT_HIDDEN +bt_bool bt_ctf_clock_class_is_absolute( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_is_absolute( + struct bt_ctf_clock_class *clock_class, bt_bool is_absolute); +BT_HIDDEN +const unsigned char *bt_ctf_clock_class_get_uuid( + struct bt_ctf_clock_class *clock_class); +BT_HIDDEN +int bt_ctf_clock_class_set_uuid(struct bt_ctf_clock_class *clock_class, + const unsigned char *uuid); + +#endif /* BABELTRACE_CTF_WRITER_CLOCK_CLASS_INTERNAL_H */ diff --git a/src/ctf-writer/clock.c b/src/ctf-writer/clock.c new file mode 100644 index 00000000..cb559265 --- /dev/null +++ b/src/ctf-writer/clock.c @@ -0,0 +1,275 @@ +/* + * clock.c + * + * Babeltrace CTF writer - Clock + * + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017 Philippe Proulx + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-CLOCK" +#include "logging.h" + +#include "common/assert.h" +#include "compat/uuid.h" +#include "compat/compiler.h" +#include +#include +#include + +#include "clock-class.h" +#include "clock.h" +#include "object.h" +#include "writer.h" + +static +void bt_ctf_clock_destroy(struct bt_ctf_object *obj); + +struct bt_ctf_clock *bt_ctf_clock_create(const char *name) +{ + int ret; + struct bt_ctf_clock *clock = NULL; + unsigned char cc_uuid[BABELTRACE_UUID_LEN]; + + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + clock = g_new0(struct bt_ctf_clock, 1); + if (!clock) { + goto error; + } + + bt_ctf_object_init_shared(&clock->base, bt_ctf_clock_destroy); + clock->value = 0; + + /* Pre-2.0.0 backward compatibility: default frequency is 1 GHz */ + clock->clock_class = (void *) bt_ctf_clock_class_create(name, 1000000000); + if (!clock->clock_class) { + goto error; + } + + /* Automatically set clock class's UUID. */ + ret = bt_uuid_generate(cc_uuid); + if (ret) { + goto error; + } + + ret = bt_ctf_clock_class_set_uuid(clock->clock_class, cc_uuid); + BT_ASSERT(ret == 0); + return clock; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(clock); + return clock; +} + +const char *bt_ctf_clock_get_name(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_name(clock->clock_class); +} + +const char *bt_ctf_clock_get_description(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_description(clock->clock_class); +} + +int bt_ctf_clock_set_description(struct bt_ctf_clock *clock, const char *desc) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_description(clock->clock_class, + desc); +} + +uint64_t bt_ctf_clock_get_frequency(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_frequency(clock->clock_class); +} + +int bt_ctf_clock_set_frequency(struct bt_ctf_clock *clock, uint64_t freq) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_frequency(clock->clock_class, + freq); +} + +uint64_t bt_ctf_clock_get_precision(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_precision(clock->clock_class); +} + +int bt_ctf_clock_set_precision(struct bt_ctf_clock *clock, uint64_t precision) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_precision(clock->clock_class, + precision); +} + +int bt_ctf_clock_get_offset_s(struct bt_ctf_clock *clock, int64_t *offset_s) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_offset_s(clock->clock_class, + offset_s); +} + +int bt_ctf_clock_set_offset_s(struct bt_ctf_clock *clock, int64_t offset_s) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_offset_s(clock->clock_class, + offset_s); +} + +int bt_ctf_clock_get_offset(struct bt_ctf_clock *clock, int64_t *offset) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_offset_cycles(clock->clock_class, + offset); +} + +int bt_ctf_clock_set_offset(struct bt_ctf_clock *clock, int64_t offset) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_offset_cycles(clock->clock_class, + offset); +} + +int bt_ctf_clock_get_is_absolute(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_is_absolute(clock->clock_class); +} + +int bt_ctf_clock_set_is_absolute(struct bt_ctf_clock *clock, int is_absolute) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_is_absolute(clock->clock_class, + is_absolute); +} + +const unsigned char *bt_ctf_clock_get_uuid(struct bt_ctf_clock *clock) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_get_uuid(clock->clock_class); +} + +int bt_ctf_clock_set_uuid(struct bt_ctf_clock *clock, const unsigned char *uuid) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + return bt_ctf_clock_class_set_uuid(clock->clock_class, uuid); +} + +int bt_ctf_clock_set_time(struct bt_ctf_clock *clock, int64_t time) +{ + int64_t value; + struct bt_ctf_clock_class *cc; + + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + cc = clock->clock_class; + + /* Common case where cycles are actually nanoseconds */ + if (cc->frequency == 1000000000) { + value = time; + } else { + value = (uint64_t) (((double) time * + (double) cc->frequency) / 1e9); + } + + BT_CTF_ASSERT_PRE(clock->value <= value, + "CTF writer clock value must be updated monotonically: " + "prev-value=%" PRId64 ", new-value=%" PRId64, + clock->value, value); + clock->value = value; + return 0; +} + +BT_HIDDEN +int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value) +{ + BT_CTF_ASSERT_PRE_NON_NULL(clock, "CTF writer clock"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + *value = clock->value; + return 0; +} + +static +void bt_ctf_clock_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_clock *clock; + + clock = container_of(obj, struct bt_ctf_clock, base); + bt_ctf_object_put_ref(clock->clock_class); + g_free(clock); +} + +BT_HIDDEN +void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class, + struct metadata_context *context) +{ + unsigned char *uuid; + + BT_LOGD("Serializing clock class's metadata: clock-class-addr=%p, " + "name=\"%s\", metadata-context-addr=%p", clock_class, + bt_ctf_clock_class_get_name(clock_class), + context); + + if (!clock_class || !context) { + BT_LOGW("Invalid parameter: clock class or metadata context is NULL: " + "clock-class-addr=%p, name=\"%s\", metadata-context-addr=%p", + clock_class, + bt_ctf_clock_class_get_name(clock_class), + context); + return; + } + + uuid = clock_class->uuid; + g_string_append(context->string, "clock {\n"); + g_string_append_printf(context->string, "\tname = %s;\n", + clock_class->name->str); + + if (clock_class->uuid_set) { + g_string_append_printf(context->string, + "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + + if (clock_class->description) { + g_string_append_printf(context->string, "\tdescription = \"%s\";\n", + clock_class->description->str); + } + + g_string_append_printf(context->string, "\tfreq = %" PRIu64 ";\n", + clock_class->frequency); + g_string_append_printf(context->string, "\tprecision = %" PRIu64 ";\n", + clock_class->precision); + g_string_append_printf(context->string, "\toffset_s = %" PRIu64 ";\n", + clock_class->offset_s); + g_string_append_printf(context->string, "\toffset = %" PRIu64 ";\n", + clock_class->offset); + g_string_append_printf(context->string, "\tabsolute = %s;\n", + clock_class->absolute ? "true" : "false"); + g_string_append(context->string, "};\n\n"); +} diff --git a/src/ctf-writer/clock.h b/src/ctf-writer/clock.h new file mode 100644 index 00000000..f510d736 --- /dev/null +++ b/src/ctf-writer/clock.h @@ -0,0 +1,50 @@ +#ifndef BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H +#define BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include +#include "compat/uuid.h" + +#include "clock-class.h" +#include "object.h" +#include "trace.h" + +struct bt_ctf_clock { + struct bt_ctf_object base; + struct bt_ctf_clock_class *clock_class; + uint64_t value; /* Current clock value */ +}; + +struct metadata_context; + +BT_HIDDEN +int bt_ctf_clock_get_value(struct bt_ctf_clock *clock, uint64_t *value); + +BT_HIDDEN +void bt_ctf_clock_class_serialize(struct bt_ctf_clock_class *clock_class, + struct metadata_context *context); + +#endif /* BABELTRACE_CTF_WRITER_CLOCK_INTERNAL_H */ diff --git a/src/ctf-writer/event-class.c b/src/ctf-writer/event-class.c new file mode 100644 index 00000000..419707e1 --- /dev/null +++ b/src/ctf-writer/event-class.c @@ -0,0 +1,588 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-EVENT-CLASS" +#include "logging.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" + +#include "assert-pre.h" +#include "attributes.h" +#include "event-class.h" +#include "event.h" +#include "fields.h" +#include "field-types.h" +#include "stream-class.h" +#include "trace.h" +#include "utils.h" +#include "validation.h" +#include "values.h" +#include "writer.h" + +BT_HIDDEN +void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj) +{ + struct bt_ctf_event_class_common *event_class; + + event_class = container_of(obj, struct bt_ctf_event_class_common, base); + BT_LOGD("Finalizing common event class: addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + + if (event_class->name) { + g_string_free(event_class->name, TRUE); + } + + if (event_class->emf_uri) { + g_string_free(event_class->emf_uri, TRUE); + } + + BT_LOGD_STR("Putting context field type."); + bt_ctf_object_put_ref(event_class->context_field_type); + BT_LOGD_STR("Putting payload field type."); + bt_ctf_object_put_ref(event_class->payload_field_type); +} + +BT_HIDDEN +int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class, + const char *name, bt_ctf_object_release_func release_func, + bt_ctf_field_type_structure_create_func ft_struct_create_func) +{ + int ret = 0; + + BT_LOGD("Initializing common event class object: name=\"%s\"", + name); + bt_ctf_object_init_shared_with_parent(&event_class->base, release_func); + event_class->payload_field_type = ft_struct_create_func(); + if (!event_class->payload_field_type) { + BT_LOGE_STR("Cannot create event class's initial payload field type object."); + goto error; + } + + event_class->id = -1; + event_class->name = g_string_new(name); + if (!event_class->name) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + event_class->emf_uri = g_string_new(NULL); + if (!event_class->emf_uri) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + event_class->log_level = BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED; + BT_LOGD("Initialized common event class object: addr=%p, name=\"%s\"", + event_class, bt_ctf_event_class_common_get_name(event_class)); + return ret; + +error: + ret = -1; + return ret; +} + +BT_HIDDEN +void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class) +{ + BT_ASSERT(event_class); + + if (event_class->frozen) { + return; + } + + BT_LOGD("Freezing event class: addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + event_class->frozen = 1; + BT_LOGD_STR("Freezing event class's context field type."); + bt_ctf_field_type_common_freeze(event_class->context_field_type); + BT_LOGD_STR("Freezing event class's payload field type."); + bt_ctf_field_type_common_freeze(event_class->payload_field_type); +} + +BT_HIDDEN +int bt_ctf_event_class_common_validate_single_clock_class( + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_clock_class **expected_clock_class) +{ + int ret = 0; + + BT_ASSERT(event_class); + BT_ASSERT(expected_clock_class); + ret = bt_ctf_field_type_common_validate_single_clock_class( + event_class->context_field_type, + expected_clock_class); + if (ret) { + BT_LOGW("Event class's context field type " + "is not recursively mapped to the " + "expected clock class: " + "event-class-addr=%p, " + "event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", " + "ft-addr=%p", + event_class, + bt_ctf_event_class_common_get_name(event_class), + event_class->id, + event_class->context_field_type); + goto end; + } + + ret = bt_ctf_field_type_common_validate_single_clock_class( + event_class->payload_field_type, + expected_clock_class); + if (ret) { + BT_LOGW("Event class's payload field type " + "is not recursively mapped to the " + "expected clock class: " + "event-class-addr=%p, " + "event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", " + "ft-addr=%p", + event_class, + bt_ctf_event_class_common_get_name(event_class), + event_class->id, + event_class->payload_field_type); + goto end; + } + +end: + return ret; +} + +static +void bt_ctf_event_class_destroy(struct bt_ctf_object *obj) +{ + bt_ctf_event_class_common_finalize(obj); + g_free(obj); +} + +struct bt_ctf_event_class *bt_ctf_event_class_create(const char *name) +{ + struct bt_ctf_event_class *ctf_event_class = NULL; + int ret; + + if (!name) { + BT_LOGW_STR("Invalid parameter: name is NULL."); + goto error; + } + + BT_LOGD("Creating event class object: name=\"%s\"", + name); + ctf_event_class = g_new0(struct bt_ctf_event_class, 1); + if (!ctf_event_class) { + BT_LOGE_STR("Failed to allocate one event class."); + goto error; + } + + ret = bt_ctf_event_class_common_initialize(BT_CTF_TO_COMMON(ctf_event_class), + name, bt_ctf_event_class_destroy, + (bt_ctf_field_type_structure_create_func) + bt_ctf_field_type_structure_create); + if (ret) { + goto error; + } + + goto end; + +error: + bt_ctf_object_put_ref(ctf_event_class); + +end: + return ctf_event_class; +} + +const char *bt_ctf_event_class_get_name(struct bt_ctf_event_class *event_class) +{ + return bt_ctf_event_class_common_get_name(BT_CTF_TO_COMMON(event_class)); +} + +int64_t bt_ctf_event_class_get_id(struct bt_ctf_event_class *event_class) +{ + return bt_ctf_event_class_common_get_id(BT_CTF_TO_COMMON(event_class)); +} + +int bt_ctf_event_class_set_id(struct bt_ctf_event_class *event_class, + uint64_t id) +{ + return bt_ctf_event_class_common_set_id(BT_CTF_TO_COMMON(event_class), id); +} + +enum bt_ctf_event_class_log_level bt_ctf_event_class_get_log_level( + struct bt_ctf_event_class *event_class) +{ + return bt_ctf_event_class_common_get_log_level(BT_CTF_TO_COMMON(event_class)); +} + +int bt_ctf_event_class_set_log_level(struct bt_ctf_event_class *event_class, + enum bt_ctf_event_class_log_level log_level) +{ + return bt_ctf_event_class_common_set_log_level(BT_CTF_TO_COMMON(event_class), + log_level); +} + +const char *bt_ctf_event_class_get_emf_uri( + struct bt_ctf_event_class *event_class) +{ + return bt_ctf_event_class_common_get_emf_uri(BT_CTF_TO_COMMON(event_class)); +} + +int bt_ctf_event_class_set_emf_uri(struct bt_ctf_event_class *event_class, + const char *emf_uri) +{ + return bt_ctf_event_class_common_set_emf_uri(BT_CTF_TO_COMMON(event_class), + emf_uri); +} + +struct bt_ctf_stream_class *bt_ctf_event_class_get_stream_class( + struct bt_ctf_event_class *event_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_stream_class( + BT_CTF_TO_COMMON(event_class))); +} + +struct bt_ctf_field_type *bt_ctf_event_class_get_payload_field_type( + struct bt_ctf_event_class *event_class) +{ + return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_payload_field_type( + BT_CTF_TO_COMMON(event_class))); +} + +int bt_ctf_event_class_set_payload_field_type( + struct bt_ctf_event_class *event_class, + struct bt_ctf_field_type *field_type) +{ + return bt_ctf_event_class_common_set_payload_field_type( + BT_CTF_TO_COMMON(event_class), (void *) field_type); +} + +struct bt_ctf_field_type *bt_ctf_event_class_get_context_field_type( + struct bt_ctf_event_class *event_class) +{ + return bt_ctf_object_get_ref(bt_ctf_event_class_common_borrow_context_field_type( + BT_CTF_TO_COMMON(event_class))); +} + +int bt_ctf_event_class_set_context_field_type( + struct bt_ctf_event_class *event_class, + struct bt_ctf_field_type *field_type) +{ + return bt_ctf_event_class_common_set_context_field_type( + BT_CTF_TO_COMMON(event_class), (void *) field_type); +} + +int bt_ctf_event_class_add_field(struct bt_ctf_event_class *event_class, + struct bt_ctf_field_type *type, + const char *name) +{ + int ret = 0; + + if (!event_class || !type) { + BT_LOGW("Invalid parameter: event class or field type is NULL: " + "event-class-addr=%p, field-type-addr=%p", + event_class, type); + ret = -1; + goto end; + } + + if (!bt_ctf_identifier_is_valid(name)) { + BT_LOGW("Invalid parameter: event class's payload field type's field name is not a valid CTF identifier: " + "addr=%p, name=\"%s\", id=%" PRId64 ", field-name=\"%s\"", + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class), + name); + ret = -1; + goto end; + } + + if (event_class->common.frozen) { + BT_LOGW("Invalid parameter: event class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + ret = -1; + goto end; + } + + if (!event_class->common.payload_field_type) { + BT_LOGW("Event class has no payload field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + ret = -1; + goto end; + } + + BT_ASSERT(bt_ctf_field_type_common_get_type_id( + event_class->common.payload_field_type) == + BT_CTF_FIELD_TYPE_ID_STRUCT); + ret = bt_ctf_field_type_structure_add_field( + (void *) event_class->common.payload_field_type, + (void *) type, name); + BT_LOGV("Added field to event class's payload field type: " + "event-class-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", field-name=\"%s\", ft-addr=%p", + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class), name, type); +end: + return ret; +} + +int64_t bt_ctf_event_class_get_payload_type_field_count( + struct bt_ctf_event_class *event_class) +{ + int64_t ret; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = (int64_t) -1; + goto end; + } + + if (!event_class->common.payload_field_type) { + BT_LOGV("Event class has no payload field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + ret = (int64_t) -1; + goto end; + } + + BT_ASSERT(bt_ctf_field_type_common_get_type_id( + event_class->common.payload_field_type) == + BT_CTF_FIELD_TYPE_ID_STRUCT); + ret = bt_ctf_field_type_common_structure_get_field_count( + event_class->common.payload_field_type); +end: + return ret; +} + +int bt_ctf_event_class_get_payload_type_field_by_index( + struct bt_ctf_event_class *event_class, + const char **field_name, struct bt_ctf_field_type **field_type, + uint64_t index) +{ + int ret; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (!event_class->common.payload_field_type) { + BT_LOGV("Event class has no payload field type: " + "addr=%p, name=\"%s\", id=%" PRId64 ", index=%" PRIu64, + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class), index); + ret = -1; + goto end; + } + + BT_ASSERT(bt_ctf_field_type_common_get_type_id( + event_class->common.payload_field_type) == + BT_CTF_FIELD_TYPE_ID_STRUCT); + ret = bt_ctf_field_type_structure_get_field_by_index( + (void *) event_class->common.payload_field_type, + field_name, (void *) field_type, index); + +end: + return ret; +} + +struct bt_ctf_field_type * +bt_ctf_event_class_get_payload_type_field_type_by_name( + struct bt_ctf_event_class *event_class, const char *name) +{ + GQuark name_quark; + struct bt_ctf_field_type *field_type = NULL; + + if (!event_class || !name) { + BT_LOGW("Invalid parameter: event class or name is NULL: " + "event-class-addr=%p, name-addr=%p", + event_class, name); + goto end; + } + + if (!event_class->common.payload_field_type) { + BT_LOGV("Event class has no payload field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + goto end; + } + + BT_ASSERT(bt_ctf_field_type_common_get_type_id( + event_class->common.payload_field_type) == + BT_CTF_FIELD_TYPE_ID_STRUCT); + name_quark = g_quark_try_string(name); + if (!name_quark) { + BT_LOGE("Cannot get GQuark: string=\"%s\"", name); + goto end; + } + + /* + * No need to increment field_type's reference count since getting it + * from the structure already does. + */ + field_type = (void *) + bt_ctf_field_type_structure_get_field_type_by_name( + (void *) event_class->common.payload_field_type, name); + +end: + return field_type; +} + +BT_HIDDEN +int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class, + struct metadata_context *context) +{ + int ret = 0; + struct bt_ctf_value *attr_value = NULL; + + BT_ASSERT(event_class); + BT_ASSERT(context); + BT_LOGD("Serializing event class's metadata: " + "event-class-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", metadata-context-addr=%p", + event_class, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class), context); + context->current_indentation_level = 1; + g_string_assign(context->field_name, ""); + g_string_append(context->string, "event {\n"); + + /* Serialize attributes */ + g_string_append_printf(context->string, "\tname = \"%s\";\n", + event_class->common.name->str); + BT_ASSERT(event_class->common.id >= 0); + g_string_append_printf(context->string, "\tid = %" PRId64 ";\n", + event_class->common.id); + g_string_append_printf(context->string, "\tstream_id = %" PRId64 ";\n", + bt_ctf_stream_class_common_get_id( + bt_ctf_event_class_common_borrow_stream_class( + BT_CTF_TO_COMMON(event_class)))); + + if (event_class->common.log_level != + BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED) { + g_string_append_printf(context->string, "\tloglevel = %d;\n", + (int) event_class->common.log_level); + } + + if (event_class->common.emf_uri->len > 0) { + g_string_append_printf(context->string, "\tmodel.emf.uri = \"%s\";\n", + event_class->common.emf_uri->str); + } + + /* Serialize context field type */ + if (event_class->common.context_field_type) { + g_string_append(context->string, "\tcontext := "); + BT_LOGD_STR("Serializing event class's context field type metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) event_class->common.context_field_type, + context); + if (ret) { + BT_LOGW("Cannot serialize event class's context field type's metadata: " + "ret=%d", ret); + goto end; + } + g_string_append(context->string, ";\n"); + } + + /* Serialize payload field type */ + if (event_class->common.payload_field_type) { + g_string_append(context->string, "\tfields := "); + BT_LOGD_STR("Serializing event class's payload field type metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) event_class->common.payload_field_type, + context); + if (ret) { + BT_LOGW("Cannot serialize event class's payload field type's metadata: " + "ret=%d", ret); + goto end; + } + g_string_append(context->string, ";\n"); + } + + g_string_append(context->string, "};\n\n"); + +end: + context->current_indentation_level = 0; + BT_CTF_OBJECT_PUT_REF_AND_RESET(attr_value); + return ret; +} + +struct bt_ctf_field_type *bt_ctf_event_class_get_field_by_name( + struct bt_ctf_event_class *event_class, const char *name) +{ + GQuark name_quark; + struct bt_ctf_field_type *field_type = NULL; + + if (!event_class || !name) { + BT_LOGW("Invalid parameter: event class or name is NULL: " + "event-class-addr=%p, name-addr=%p", + event_class, name); + goto end; + } + + if (!event_class->common.payload_field_type) { + BT_LOGV("Event class has no payload field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, + bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + goto end; + } + + BT_ASSERT(event_class->common.payload_field_type->id == + BT_CTF_FIELD_TYPE_ID_STRUCT); + name_quark = g_quark_try_string(name); + if (!name_quark) { + BT_LOGE("Cannot get GQuark: string=\"%s\"", name); + goto end; + } + + /* + * No need to increment field_type's reference count since getting it + * from the structure already does. + */ + field_type = bt_ctf_object_get_ref( + bt_ctf_field_type_common_structure_borrow_field_type_by_name( + event_class->common.payload_field_type, name)); + +end: + return field_type; +} diff --git a/src/ctf-writer/event-class.h b/src/ctf-writer/event-class.h new file mode 100644 index 00000000..c5ebb85f --- /dev/null +++ b/src/ctf-writer/event-class.h @@ -0,0 +1,396 @@ +#ifndef BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H +#define BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/assert.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include +#include + +#include "object.h" +#include "trace.h" +#include "values.h" + +struct bt_ctf_event_class_common { + struct bt_ctf_object base; + struct bt_ctf_field_type_common *context_field_type; + struct bt_ctf_field_type_common *payload_field_type; + int frozen; + + /* + * This flag indicates if the event class is valid. A valid + * event class is _always_ frozen. However, an event class + * may be frozen, but not valid yet. This is okay, as long as + * no events are created out of this event class. + */ + int valid; + + /* Attributes */ + GString *name; + int64_t id; + int log_level; + GString *emf_uri; +}; + +BT_HIDDEN +void bt_ctf_event_class_common_freeze(struct bt_ctf_event_class_common *event_class); + +BT_HIDDEN +void bt_ctf_event_class_common_set_native_byte_order( + struct bt_ctf_event_class_common *event_class, int byte_order); + +static inline +struct bt_ctf_stream_class_common *bt_ctf_event_class_common_borrow_stream_class( + struct bt_ctf_event_class_common *event_class) +{ + BT_ASSERT(event_class); + return (void *) bt_ctf_object_borrow_parent(&event_class->base); +} + +typedef struct bt_ctf_field_type_common *(*bt_ctf_field_type_structure_create_func)(); + +BT_HIDDEN +int bt_ctf_event_class_common_initialize(struct bt_ctf_event_class_common *event_class, + const char *name, bt_ctf_object_release_func release_func, + bt_ctf_field_type_structure_create_func ft_struct_create_func); + +BT_HIDDEN +void bt_ctf_event_class_common_finalize(struct bt_ctf_object *obj); + +BT_HIDDEN +int bt_ctf_event_class_common_validate_single_clock_class( + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_clock_class **expected_clock_class); + +static inline +const char *bt_ctf_event_class_common_get_name( + struct bt_ctf_event_class_common *event_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT(event_class->name); + return event_class->name->str; +} + +static inline +int64_t bt_ctf_event_class_common_get_id( + struct bt_ctf_event_class_common *event_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->id; +} + +static inline +int bt_ctf_event_class_common_set_id( + struct bt_ctf_event_class_common *event_class, uint64_t id_param) +{ + int ret = 0; + int64_t id = (int64_t) id_param; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (event_class->frozen) { + BT_LOGW("Invalid parameter: event class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, + bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + ret = -1; + goto end; + } + + if (id < 0) { + BT_LOGW("Invalid parameter: invalid event class's ID: " + "addr=%p, name=\"%s\", id=%" PRIu64, + event_class, + bt_ctf_event_class_common_get_name(event_class), + id_param); + ret = -1; + goto end; + } + + event_class->id = id; + BT_LOGV("Set event class's ID: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), id); + +end: + return ret; +} + +static inline +int bt_ctf_event_class_common_get_log_level( + struct bt_ctf_event_class_common *event_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->log_level; +} + +static inline +int bt_ctf_event_class_common_set_log_level( + struct bt_ctf_event_class_common *event_class, int log_level) +{ + int ret = 0; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (event_class->frozen) { + BT_LOGW("Invalid parameter: event class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, + bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + ret = -1; + goto end; + } + + switch (log_level) { + case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG: + break; + default: + BT_LOGW("Invalid parameter: unknown event class log level: " + "addr=%p, name=\"%s\", id=%" PRId64 ", log-level=%d", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), log_level); + ret = -1; + goto end; + } + + event_class->log_level = log_level; + BT_LOGV("Set event class's log level: " + "addr=%p, name=\"%s\", id=%" PRId64 ", log-level=%s", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), + bt_ctf_event_class_log_level_string(log_level)); + +end: + return ret; +} + +static inline +const char *bt_ctf_event_class_common_get_emf_uri( + struct bt_ctf_event_class_common *event_class) +{ + const char *emf_uri = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + + if (event_class->emf_uri->len > 0) { + emf_uri = event_class->emf_uri->str; + } + + return emf_uri; +} + +static inline +int bt_ctf_event_class_common_set_emf_uri( + struct bt_ctf_event_class_common *event_class, + const char *emf_uri) +{ + int ret = 0; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (emf_uri && strlen(emf_uri) == 0) { + BT_LOGW_STR("Invalid parameter: EMF URI is empty."); + ret = -1; + goto end; + } + + if (event_class->frozen) { + BT_LOGW("Invalid parameter: event class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + ret = -1; + goto end; + } + + if (emf_uri) { + g_string_assign(event_class->emf_uri, emf_uri); + BT_LOGV("Set event class's EMF URI: " + "addr=%p, name=\"%s\", id=%" PRId64 ", emf-uri=\"%s\"", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), emf_uri); + } else { + g_string_assign(event_class->emf_uri, ""); + BT_LOGV("Reset event class's EMF URI: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + } + +end: + return ret; +} + +static inline +struct bt_ctf_field_type_common *bt_ctf_event_class_common_borrow_context_field_type( + struct bt_ctf_event_class_common *event_class) +{ + struct bt_ctf_field_type_common *context_ft = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + + if (!event_class->context_field_type) { + BT_LOGV("Event class has no context field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + goto end; + } + + context_ft = event_class->context_field_type; + +end: + return context_ft; +} + +static inline +int bt_ctf_event_class_common_set_context_field_type( + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_field_type_common *context_ft) +{ + int ret = 0; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (event_class->frozen) { + BT_LOGW("Invalid parameter: event class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + ret = -1; + goto end; + } + + if (context_ft && bt_ctf_field_type_common_get_type_id(context_ft) != + BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid parameter: event class's context field type must be a structure: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "context-ft-id=%s", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), + bt_ctf_field_type_id_string( + bt_ctf_field_type_common_get_type_id(context_ft))); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(event_class->context_field_type); + event_class->context_field_type = context_ft; + bt_ctf_object_get_ref(event_class->context_field_type); + BT_LOGV("Set event class's context field type: " + "event-class-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", context-ft-addr=%p", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), context_ft); + +end: + return ret; +} + +static inline +struct bt_ctf_field_type_common *bt_ctf_event_class_common_borrow_payload_field_type( + struct bt_ctf_event_class_common *event_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->payload_field_type; +} + +static inline +int bt_ctf_event_class_common_set_payload_field_type( + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_field_type_common *payload_ft) +{ + int ret = 0; + + if (!event_class) { + BT_LOGW_STR("Invalid parameter: event class is NULL."); + ret = -1; + goto end; + } + + if (payload_ft && bt_ctf_field_type_common_get_type_id(payload_ft) != + BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid parameter: event class's payload field type must be a structure: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "payload-ft-addr=%p, payload-ft-id=%s", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), payload_ft, + bt_ctf_field_type_id_string( + bt_ctf_field_type_common_get_type_id(payload_ft))); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(event_class->payload_field_type); + event_class->payload_field_type = payload_ft; + bt_ctf_object_get_ref(event_class->payload_field_type); + BT_LOGV("Set event class's payload field type: " + "event-class-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", payload-ft-addr=%p", + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class), payload_ft); +end: + return ret; +} + +#endif /* BABELTRACE_CTF_WRITER_EVENT_CLASS_INTERNAL_H */ diff --git a/src/ctf-writer/event.c b/src/ctf-writer/event.c new file mode 100644 index 00000000..e1bc85e7 --- /dev/null +++ b/src/ctf-writer/event.c @@ -0,0 +1,903 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-EVENT" +#include "logging.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common/assert.h" +#include "compat/compiler.h" + +#include "assert-pre.h" +#include "attributes.h" +#include "clock-class.h" +#include "clock.h" +#include "event-class.h" +#include "event.h" +#include "fields.h" +#include "field-types.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" +#include "validation.h" + +static +int bt_ctf_event_common_validate_types_for_create( + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_validation_output *validation_output, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) +{ + int ret; + enum bt_ctf_validation_flag validation_flags = + BT_CTF_VALIDATION_FLAG_STREAM | + BT_CTF_VALIDATION_FLAG_EVENT; + struct bt_ctf_trace_common *trace = NULL; + struct bt_ctf_stream_class_common *stream_class = NULL; + struct bt_ctf_field_type_common *packet_header_type = NULL; + struct bt_ctf_field_type_common *packet_context_type = NULL; + struct bt_ctf_field_type_common *event_header_type = NULL; + struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; + struct bt_ctf_field_type_common *event_context_type = NULL; + struct bt_ctf_field_type_common *event_payload_type = NULL; + int trace_valid = 0; + struct bt_ctf_private_value *environment = NULL; + + stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); + BT_ASSERT(stream_class); + trace = bt_ctf_stream_class_common_borrow_trace(stream_class); + if (trace) { + BT_LOGD_STR("Event class is part of a trace."); + packet_header_type = + bt_ctf_trace_common_borrow_packet_header_field_type(trace); + trace_valid = trace->valid; + BT_ASSERT(trace_valid); + environment = trace->environment; + } + + packet_context_type = + bt_ctf_stream_class_common_borrow_packet_context_field_type( + stream_class); + event_header_type = + bt_ctf_stream_class_common_borrow_event_header_field_type( + stream_class); + stream_event_ctx_type = + bt_ctf_stream_class_common_borrow_event_context_field_type( + stream_class); + event_context_type = + bt_ctf_event_class_common_borrow_context_field_type(event_class); + event_payload_type = + bt_ctf_event_class_common_borrow_payload_field_type(event_class); + ret = bt_ctf_validate_class_types(environment, packet_header_type, + packet_context_type, event_header_type, stream_event_ctx_type, + event_context_type, event_payload_type, trace_valid, + stream_class->valid, event_class->valid, + validation_output, validation_flags, copy_field_type_func); + if (ret) { + /* + * This means something went wrong during the validation + * process, not that the objects are invalid. + */ + BT_LOGE("Failed to validate event and parents: ret=%d", ret); + goto error; + } + + if ((validation_output->valid_flags & validation_flags) != + validation_flags) { + /* Invalid trace/stream class/event class */ + BT_LOGW("Invalid trace, stream class, or event class: " + "valid-flags=0x%x", validation_output->valid_flags); + goto error; + } + + goto end; + +error: + bt_ctf_validation_output_put_types(validation_output); + ret = -1; + +end: + return ret; +} + +static +int bt_ctf_event_common_create_fields( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_validation_output *validation_output, + create_field_func create_field_func, + release_field_func release_field_func, + create_header_field_func create_header_field_func, + release_header_field_func release_header_field_func, + struct bt_ctf_field_wrapper **header_field, + struct bt_ctf_field_common **stream_event_context_field, + struct bt_ctf_field_common **context_field, + struct bt_ctf_field_common **payload_field) +{ + int ret = 0; + + if (validation_output->event_header_type) { + BT_LOGD("Creating initial event header field: ft-addr=%p", + validation_output->event_header_type); + *header_field = + create_header_field_func(stream_class, + validation_output->event_header_type); + if (!*header_field) { + BT_LOGE_STR("Cannot create initial event header field object."); + goto error; + } + } + + if (validation_output->stream_event_ctx_type) { + BT_LOGD("Creating initial stream event context field: ft-addr=%p", + validation_output->stream_event_ctx_type); + *stream_event_context_field = create_field_func( + validation_output->stream_event_ctx_type); + if (!*stream_event_context_field) { + BT_LOGE_STR("Cannot create initial stream event context field object."); + goto error; + } + } + + if (validation_output->event_context_type) { + BT_LOGD("Creating initial event context field: ft-addr=%p", + validation_output->event_context_type); + *context_field = create_field_func( + validation_output->event_context_type); + if (!*context_field) { + BT_LOGE_STR("Cannot create initial event context field object."); + goto error; + } + } + + if (validation_output->event_payload_type) { + BT_LOGD("Creating initial event payload field: ft-addr=%p", + validation_output->event_payload_type); + *payload_field = create_field_func( + validation_output->event_payload_type); + if (!*payload_field) { + BT_LOGE_STR("Cannot create initial event payload field object."); + goto error; + } + } + + goto end; + +error: + if (*header_field) { + release_header_field_func(*header_field, stream_class); + } + + if (*stream_event_context_field) { + release_field_func(*stream_event_context_field); + } + + if (*context_field) { + release_field_func(*context_field); + } + + if (*payload_field) { + release_field_func(*payload_field); + } + + ret = -1; + +end: + return ret; +} + +BT_HIDDEN +int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event) +{ + int ret = 0; + struct bt_ctf_stream_class_common *stream_class; + + BT_ASSERT(event); + if (event->header_field) { + ret = bt_ctf_field_common_validate_recursive( + event->header_field->field); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid event's header field: " + "event-addr=%p, field-addr=%p", + event, event->header_field->field); + goto end; + } + } + + stream_class = bt_ctf_event_class_common_borrow_stream_class(event->class); + + /* + * We should not have been able to create the event without associating + * the event class to a stream class. + */ + BT_ASSERT(stream_class); + + if (stream_class->event_context_field_type) { + ret = bt_ctf_field_common_validate_recursive( + event->stream_event_context_field); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid event's stream event context field: " + "event-addr=%p, field-addr=%p", + event, event->stream_event_context_field); + goto end; + } + } + + if (event->class->context_field_type) { + ret = bt_ctf_field_common_validate_recursive(event->context_field); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid event's payload field: " + "event-addr=%p, field-addr=%p", + event, event->context_field); + goto end; + } + } + + ret = bt_ctf_field_common_validate_recursive(event->payload_field); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid event's payload field: " + "event-addr=%p, field-addr=%p", + event, event->payload_field); + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event, + bool is_frozen) +{ + BT_ASSERT(event); + BT_LOGD("Freezing event: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + + if (event->header_field) { + BT_LOGD_STR("Freezing event's header field."); + bt_ctf_field_common_set_is_frozen_recursive( + event->header_field->field, is_frozen); + } + + if (event->stream_event_context_field) { + BT_LOGD_STR("Freezing event's stream event context field."); + bt_ctf_field_common_set_is_frozen_recursive( + event->stream_event_context_field, is_frozen); + } + + if (event->context_field) { + BT_LOGD_STR("Freezing event's context field."); + bt_ctf_field_common_set_is_frozen_recursive(event->context_field, + is_frozen); + } + + if (event->payload_field) { + BT_LOGD_STR("Freezing event's payload field."); + bt_ctf_field_common_set_is_frozen_recursive(event->payload_field, + is_frozen); + } + + event->frozen = is_frozen; +} + +BT_HIDDEN +int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event, + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_clock_class *init_expected_clock_class, + bool is_shared_with_parent, bt_ctf_object_release_func release_func, + bt_ctf_validation_flag_copy_field_type_func field_type_copy_func, + bool must_be_in_trace, + int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_field_type, + struct bt_ctf_field_type_common *event_header_field_type), + create_field_func create_field_func, + release_field_func release_field_func, + create_header_field_func create_header_field_func, + release_header_field_func release_header_field_func) +{ + int ret; + struct bt_ctf_trace_common *trace = NULL; + struct bt_ctf_stream_class_common *stream_class = NULL; + struct bt_ctf_field_wrapper *event_header = NULL; + struct bt_ctf_field_common *stream_event_context = NULL; + struct bt_ctf_field_common *event_context = NULL; + struct bt_ctf_field_common *event_payload = NULL; + struct bt_ctf_validation_output validation_output = { 0 }; + struct bt_ctf_clock_class *expected_clock_class = + init_expected_clock_class ? bt_ctf_object_get_ref(init_expected_clock_class) : + NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_LOGD("Initializing common event object: event-class-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + + stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); + BT_CTF_ASSERT_PRE(stream_class, + "Event class is not part of a stream class: event-class-addr=%p", + event_class); + + /* The event class was frozen when added to its stream class */ + BT_ASSERT(event_class->frozen); + trace = bt_ctf_stream_class_common_borrow_trace(stream_class); + + if (must_be_in_trace) { + BT_CTF_ASSERT_PRE(trace, + "Event class's stream class is not part of a trace: " + "ec-addr=%p, sc-addr=%p", event_class, stream_class); + } + + /* + * This must be called before anything that can fail because on + * failure, the caller releases the reference to `event` to + * destroy it. + */ + if (is_shared_with_parent) { + bt_ctf_object_init_shared_with_parent(&event->base, release_func); + } else { + bt_ctf_object_init_unique(&event->base); + } + + if (!stream_class->frozen) { + /* + * Because this function freezes the stream class, + * validate that this stream class contains at most a + * single clock class so that we set its expected clock + * class for future checks. + */ + ret = bt_ctf_stream_class_common_validate_single_clock_class( + stream_class, &expected_clock_class); + if (ret) { + BT_LOGW("Event class's stream class or one of its event " + "classes contains a field type which is not " + "recursively mapped to the expected " + "clock class: " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "stream-class-name=\"%s\", " + "expected-clock-class-addr=%p, " + "expected-clock-class-name=\"%s\"", + stream_class, + bt_ctf_stream_class_common_get_id(stream_class), + bt_ctf_stream_class_common_get_name(stream_class), + expected_clock_class, + expected_clock_class ? + bt_ctf_clock_class_get_name(expected_clock_class) : + NULL); + goto error; + } + } + + /* Validate the trace, the stream class, and the event class */ + ret = bt_ctf_event_common_validate_types_for_create( + event_class, &validation_output, field_type_copy_func); + if (ret) { + /* bt_ctf_event_common_validate_types_for_create() logs errors */ + goto error; + } + + if (map_clock_classes_func) { + /* + * Safe to automatically map selected fields to the + * stream's clock's class here because the stream class + * is about to be frozen. + */ + if (map_clock_classes_func(stream_class, + validation_output.packet_context_type, + validation_output.event_header_type)) { + BT_LOGW_STR("Cannot automatically map selected stream class's " + "field types to stream class's clock's class."); + goto error; + } + } + + /* + * event does not share a common ancestor with the event class; it has + * to guarantee its existence by holding a reference. This reference + * shall be released once the event is associated to a stream since, + * from that point, the event and its class will share the same + * lifetime. + */ + event->class = bt_ctf_object_get_ref(event_class); + + ret = bt_ctf_event_common_create_fields(stream_class, + &validation_output, + create_field_func, release_field_func, + create_header_field_func, release_header_field_func, + &event_header, &stream_event_context, &event_context, + &event_payload); + if (ret) { + /* bt_ctf_event_common_create_fields() logs errors */ + goto error; + } + + /* + * At this point all the fields are created, potentially from + * validated copies of field types, so that the field types and + * fields can be replaced in the trace, stream class, + * event class, and created event. + */ + bt_ctf_validation_replace_types(trace, stream_class, event_class, + &validation_output, + BT_CTF_VALIDATION_FLAG_STREAM | BT_CTF_VALIDATION_FLAG_EVENT); + event->header_field = event_header; + event_header = NULL; + event->stream_event_context_field = stream_event_context; + stream_event_context = NULL; + event->context_field = event_context; + event_context = NULL; + event->payload_field = event_payload; + event_payload = NULL; + + /* + * Put what was not moved in bt_ctf_validation_replace_types(). + */ + bt_ctf_validation_output_put_types(&validation_output); + + /* + * Freeze the stream class since the event header must not be changed + * anymore. + */ + bt_ctf_stream_class_common_freeze(stream_class); + + /* + * It is safe to set the stream class's unique clock class + * now because the stream class is frozen. + */ + if (expected_clock_class) { + BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); + } + + /* + * Mark stream class, and event class as valid since + * they're all frozen now. + */ + stream_class->valid = 1; + event_class->valid = 1; + + /* Put stuff we borrowed from the event class */ + BT_LOGD("Initialized event object: addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + goto end; + +error: + bt_ctf_validation_output_put_types(&validation_output); + bt_ctf_object_put_ref(expected_clock_class); + + if (event_header) { + release_header_field_func(event_header, stream_class); + } + + if (stream_event_context) { + release_field_func(stream_event_context); + } + + if (event_context) { + release_field_func(event_context); + } + + if (event_payload) { + release_field_func(event_payload); + } + + ret = -1; + +end: + return ret; +} + +int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type) +{ + int ret = bt_ctf_stream_class_map_clock_class( + BT_CTF_FROM_COMMON(stream_class), + BT_CTF_FROM_COMMON(packet_context_type), + BT_CTF_FROM_COMMON(event_header_type)); + + if (ret) { + BT_LOGW_STR("Cannot automatically map selected stream class's field types to stream class's clock's class."); + } + + return ret; +} + +static +void destroy_event_header_field(struct bt_ctf_field_wrapper *field_wrapper) +{ + BT_ASSERT(field_wrapper); + bt_ctf_object_put_ref(field_wrapper->field); + bt_ctf_field_wrapper_destroy(field_wrapper); +} + +static +struct bt_ctf_field_wrapper *create_event_header_field( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_wrapper *field_wrapper = NULL; + struct bt_ctf_field *field = bt_ctf_field_create((void *) ft); + + if (!field) { + goto error; + } + + field_wrapper = bt_ctf_field_wrapper_new(NULL); + if (!field_wrapper) { + goto error; + } + + field_wrapper->field = (void *) field; + field = NULL; + goto end; + +error: + bt_ctf_object_put_ref(field); + + if (field_wrapper) { + destroy_event_header_field(field_wrapper); + field_wrapper = NULL; + } + +end: + return field_wrapper; +} + +static +void release_event_header_field(struct bt_ctf_field_wrapper *field_wrapper, + struct bt_ctf_event_common *event_common) +{ + BT_ASSERT(field_wrapper); + BT_CTF_OBJECT_PUT_REF_AND_RESET(field_wrapper->field); + bt_ctf_field_wrapper_destroy(field_wrapper); +} + +static +void bt_ctf_event_destroy(struct bt_ctf_object *obj) +{ + bt_ctf_event_common_finalize(obj, (void *) bt_ctf_object_put_ref, + (void *) release_event_header_field); + g_free(obj); +} + +struct bt_ctf_event *bt_ctf_event_create(struct bt_ctf_event_class *event_class) +{ + int ret; + struct bt_ctf_event *event = NULL; + struct bt_ctf_clock_class *expected_clock_class = NULL; + + event = g_new0(struct bt_ctf_event, 1); + if (!event) { + BT_LOGE_STR("Failed to allocate one CTF writer event."); + goto error; + } + + if (event_class) { + struct bt_ctf_stream_class *stream_class = + BT_CTF_FROM_COMMON(bt_ctf_event_class_common_borrow_stream_class( + BT_CTF_TO_COMMON(event_class))); + + if (stream_class && stream_class->clock) { + expected_clock_class = stream_class->clock->clock_class; + } + } + + ret = bt_ctf_event_common_initialize(BT_CTF_TO_COMMON(event), + BT_CTF_TO_COMMON(event_class), expected_clock_class, + true, bt_ctf_event_destroy, + (bt_ctf_validation_flag_copy_field_type_func) + bt_ctf_field_type_copy, + false, map_clock_classes_func, + (create_field_func) bt_ctf_field_create, + (release_field_func) bt_ctf_object_put_ref, + (create_header_field_func) create_event_header_field, + (release_header_field_func) destroy_event_header_field); + if (ret) { + /* bt_ctf_event_common_initialize() logs errors */ + goto error; + } + + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(event); + +end: + return event; +} + +struct bt_ctf_event_class *bt_ctf_event_get_class(struct bt_ctf_event *event) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))); +} + +BT_HIDDEN +struct bt_ctf_stream *bt_ctf_event_borrow_stream(struct bt_ctf_event *event) +{ + BT_ASSERT(event); + return (struct bt_ctf_stream *) + bt_ctf_object_borrow_parent(&BT_CTF_TO_COMMON(event)->base); +} + +struct bt_ctf_stream *bt_ctf_event_get_stream(struct bt_ctf_event *event) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + return bt_ctf_object_get_ref(bt_ctf_event_borrow_stream(event)); +} + +int bt_ctf_event_set_payload(struct bt_ctf_event *event, const char *name, + struct bt_ctf_field *field) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + BT_CTF_ASSERT_PRE_NON_NULL(field, "Payload field"); + BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); + return bt_ctf_field_structure_set_field_by_name( + (void *) event->common.payload_field, name, field); +} + +struct bt_ctf_field *bt_ctf_event_get_payload(struct bt_ctf_event *event, + const char *name) +{ + struct bt_ctf_field *field = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + + if (name) { + field = bt_ctf_field_structure_get_field_by_name( + BT_CTF_FROM_COMMON(event->common.payload_field), name); + } else { + field = BT_CTF_FROM_COMMON(event->common.payload_field); + bt_ctf_object_get_ref(field); + } + + return field; +} + +struct bt_ctf_field *bt_ctf_event_get_payload_field( + struct bt_ctf_event *event) +{ + return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_payload(BT_CTF_TO_COMMON(event))); +} + +struct bt_ctf_field *bt_ctf_event_get_header(struct bt_ctf_event *event) +{ + return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_header(BT_CTF_TO_COMMON(event))); +} + +struct bt_ctf_field *bt_ctf_event_get_context(struct bt_ctf_event *event) +{ + return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_context(BT_CTF_TO_COMMON(event))); +} + +struct bt_ctf_field *bt_ctf_event_get_stream_event_context( + struct bt_ctf_event *event) +{ + return bt_ctf_object_get_ref(bt_ctf_event_common_borrow_stream_event_context( + BT_CTF_TO_COMMON(event))); +} + +BT_HIDDEN +int bt_ctf_event_serialize(struct bt_ctf_event *event, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int ret = 0; + + BT_ASSERT(event); + BT_ASSERT(ctfser); + + BT_LOGV_STR("Serializing event's context field."); + if (event->common.context_field) { + ret = bt_ctf_field_serialize_recursive( + (void *) event->common.context_field, ctfser, + native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize event's context field: " + "event-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64, + event, + bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class)); + goto end; + } + } + + BT_LOGV_STR("Serializing event's payload field."); + if (event->common.payload_field) { + ret = bt_ctf_field_serialize_recursive( + (void *) event->common.payload_field, ctfser, + native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize event's payload field: " + "event-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64, + event, + bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class)); + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +void _bt_ctf_event_freeze(struct bt_ctf_event *event) +{ + _bt_ctf_event_common_set_is_frozen(BT_CTF_TO_COMMON(event), true); +} + +int bt_ctf_event_set_header(struct bt_ctf_event *event, + struct bt_ctf_field *header) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); + + /* + * Ensure the provided header's type matches the one registered to the + * stream class. + */ + if (header) { + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( + ((struct bt_ctf_field_common *) header)->type, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type) == 0, + "Header field's type is different from the " + "expected field type: event-addr=%p, ft-addr=%p, " + "expected-ft-addr=%p", + event, ((struct bt_ctf_field_common *) header)->type, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type); + } else { + BT_CTF_ASSERT_PRE(!bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type, + "Setting no event header field, " + "but event header field type is not NULL: " + "event-addr=%p, header-ft-addr=%p", + event, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_header_field_type); + } + + bt_ctf_object_put_ref(event->common.header_field->field); + event->common.header_field->field = bt_ctf_object_get_ref(header); + BT_LOGV("Set event's header field: event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "header-field-addr=%p", + event, bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class), header); + return 0; +} + +int bt_ctf_event_common_set_payload(struct bt_ctf_event *event, + struct bt_ctf_field *payload) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); + + if (payload) { + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( + ((struct bt_ctf_field_common *) payload)->type, + event->common.class->payload_field_type) == 0, + "Payload field's type is different from the " + "expected field type: event-addr=%p, ft-addr=%p, " + "expected-ft-addr=%p", + event, + ((struct bt_ctf_field_common *) payload)->type, + event->common.class->payload_field_type); + } else { + BT_CTF_ASSERT_PRE(!event->common.class->payload_field_type, + "Setting no event payload field, " + "but event payload field type is not NULL: " + "event-addr=%p, payload-ft-addr=%p", + event, event->common.class->payload_field_type); + } + + bt_ctf_object_put_ref(event->common.payload_field); + event->common.payload_field = bt_ctf_object_get_ref(payload); + BT_LOGV("Set event's payload field: event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "payload-field-addr=%p", + event, bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class), payload); + return 0; +} + +int bt_ctf_event_set_context(struct bt_ctf_event *event, + struct bt_ctf_field *context) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); + + if (context) { + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( + ((struct bt_ctf_field_common *) context)->type, + event->common.class->context_field_type) == 0, + "Context field's type is different from the " + "expected field type: event-addr=%p, ft-addr=%p, " + "expected-ft-addr=%p", + event, ((struct bt_ctf_field_common *) context)->type, + event->common.class->context_field_type); + } else { + BT_CTF_ASSERT_PRE(!event->common.class->context_field_type, + "Setting no event context field, " + "but event context field type is not NULL: " + "event-addr=%p, context-ft-addr=%p", + event, event->common.class->context_field_type); + } + + bt_ctf_object_put_ref(event->common.context_field); + event->common.context_field = bt_ctf_object_get_ref(context); + BT_LOGV("Set event's context field: event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "context-field-addr=%p", + event, bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class), context); + return 0; +} + +int bt_ctf_event_set_stream_event_context(struct bt_ctf_event *event, + struct bt_ctf_field *stream_event_context) +{ + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(BT_CTF_TO_COMMON(event), "Event"); + + if (stream_event_context) { + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( + ((struct bt_ctf_field_common *) stream_event_context)->type, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type) == 0, + "Stream event context field's type is different from the " + "expected field type: event-addr=%p, ft-addr=%p, " + "expected-ft-addr=%p", + event, + ((struct bt_ctf_field_common *) stream_event_context)->type, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type); + } else { + BT_CTF_ASSERT_PRE(!bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type, + "Setting no stream event context field, " + "but stream event context field type is not NULL: " + "event-addr=%p, context-ft-addr=%p", + event, + bt_ctf_event_class_common_borrow_stream_class(event->common.class)->event_context_field_type); + } + + bt_ctf_object_put_ref(event->common.stream_event_context_field); + event->common.stream_event_context_field = bt_ctf_object_get_ref(stream_event_context); + BT_LOGV("Set event's stream event context field: event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "stream-event-context-field-addr=%p", + event, bt_ctf_event_class_common_get_name(event->common.class), + bt_ctf_event_class_common_get_id(event->common.class), + stream_event_context); + return 0; +} diff --git a/src/ctf-writer/event.h b/src/ctf-writer/event.h new file mode 100644 index 00000000..1a8138ab --- /dev/null +++ b/src/ctf-writer/event.h @@ -0,0 +1,270 @@ +#ifndef BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H +#define BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "common/assert.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include "ctfser/ctfser.h" + +#include "assert-pre.h" +#include "event-class.h" +#include "event.h" +#include "fields.h" +#include "field-wrapper.h" +#include "object.h" +#include "stream.h" +#include "validation.h" +#include "values.h" + +struct bt_ctf_stream_class; +struct bt_ctf_stream_pos; +struct metadata_context; + +struct bt_ctf_event_common { + struct bt_ctf_object base; + struct bt_ctf_event_class_common *class; + struct bt_ctf_field_wrapper *header_field; + struct bt_ctf_field_common *stream_event_context_field; + struct bt_ctf_field_common *context_field; + struct bt_ctf_field_common *payload_field; + int frozen; +}; + +BT_HIDDEN +int _bt_ctf_event_common_validate(struct bt_ctf_event_common *event); + +BT_HIDDEN +void _bt_ctf_event_common_set_is_frozen(struct bt_ctf_event_common *event, + bool is_frozen); + +#ifdef BT_DEV_MODE +# define bt_ctf_event_common_validate _bt_ctf_event_common_validate +# define bt_ctf_event_common_set_is_frozen _bt_ctf_event_common_set_is_frozen +#else +# define bt_ctf_event_common_validate(_event) 0 +# define bt_ctf_event_common_set_is_frozen(_event, _is_frozen) +#endif + +#define BT_CTF_ASSERT_PRE_EVENT_COMMON_HOT(_event, _name) \ + BT_CTF_ASSERT_PRE_HOT((_event), (_name), ": event-addr=%p", (_event)) + +static inline +struct bt_ctf_event_class_common *bt_ctf_event_common_borrow_class( + struct bt_ctf_event_common *event) +{ + BT_ASSERT(event); + return event->class; +} + +typedef void *(*create_field_func)(void *); +typedef void (*release_field_func)(void *); +typedef void *(*create_header_field_func)(void *, void *); +typedef void (*release_header_field_func)(void *, void *); + +BT_HIDDEN +int bt_ctf_event_common_initialize(struct bt_ctf_event_common *event, + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_clock_class *init_expected_clock_class, + bool is_shared_with_parent, bt_ctf_object_release_func release_func, + bt_ctf_validation_flag_copy_field_type_func field_type_copy_func, + bool must_be_in_trace, + int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_field_type, + struct bt_ctf_field_type_common *event_header_field_type), + create_field_func create_field_func, + release_field_func release_field_func, + create_header_field_func create_header_field_func, + release_header_field_func release_header_field_func); + +static inline +struct bt_ctf_field_common *bt_ctf_event_common_borrow_payload( + struct bt_ctf_event_common *event) +{ + struct bt_ctf_field_common *payload = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + + if (!event->payload_field) { + BT_LOGV("Event has no current payload field: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + goto end; + } + + payload = event->payload_field; + +end: + return payload; +} + +static inline +struct bt_ctf_field_common *bt_ctf_event_common_borrow_header( + struct bt_ctf_event_common *event) +{ + struct bt_ctf_field_common *header = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + + if (!event->header_field) { + BT_LOGV("Event has no current header field: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + goto end; + } + + header = event->header_field->field; + +end: + return header; +} + +static inline +struct bt_ctf_field_common *bt_ctf_event_common_borrow_context( + struct bt_ctf_event_common *event) +{ + struct bt_ctf_field_common *context = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + + if (!event->context_field) { + BT_LOGV("Event has no current context field: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + goto end; + } + + context = event->context_field; + +end: + return context; +} + +static inline +struct bt_ctf_field_common *bt_ctf_event_common_borrow_stream_event_context( + struct bt_ctf_event_common *event) +{ + struct bt_ctf_field_common *stream_event_context = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(event, "Event"); + + if (!event->stream_event_context_field) { + BT_LOGV("Event has no current stream event context field: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, bt_ctf_event_class_common_get_name(event->class), + bt_ctf_event_class_common_get_id(event->class)); + goto end; + } + + stream_event_context = event->stream_event_context_field; + +end: + return stream_event_context; +} + +static inline +void bt_ctf_event_common_finalize(struct bt_ctf_object *obj, + void (*field_release_func)(void *), + void (*header_field_release_func)(void *, struct bt_ctf_event_common *)) +{ + struct bt_ctf_event_common *event = (void *) obj; + + BT_LOGD("Destroying event: addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + event, + event->class ? bt_ctf_event_class_common_get_name(event->class) : NULL, + event->class ? bt_ctf_event_class_common_get_id(event->class) : INT64_C(-1)); + + if (event->header_field) { + BT_LOGD_STR("Releasing event's header field."); + header_field_release_func(event->header_field, event); + } + + if (event->stream_event_context_field) { + BT_LOGD_STR("Releasing event's stream event context field."); + field_release_func(event->stream_event_context_field); + } + + if (event->context_field) { + BT_LOGD_STR("Releasing event's context field."); + field_release_func(event->context_field); + } + + if (event->payload_field) { + BT_LOGD_STR("Releasing event's payload field."); + field_release_func(event->payload_field); + } + + /* + * Leave this after calling header_field_release_func() because + * this function receives the event object and could need its + * class to perform some cleanup. + */ + if (!event->base.parent) { + /* + * Event was keeping a reference to its class since it shared no + * common ancestor with it to guarantee they would both have the + * same lifetime. + */ + bt_ctf_object_put_ref(event->class); + } +} + +struct bt_ctf_event { + struct bt_ctf_event_common common; +}; + +struct bt_ctf_event_class { + struct bt_ctf_event_class_common common; +}; + +BT_HIDDEN +int bt_ctf_event_class_serialize(struct bt_ctf_event_class *event_class, + struct metadata_context *context); + +BT_HIDDEN +int bt_ctf_event_serialize(struct bt_ctf_event *event, + struct bt_ctfser *pos, + enum bt_ctf_byte_order native_byte_order); + +static inline +struct bt_ctf_stream_class *bt_ctf_event_class_borrow_stream_class( + struct bt_ctf_event_class *event_class) +{ + return BT_CTF_FROM_COMMON(bt_ctf_event_class_common_borrow_stream_class( + BT_CTF_TO_COMMON(event_class))); +} + +#endif /* BABELTRACE_CTF_WRITER_EVENT_INTERNAL_H */ diff --git a/src/ctf-writer/field-path.c b/src/ctf-writer/field-path.c new file mode 100644 index 00000000..dbd817a5 --- /dev/null +++ b/src/ctf-writer/field-path.c @@ -0,0 +1,175 @@ +/* + * field-path.c + * + * Babeltrace CTF writer - Field path + * + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-FIELD-PATH" +#include "logging.h" + +#include +#include +#include +#include + +#include + +#include "common/assert.h" + +#include "field-path.h" +#include "field-types.h" + +static +void field_path_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_path *field_path = (struct bt_ctf_field_path *) obj; + + BT_LOGD("Destroying field path: addr=%p", obj); + + if (!field_path) { + return; + } + + if (field_path->indexes) { + g_array_free(field_path->indexes, TRUE); + } + g_free(field_path); +} + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_path_create(void) +{ + struct bt_ctf_field_path *field_path = NULL; + + BT_LOGD_STR("Creating empty field path object."); + + field_path = g_new0(struct bt_ctf_field_path, 1); + if (!field_path) { + BT_LOGE_STR("Failed to allocate one field path."); + goto error; + } + + bt_ctf_object_init_shared(&field_path->base, field_path_destroy); + field_path->root = BT_CTF_SCOPE_UNKNOWN; + field_path->indexes = g_array_new(TRUE, FALSE, sizeof(int)); + if (!field_path->indexes) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + BT_LOGD("Created empty field path object: addr=%p", field_path); + return field_path; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); + return NULL; +} + +BT_HIDDEN +void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path) +{ + if (field_path->indexes->len > 0) { + g_array_remove_range(field_path->indexes, 0, + field_path->indexes->len); + } +} + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_path_copy( + struct bt_ctf_field_path *path) +{ + struct bt_ctf_field_path *new_path; + + BT_ASSERT(path); + BT_LOGD("Copying field path: addr=%p, index-count=%u", + path, path->indexes->len); + new_path = bt_ctf_field_path_create(); + if (!new_path) { + BT_LOGE_STR("Cannot create empty field path."); + goto end; + } + + new_path->root = path->root; + g_array_insert_vals(new_path->indexes, 0, + path->indexes->data, path->indexes->len); + BT_LOGD("Copied field path: original-addr=%p, copy-addr=%p", + path, new_path); +end: + return new_path; +} + +enum bt_ctf_scope bt_ctf_field_path_get_root_scope( + const struct bt_ctf_field_path *field_path) +{ + enum bt_ctf_scope scope = BT_CTF_SCOPE_UNKNOWN; + + if (!field_path) { + BT_LOGW_STR("Invalid parameter: field path is NULL."); + goto end; + } + + scope = field_path->root; + +end: + return scope; +} + +int64_t bt_ctf_field_path_get_index_count( + const struct bt_ctf_field_path *field_path) +{ + int64_t count = (int64_t) -1; + + if (!field_path) { + BT_LOGW_STR("Invalid parameter: field path is NULL."); + goto end; + } + + count = (int64_t) field_path->indexes->len; + +end: + return count; +} + +int bt_ctf_field_path_get_index(const struct bt_ctf_field_path *field_path, + uint64_t index) +{ + int ret = INT_MIN; + + if (!field_path) { + BT_LOGW_STR("Invalid parameter: field path is NULL."); + goto end; + } + + if (index >= field_path->indexes->len) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "addr=%p, index=%" PRIu64 ", count=%u", + field_path, index, field_path->indexes->len); + goto end; + } + + ret = g_array_index(field_path->indexes, int, index); + +end: + return ret; +} diff --git a/src/ctf-writer/field-path.h b/src/ctf-writer/field-path.h new file mode 100644 index 00000000..cf9edd3f --- /dev/null +++ b/src/ctf-writer/field-path.h @@ -0,0 +1,68 @@ +#ifndef BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL +#define BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL + +/* + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "common/common.h" +#include "common/assert.h" +#include +#include + +#include "object.h" + +struct bt_ctf_field_path { + struct bt_ctf_object base; + enum bt_ctf_scope root; + + /* + * Array of integers (int) indicating the index in either + * structures, variants, arrays, or sequences that make up + * the path to a field type. -1 means the "current element + * of an array or sequence type". + */ + GArray *indexes; +}; + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_path_create(void); + +BT_HIDDEN +void bt_ctf_field_path_clear(struct bt_ctf_field_path *field_path); + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_path_copy( + struct bt_ctf_field_path *path); + +BT_HIDDEN enum bt_ctf_scope bt_ctf_field_path_get_root_scope( + const struct bt_ctf_field_path *field_path); + +BT_HIDDEN int64_t bt_ctf_field_path_get_index_count( + const struct bt_ctf_field_path *field_path); + +BT_HIDDEN int bt_ctf_field_path_get_index( + const struct bt_ctf_field_path *field_path, uint64_t index); + +#endif /* BABELTRACE_CTF_WRITER_FIELD_PATH_INTERNAL */ diff --git a/src/ctf-writer/field-types.c b/src/ctf-writer/field-types.c new file mode 100644 index 00000000..644270a6 --- /dev/null +++ b/src/ctf-writer/field-types.c @@ -0,0 +1,5567 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-FIELD-TYPES" +#include "logging.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" + +#include "assert-pre.h" +#include "clock-class.h" +#include "clock.h" +#include "field-path.h" +#include "fields.h" +#include "field-types.h" +#include "object.h" +#include "utils.h" + +static +void destroy_enumeration_mapping(struct bt_ctf_enumeration_mapping *mapping) +{ + g_free(mapping); +} + +BT_HIDDEN +void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft, + bool init_bo, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + BT_ASSERT(ft && (ft->id > BT_CTF_FIELD_TYPE_ID_UNKNOWN) && + (ft->id < BT_CTF_FIELD_TYPE_ID_NR)); + + bt_ctf_object_init_shared(&ft->base, release_func); + ft->methods = methods; + + if (init_bo) { + int ret; + const enum bt_ctf_byte_order bo = BT_CTF_BYTE_ORDER_NATIVE; + + BT_LOGD("Setting initial field type's byte order: bo=%s", + bt_ctf_byte_order_string(bo)); + ret = bt_ctf_field_type_common_set_byte_order(ft, bo); + BT_ASSERT(ret == 0); + } + + ft->alignment = 1; +} + +BT_HIDDEN +void bt_ctf_field_type_common_integer_initialize( + struct bt_ctf_field_type_common *ft, + unsigned int size, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(size > 0); + BT_LOGD("Initializing common integer field type object: size=%u", + size); + ft->id = BT_CTF_FIELD_TYPE_ID_INTEGER; + int_ft->size = size; + int_ft->base = BT_CTF_INTEGER_BASE_DECIMAL; + int_ft->encoding = BT_CTF_STRING_ENCODING_NONE; + bt_ctf_field_type_common_initialize(ft, true, release_func, methods); + BT_LOGD("Initialized common integer field type object: addr=%p, size=%u", + ft, size); +} + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + BT_LOGD_STR("Initializing common floating point number field type object."); + ft->id = BT_CTF_FIELD_TYPE_ID_FLOAT; + flt_ft->exp_dig = sizeof(float) * CHAR_BIT - FLT_MANT_DIG; + flt_ft->mant_dig = FLT_MANT_DIG; + bt_ctf_field_type_common_initialize(ft, true, release_func, methods); + BT_LOGD("Initialized common floating point number field type object: addr=%p, " + "exp-size=%u, mant-size=%u", ft, flt_ft->exp_dig, + flt_ft->mant_dig); +} + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *container_ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(container_ft); + BT_LOGD("Initializing common enumeration field type object: int-ft-addr=%p", + container_ft); + ft->id = BT_CTF_FIELD_TYPE_ID_ENUM; + enum_ft->container_ft = bt_ctf_object_get_ref(container_ft); + enum_ft->entries = g_ptr_array_new_with_free_func( + (GDestroyNotify) destroy_enumeration_mapping); + bt_ctf_field_type_common_initialize(ft, false, release_func, methods); + BT_LOGD("Initialized common enumeration field type object: addr=%p, " + "int-ft-addr=%p, int-ft-size=%u", ft, container_ft, + bt_ctf_field_type_common_integer_get_size(container_ft)); +} + +BT_HIDDEN +void bt_ctf_field_type_common_string_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); + + BT_LOGD_STR("Initializing common string field type object."); + ft->id = BT_CTF_FIELD_TYPE_ID_STRING; + bt_ctf_field_type_common_initialize(ft, true, release_func, methods); + string_ft->encoding = BT_CTF_STRING_ENCODING_UTF8; + ft->alignment = CHAR_BIT; + BT_LOGD("Initialized common string field type object: addr=%p", ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_structure_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + + BT_LOGD_STR("Initializing common structure field type object."); + ft->id = BT_CTF_FIELD_TYPE_ID_STRUCT; + struct_ft->fields = g_array_new(FALSE, TRUE, + sizeof(struct bt_ctf_field_type_common_structure_field)); + struct_ft->field_name_to_index = g_hash_table_new(NULL, NULL); + bt_ctf_field_type_common_initialize(ft, true, release_func, methods); + BT_LOGD("Initialized common structure field type object: addr=%p", ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_array_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft, + unsigned int length, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(element_ft); + BT_LOGD("Initializing common array field type object: element-ft-addr=%p, " + "length=%u", element_ft, length); + ft->id = BT_CTF_FIELD_TYPE_ID_ARRAY; + array_ft->element_ft = bt_ctf_object_get_ref(element_ft); + array_ft->length = length; + bt_ctf_field_type_common_initialize(ft, false, release_func, methods); + BT_LOGD("Initialized common array field type object: addr=%p, " + "element-ft-addr=%p, length=%u", ft, element_ft, length); +} + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft, + const char *length_field_name, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(element_ft); + BT_ASSERT(length_field_name); + BT_ASSERT(bt_ctf_identifier_is_valid(length_field_name)); + BT_LOGD("Initializing common sequence field type object: element-ft-addr=%p, " + "length-field-name=\"%s\"", element_ft, length_field_name); + ft->id = BT_CTF_FIELD_TYPE_ID_SEQUENCE; + seq_ft->element_ft = bt_ctf_object_get_ref(element_ft); + seq_ft->length_field_name = g_string_new(length_field_name); + bt_ctf_field_type_common_initialize(ft, false, release_func, methods); + BT_LOGD("Initialized common sequence field type object: addr=%p, " + "element-ft-addr=%p, length-field-name=\"%s\"", + ft, element_ft, length_field_name); +} + +BT_HIDDEN +void bt_ctf_field_type_common_variant_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *tag_ft, + const char *tag_name, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(!tag_name || bt_ctf_identifier_is_valid(tag_name)); + BT_LOGD("Initializing common variant field type object: " + "tag-ft-addr=%p, tag-field-name=\"%s\"", + tag_ft, tag_name); + ft->id = BT_CTF_FIELD_TYPE_ID_VARIANT; + var_ft->tag_name = g_string_new(tag_name); + var_ft->choice_name_to_index = g_hash_table_new(NULL, NULL); + var_ft->choices = g_array_new(FALSE, TRUE, + sizeof(struct bt_ctf_field_type_common_variant_choice)); + + if (tag_ft) { + var_ft->tag_ft = bt_ctf_object_get_ref(tag_ft); + } + + bt_ctf_field_type_common_initialize(ft, true, release_func, methods); + /* A variant's alignment is undefined */ + ft->alignment = 0; + BT_LOGD("Initialized common variant field type object: addr=%p, " + "tag-ft-addr=%p, tag-field-name=\"%s\"", + ft, tag_ft, tag_name); +} + +BT_HIDDEN +void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_integer *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying integer field type object: addr=%p", ft); + BT_LOGD_STR("Putting mapped clock class."); + bt_ctf_object_put_ref(ft->mapped_clock_class); + g_free(ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_floating_point *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying floating point number field type object: addr=%p", ft); + g_free(ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_enumeration *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying enumeration field type object: addr=%p", ft); + g_ptr_array_free(ft->entries, TRUE); + BT_LOGD_STR("Putting container field type."); + bt_ctf_object_put_ref(ft->container_ft); + g_free(ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_string *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying string field type object: addr=%p", ft); + g_free(ft); +} + +static +void bt_ctf_field_type_common_structure_field_finalize( + struct bt_ctf_field_type_common_structure_field *field) +{ + if (!field) { + return; + } + + BT_LOGD("Finalizing structure field type's field: " + "addr=%p, field-ft-addr=%p, field-name=\"%s\"", + field, field->type, g_quark_to_string(field->name)); + BT_LOGD_STR("Putting field type."); + bt_ctf_object_put_ref(field->type); +} + +BT_HIDDEN +void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_structure *ft = (void *) obj; + uint64_t i; + + if (!ft) { + return; + } + + BT_LOGD("Destroying structure field type object: addr=%p", ft); + + if (ft->fields) { + for (i = 0; i < ft->fields->len; i++) { + bt_ctf_field_type_common_structure_field_finalize( + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + ft, i)); + } + + g_array_free(ft->fields, TRUE); + } + + if (ft->field_name_to_index) { + g_hash_table_destroy(ft->field_name_to_index); + } + + g_free(ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_array *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying array field type object: addr=%p", ft); + BT_LOGD_STR("Putting element field type."); + bt_ctf_object_put_ref(ft->element_ft); + g_free(ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_sequence *ft = (void *) obj; + + if (!ft) { + return; + } + + BT_LOGD("Destroying sequence field type object: addr=%p", ft); + BT_LOGD_STR("Putting element field type."); + bt_ctf_object_put_ref(ft->element_ft); + g_string_free(ft->length_field_name, TRUE); + BT_LOGD_STR("Putting length field path."); + bt_ctf_object_put_ref(ft->length_field_path); + g_free(ft); +} + +static +void bt_ctf_field_type_common_variant_choice_finalize( + struct bt_ctf_field_type_common_variant_choice *choice) +{ + if (!choice) { + return; + } + + BT_LOGD("Finalizing variant field type's choice: " + "addr=%p, field-ft-addr=%p, field-name=\"%s\"", + choice, choice->type, g_quark_to_string(choice->name)); + BT_LOGD_STR("Putting field type."); + bt_ctf_object_put_ref(choice->type); + + if (choice->ranges) { + g_array_free(choice->ranges, TRUE); + } +} + +BT_HIDDEN +void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_common_variant *ft = (void *) obj; + uint64_t i; + + if (!ft) { + return; + } + + BT_LOGD("Destroying variant field type object: addr=%p", ft); + + if (ft->choices) { + for (i = 0; i < ft->choices->len; i++) { + bt_ctf_field_type_common_variant_choice_finalize( + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + ft, i)); + } + + g_array_free(ft->choices, TRUE); + } + + if (ft->choice_name_to_index) { + g_hash_table_destroy(ft->choice_name_to_index); + } + + if (ft->tag_name) { + g_string_free(ft->tag_name, TRUE); + } + + BT_LOGD_STR("Putting tag field type."); + bt_ctf_object_put_ref(ft->tag_ft); + BT_LOGD_STR("Putting tag field path."); + bt_ctf_object_put_ref(ft->tag_field_path); + g_free(ft); +} + +struct range_overlap_query { + union { + uint64_t _unsigned; + int64_t _signed; + } range_start; + + union { + uint64_t _unsigned; + int64_t _signed; + } range_end; + int overlaps; + GQuark mapping_name; +}; + +static +void check_ranges_overlap(gpointer element, gpointer query) +{ + struct bt_ctf_enumeration_mapping *mapping = element; + struct range_overlap_query *overlap_query = query; + + if (mapping->range_start._signed <= overlap_query->range_end._signed + && overlap_query->range_start._signed <= + mapping->range_end._signed) { + overlap_query->overlaps = 1; + overlap_query->mapping_name = mapping->string; + } + + overlap_query->overlaps |= + mapping->string == overlap_query->mapping_name; + + if (overlap_query->overlaps) { + BT_LOGV("Overlapping enumeration field type mappings: " + "mapping-name=\"%s\", " + "mapping-a-range-start=%" PRId64 ", " + "mapping-a-range-end=%" PRId64 ", " + "mapping-b-range-start=%" PRId64 ", " + "mapping-b-range-end=%" PRId64, + g_quark_to_string(mapping->string), + mapping->range_start._signed, + mapping->range_end._signed, + overlap_query->range_start._signed, + overlap_query->range_end._signed); + } +} + +static +void check_ranges_overlap_unsigned(gpointer element, gpointer query) +{ + struct bt_ctf_enumeration_mapping *mapping = element; + struct range_overlap_query *overlap_query = query; + + if (mapping->range_start._unsigned <= overlap_query->range_end._unsigned + && overlap_query->range_start._unsigned <= + mapping->range_end._unsigned) { + overlap_query->overlaps = 1; + overlap_query->mapping_name = mapping->string; + } + + overlap_query->overlaps |= + mapping->string == overlap_query->mapping_name; + + if (overlap_query->overlaps) { + BT_LOGW("Overlapping enumeration field type mappings: " + "mapping-name=\"%s\", " + "mapping-a-range-start=%" PRIu64 ", " + "mapping-a-range-end=%" PRIu64 ", " + "mapping-b-range-start=%" PRIu64 ", " + "mapping-b-range-end=%" PRIu64, + g_quark_to_string(mapping->string), + mapping->range_start._unsigned, + mapping->range_end._unsigned, + overlap_query->range_start._unsigned, + overlap_query->range_end._unsigned); + } +} + +static +gint compare_enumeration_mappings_signed(struct bt_ctf_enumeration_mapping **a, + struct bt_ctf_enumeration_mapping **b) +{ + return ((*a)->range_start._signed < (*b)->range_start._signed) ? -1 : 1; +} + +static +gint compare_enumeration_mappings_unsigned(struct bt_ctf_enumeration_mapping **a, + struct bt_ctf_enumeration_mapping **b) +{ + return ((*a)->range_start._unsigned < (*b)->range_start._unsigned) ? -1 : 1; +} + +static +int add_structure_variant_member(GArray *members, + GHashTable *field_name_to_index, + struct bt_ctf_field_type_common *field_type, const char *field_name, + bool is_variant) +{ + int ret = 0; + GQuark name_quark = g_quark_from_string(field_name); + struct bt_ctf_field_type_common **member_ft; + GQuark *member_name; + + /* Make sure structure does not contain a field of the same name */ + if (g_hash_table_lookup_extended(field_name_to_index, + GUINT_TO_POINTER(name_quark), NULL, NULL)) { + BT_LOGW("Structure or variant field type already contains a field type with this name: " + "field-name=\"%s\"", field_name); + ret = -1; + goto end; + } + + g_array_set_size(members, members->len + 1); + + if (is_variant) { + struct bt_ctf_field_type_common_variant_choice *choice = + &g_array_index(members, + struct bt_ctf_field_type_common_variant_choice, + members->len - 1); + + member_ft = &choice->type; + member_name = &choice->name; + BT_ASSERT(!choice->ranges); + choice->ranges = g_array_new(FALSE, TRUE, + sizeof(struct bt_ctf_field_type_common_variant_choice_range)); + BT_ASSERT(choice->ranges); + } else { + struct bt_ctf_field_type_common_structure_field *field = + &g_array_index(members, + struct bt_ctf_field_type_common_structure_field, + members->len - 1); + + member_ft = &field->type; + member_name = &field->name; + } + + *member_name = name_quark; + *member_ft = bt_ctf_object_get_ref(field_type); + g_hash_table_insert(field_name_to_index, + GUINT_TO_POINTER(name_quark), + GUINT_TO_POINTER(members->len - 1)); + BT_LOGV("Added structure/variant field type member: member-ft-addr=%p, " + "member-name=\"%s\"", field_type, field_name); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + if (int_ft->mapped_clock_class && int_ft->is_signed) { + BT_LOGW("Invalid integer field type: cannot be signed and have a mapped clock class: " + "ft-addr=%p, clock-class-addr=%p, clock-class-name=\"%s\"", + ft, int_ft->mapped_clock_class, + bt_ctf_clock_class_get_name(int_ft->mapped_clock_class)); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +void bt_ctf_field_type_enum_iter_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_field_type_enumeration_mapping_iterator *iter = + container_of(obj, + struct bt_ctf_field_type_enumeration_mapping_iterator, + base); + + BT_LOGD("Destroying enumeration field type mapping iterator: addr=%p", + obj); + BT_LOGD_STR("Putting parent enumeration field type."); + bt_ctf_object_put_ref(iter->enumeration_ft); + g_free(iter); +} + +static +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_find_mappings_type( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_field_type_enumeration_mapping_iterator_type iterator_type) +{ + struct bt_ctf_field_type_enumeration_mapping_iterator *iter = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, + "Field type"); + iter = g_new0(struct bt_ctf_field_type_enumeration_mapping_iterator, 1); + if (!iter) { + BT_LOGE_STR("Failed to allocate one enumeration field type mapping."); + goto end; + } + + bt_ctf_object_init_shared(&iter->base, bt_ctf_field_type_enum_iter_destroy); + iter->enumeration_ft = bt_ctf_object_get_ref(ft); + iter->index = -1; + iter->type = iterator_type; + +end: + return iter; +} + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_find_mappings_by_name( + struct bt_ctf_field_type_common *ft, const char *name) +{ + struct bt_ctf_field_type_enumeration_mapping_iterator *iter; + + iter = bt_ctf_field_type_common_enumeration_find_mappings_type( + ft, CTF_ITERATOR_BY_NAME); + if (!iter) { + BT_LOGW("Cannot create enumeration field type mapping iterator: " + "ft-addr=%p, mapping-name=\"%s\"", ft, name); + goto error; + } + + iter->u.name_quark = g_quark_try_string(name); + if (!iter->u.name_quark) { + /* + * No results are possible, set the iterator's position at the + * end. + */ + iter->index = iter->enumeration_ft->entries->len; + } + + return iter; + +error: + bt_ctf_object_put_ref(iter); + return NULL; +} + +static +struct bt_ctf_enumeration_mapping *bt_ctf_field_type_common_enumeration_get_mapping_by_index( + struct bt_ctf_field_type_common *ft, uint64_t index) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_enumeration_mapping *mapping = NULL; + + if (index >= enum_ft->entries->len) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "addr=%p, index=%" PRIu64 ", count=%u", + ft, index, enum_ft->entries->len); + goto end; + } + + mapping = g_ptr_array_index(enum_ft->entries, index); + +end: + return mapping; +} + +BT_HIDDEN +int bt_ctf_field_type_enumeration_mapping_iterator_next( + struct bt_ctf_field_type_enumeration_mapping_iterator *iter) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = iter->enumeration_ft; + int i, ret = 0, len; + + BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); + len = enum_ft->entries->len; + for (i = iter->index + 1; i < len; i++) { + struct bt_ctf_enumeration_mapping *mapping = + bt_ctf_field_type_common_enumeration_get_mapping_by_index( + BT_CTF_TO_COMMON(enum_ft), i); + + switch (iter->type) { + case CTF_ITERATOR_BY_NAME: + if (mapping->string == iter->u.name_quark) { + iter->index = i; + goto end; + } + break; + case CTF_ITERATOR_BY_SIGNED_VALUE: + { + int64_t value = iter->u.signed_value; + + if (value >= mapping->range_start._signed && + value <= mapping->range_end._signed) { + iter->index = i; + goto end; + } + break; + } + case CTF_ITERATOR_BY_UNSIGNED_VALUE: + { + uint64_t value = iter->u.unsigned_value; + + if (value >= mapping->range_start._unsigned && + value <= mapping->range_end._unsigned) { + iter->index = i; + goto end; + } + break; + } + default: + BT_LOGF("Invalid enumeration field type mapping iterator type: " + "type=%d", iter->type); + abort(); + } + } + + ret = -1; + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value( + struct bt_ctf_field_type_common *ft, int64_t value) +{ + struct bt_ctf_field_type_enumeration_mapping_iterator *iter; + + iter = bt_ctf_field_type_common_enumeration_find_mappings_type( + ft, CTF_ITERATOR_BY_SIGNED_VALUE); + if (!iter) { + BT_LOGW("Cannot create enumeration field type mapping iterator: " + "ft-addr=%p, value=%" PRId64, ft, value); + goto error; + } + + if (bt_ctf_field_type_common_integer_is_signed( + BT_CTF_TO_COMMON(iter->enumeration_ft->container_ft)) != 1) { + BT_LOGW("Invalid parameter: enumeration field type is unsigned: " + "enum-ft-addr=%p, int-ft-addr=%p", + ft, iter->enumeration_ft->container_ft); + goto error; + } + + iter->u.signed_value = value; + return iter; + +error: + bt_ctf_object_put_ref(iter); + return NULL; +} + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value( + struct bt_ctf_field_type_common *ft, uint64_t value) +{ + struct bt_ctf_field_type_enumeration_mapping_iterator *iter; + + iter = bt_ctf_field_type_common_enumeration_find_mappings_type( + ft, CTF_ITERATOR_BY_UNSIGNED_VALUE); + if (!iter) { + BT_LOGW("Cannot create enumeration field type mapping iterator: " + "ft-addr=%p, value=%" PRIu64, ft, value); + goto error; + } + + if (bt_ctf_field_type_common_integer_is_signed( + BT_CTF_TO_COMMON(iter->enumeration_ft->container_ft)) != 0) { + BT_LOGW("Invalid parameter: enumeration field type is signed: " + "enum-ft-addr=%p, int-ft-addr=%p", + ft, iter->enumeration_ft->container_ft); + goto error; + } + + iter->u.unsigned_value = value; + return iter; + +error: + bt_ctf_object_put_ref(iter); + return NULL; +} + +BT_HIDDEN +int bt_ctf_field_type_enumeration_mapping_iterator_signed_get( + struct bt_ctf_field_type_enumeration_mapping_iterator *iter, + const char **mapping_name, int64_t *range_begin, + int64_t *range_end) +{ + BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); + BT_CTF_ASSERT_PRE(iter->index != -1, + "Invalid enumeration field type mapping iterator access: " + "addr=%p, position=-1", iter); + return bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( + (void *) iter->enumeration_ft, iter->index, + mapping_name, range_begin, range_end); +} + +BT_HIDDEN +int bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get( + struct bt_ctf_field_type_enumeration_mapping_iterator *iter, + const char **mapping_name, uint64_t *range_begin, + uint64_t *range_end) +{ + BT_CTF_ASSERT_PRE_NON_NULL(iter, "Enumeration field type mapping iterator"); + BT_CTF_ASSERT_PRE(iter->index != -1, + "Invalid enumeration field type mapping iterator access: " + "addr=%p, position=-1", iter); + return bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( + (void *) iter->enumeration_ft, iter->index, + mapping_name, range_begin, range_end); +} + +/* + * Note: This algorithm is O(n^2) vs number of enumeration mappings. + * Only used when freezing an enumeration. + */ +static +void bt_ctf_field_type_common_enumeration_set_range_overlap( + struct bt_ctf_field_type_common_enumeration *ft) +{ + int64_t i, j, len; + int is_signed; + + BT_LOGV("Setting enumeration field type's overlap flag: addr=%p", + ft); + len = ft->entries->len; + is_signed = bt_ctf_field_type_common_integer_is_signed( + BT_CTF_TO_COMMON(ft->container_ft)); + + for (i = 0; i < len; i++) { + for (j = i + 1; j < len; j++) { + struct bt_ctf_enumeration_mapping *mapping[2]; + + mapping[0] = bt_ctf_field_type_common_enumeration_get_mapping_by_index( + BT_CTF_TO_COMMON(ft), i); + mapping[1] = bt_ctf_field_type_common_enumeration_get_mapping_by_index( + BT_CTF_TO_COMMON(ft), j); + if (is_signed) { + if (mapping[0]->range_start._signed + <= mapping[1]->range_end._signed + && mapping[0]->range_end._signed + >= mapping[1]->range_start._signed) { + ft->has_overlapping_ranges = BT_TRUE; + goto end; + } + } else { + if (mapping[0]->range_start._unsigned + <= mapping[1]->range_end._unsigned + && mapping[0]->range_end._unsigned + >= mapping[1]->range_start._unsigned) { + ft->has_overlapping_ranges = BT_TRUE; + goto end; + } + } + } + } + +end: + if (ft->has_overlapping_ranges) { + BT_LOGV_STR("Enumeration field type has overlapping ranges."); + } else { + BT_LOGV_STR("Enumeration field type has no overlapping ranges."); + } +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_validate_recursive( + struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + ret = bt_ctf_field_type_common_integer_validate( + BT_CTF_TO_COMMON(enum_ft->container_ft)); + if (ret) { + BT_LOGW("Invalid enumeration field type: container type is invalid: " + "enum-ft-addr=%p, int-ft-addr=%p", + ft, enum_ft->container_ft); + goto end; + } + + /* Ensure enum has entries */ + if (enum_ft->entries->len == 0) { + BT_LOGW("Invalid enumeration field type: no entries: " + "addr=%p", ft); + ret = -1; + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_validate_recursive( + struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + /* Length field name should be set at this point */ + if (seq_ft->length_field_name->len == 0) { + BT_LOGW("Invalid sequence field type: no length field name: " + "addr=%p", ft); + ret = -1; + goto end; + } + + ret = bt_ctf_field_type_common_validate(seq_ft->element_ft); + if (ret) { + BT_LOGW("Invalid sequence field type: invalid element field type: " + "seq-ft-addr=%p, element-ft-add=%p", + ft, seq_ft->element_ft); + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_array_validate_recursive( + struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + ret = bt_ctf_field_type_common_validate(array_ft->element_ft); + if (ret) { + BT_LOGW("Invalid array field type: invalid element field type: " + "array-ft-addr=%p, element-ft-add=%p", + ft, array_ft->element_ft); + } + + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_validate_recursive( + struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + struct bt_ctf_field_type_common *child_ft = NULL; + int64_t field_count = + bt_ctf_field_type_common_structure_get_field_count(ft); + int64_t i; + + BT_ASSERT(field_count >= 0); + + for (i = 0; i < field_count; ++i) { + const char *field_name; + + ret = bt_ctf_field_type_common_structure_borrow_field_by_index(ft, + &field_name, &child_ft, i); + BT_ASSERT(ret == 0); + ret = bt_ctf_field_type_common_validate(child_ft); + if (ret) { + BT_LOGW("Invalid structure field type: " + "a contained field type is invalid: " + "struct-ft-addr=%p, field-ft-addr=%p, " + "field-name=\"%s\", field-index=%" PRId64, + ft, child_ft, field_name, i); + goto end; + } + } + +end: + return ret; +} + +static +bt_bool bt_ctf_field_type_common_enumeration_has_overlapping_ranges( + struct bt_ctf_field_type_common_enumeration *enum_ft) +{ + if (!enum_ft->common.frozen) { + bt_ctf_field_type_common_enumeration_set_range_overlap(enum_ft); + } + + return enum_ft->has_overlapping_ranges; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_validate_recursive( + struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + int64_t field_count; + struct bt_ctf_field_type_common *child_ft = NULL; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + int64_t i; + + if (var_ft->tag_name->len == 0) { + BT_LOGW("Invalid variant field type: no tag field name: " + "addr=%p", ft); + ret = -1; + goto end; + } + + if (!var_ft->tag_ft) { + BT_LOGW("Invalid variant field type: no tag field type: " + "addr=%p, tag-field-name=\"%s\"", var_ft, + var_ft->tag_name->str); + ret = -1; + goto end; + } + + if (bt_ctf_field_type_common_enumeration_has_overlapping_ranges(var_ft->tag_ft)) { + BT_LOGW("Invalid variant field type: enumeration tag field type has overlapping ranges: " + "variant-ft-addr=%p, tag-field-name=\"%s\", " + "enum-ft-addr=%p", ft, var_ft->tag_name->str, + var_ft->tag_ft); + ret = -1; + goto end; + } + + /* + * It is valid to have a variant field type which does not have + * the fields corresponding to each label in the associated + * enumeration. + * + * It is also valid to have variant field type fields which + * cannot be selected because the variant field type tag has no + * mapping named as such. This scenario, while not ideal, cannot + * cause any error. + * + * If a non-existing field happens to be selected by an + * enumeration while reading a variant field, an error will be + * generated at that point (while reading the stream). + */ + field_count = bt_ctf_field_type_common_variant_get_field_count(ft); + if (field_count < 0) { + BT_LOGW("Invalid variant field type: no fields: " + "addr=%p, tag-field-name=\"%s\"", + ft, var_ft->tag_name->str); + ret = -1; + goto end; + } + + for (i = 0; i < field_count; ++i) { + const char *field_name; + + ret = bt_ctf_field_type_common_variant_borrow_field_by_index(ft, + &field_name, &child_ft, i); + BT_ASSERT(ret == 0); + ret = bt_ctf_field_type_common_validate(child_ft); + if (ret) { + BT_LOGW("Invalid variant field type: " + "a contained field type is invalid: " + "variant-ft-addr=%p, tag-field-name=\"%s\", " + "field-ft-addr=%p, field-name=\"%s\", " + "field-index=%" PRId64, + ft, var_ft->tag_name->str, child_ft, + field_name, i); + goto end; + } + } + +end: + return ret; +} + +/* + * This function validates a given field type without considering + * where this field type is located. It only validates the properties + * of the given field type and the properties of its children if + * applicable. + */ +BT_HIDDEN +int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft) +{ + int ret = 0; + + BT_ASSERT(ft); + + if (ft->valid) { + /* Already marked as valid */ + goto end; + } + + if (ft->methods->validate) { + ret = ft->methods->validate(ft); + } + + if (ret == 0 && ft->frozen) { + /* Field type is valid */ + BT_LOGV("Marking field type as valid: addr=%p", ft); + ft->valid = 1; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, + "Field type"); + return (int) int_ft->size; +} + +BT_HIDDEN +bt_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, + "Field type"); + return int_ft->is_signed; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft, + bt_bool is_signed) +{ + int ret = 0; + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: field type is not an integer field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + int_ft->is_signed = !!is_signed; + BT_LOGV("Set integer field type's signedness: addr=%p, is-signed=%d", + ft, is_signed); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft, + unsigned int size) +{ + int ret = 0; + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: field type is not an integer field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (size == 0 || size > 64) { + BT_LOGW("Invalid parameter: size must be between 1 and 64: " + "addr=%p, size=%u", ft, size); + ret = -1; + goto end; + } + + int_ft->size = size; + BT_LOGV("Set integer field type's size: addr=%p, size=%u", + ft, size); + +end: + return ret; +} + +BT_HIDDEN +enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, + "Field type"); + return int_ft->base; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft, + enum bt_ctf_integer_base base) +{ + int ret = 0; + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: field type is not an integer field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + switch (base) { + case BT_CTF_INTEGER_BASE_UNSPECIFIED: + case BT_CTF_INTEGER_BASE_BINARY: + case BT_CTF_INTEGER_BASE_OCTAL: + case BT_CTF_INTEGER_BASE_DECIMAL: + case BT_CTF_INTEGER_BASE_HEXADECIMAL: + { + int_ft->base = base; + break; + } + default: + BT_LOGW("Invalid parameter: unknown integer field type base: " + "addr=%p, base=%d", ft, base); + ret = -1; + } + + BT_LOGV("Set integer field type's base: addr=%p, base=%s", + ft, bt_ctf_integer_base_string(base)); + +end: + return ret; +} + +BT_HIDDEN +enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, + "Field type"); + return int_ft->encoding; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft, + enum bt_ctf_string_encoding encoding) +{ + int ret = 0; + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: field type is not an integer field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (encoding != BT_CTF_STRING_ENCODING_UTF8 && + encoding != BT_CTF_STRING_ENCODING_ASCII && + encoding != BT_CTF_STRING_ENCODING_NONE) { + BT_LOGW("Invalid parameter: unknown string encoding: " + "addr=%p, encoding=%d", ft, encoding); + ret = -1; + goto end; + } + + int_ft->encoding = encoding; + BT_LOGV("Set integer field type's encoding: addr=%p, encoding=%s", + ft, bt_ctf_string_encoding_string(encoding)); + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_INTEGER, + "Field type"); + return int_ft->mapped_clock_class; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class *clock_class) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + int ret = 0; + + if (!clock_class) { + BT_LOGW_STR("Invalid parameter: clock class is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: field type is not an integer field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + goto end; + } + + if (!bt_ctf_clock_class_is_valid(clock_class)) { + BT_LOGW("Invalid parameter: clock class is invalid: ft-addr=%p" + "clock-class-addr=%p, clock-class-name=\"%s\"", + ft, clock_class, + bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(int_ft->mapped_clock_class); + int_ft->mapped_clock_class = bt_ctf_object_get_ref(clock_class); + BT_LOGV("Set integer field type's mapped clock class: ft-addr=%p, " + "clock-class-addr=%p, clock-class-name=\"%s\"", + ft, clock_class, bt_ctf_clock_class_get_name(clock_class)); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_mapped_clock_class( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class *clock_class) +{ + int ret = 0; + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + ret = bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( + ft, clock_class); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( + struct bt_ctf_field_type_common *ft, uint64_t index, + const char **mapping_name, int64_t *range_begin, + int64_t *range_end) +{ + int ret = 0; + struct bt_ctf_enumeration_mapping *mapping; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, + BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); + mapping = bt_ctf_field_type_common_enumeration_get_mapping_by_index(ft, + index); + if (!mapping) { + /* bt_ctf_field_type_common_enumeration_get_mapping_by_index() logs errors */ + ret = -1; + goto end; + } + + if (mapping_name) { + *mapping_name = g_quark_to_string(mapping->string); + BT_ASSERT(*mapping_name); + } + + if (range_begin) { + *range_begin = mapping->range_start._signed; + } + + if (range_end) { + *range_end = mapping->range_end._signed; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( + struct bt_ctf_field_type_common *ft, uint64_t index, + const char **mapping_name, uint64_t *range_begin, + uint64_t *range_end) +{ + int ret = 0; + struct bt_ctf_enumeration_mapping *mapping; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); + mapping = bt_ctf_field_type_common_enumeration_get_mapping_by_index( + ft, index); + if (!mapping) { + /* bt_ctf_field_type_common_enumeration_get_mapping_by_index() reports any error */ + ret = -1; + goto end; + } + + if (mapping_name) { + *mapping_name = g_quark_to_string(mapping->string); + BT_ASSERT(*mapping_name); + } + + if (range_begin) { + *range_begin = mapping->range_start._unsigned; + } + + if (range_end) { + *range_end = mapping->range_end._unsigned; + } + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_enumeration_borrow_container_field_type( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); + return BT_CTF_TO_COMMON(enum_ft->container_ft); +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_signed_add_mapping( + struct bt_ctf_field_type_common *ft, const char *string, + int64_t range_start, int64_t range_end) +{ + int ret = 0; + GQuark mapping_name; + struct bt_ctf_enumeration_mapping *mapping; + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + char *escaped_string; + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (!string) { + BT_LOGW_STR("Invalid parameter: string is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { + BT_LOGW("Invalid parameter: field type is not an enumeration field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (range_end < range_start) { + BT_LOGW("Invalid parameter: range's end is lesser than range's start: " + "addr=%p, range-start=%" PRId64 ", range-end=%" PRId64, + ft, range_start, range_end); + ret = -1; + goto end; + } + + if (strlen(string) == 0) { + BT_LOGW("Invalid parameter: mapping name is an empty string: " + "enum-ft-addr=%p, mapping-name-addr=%p", ft, + string); + ret = -1; + goto end; + } + + escaped_string = g_strescape(string, NULL); + if (!escaped_string) { + BT_LOGE("Cannot escape mapping name: enum-ft-addr=%p, " + "mapping-name-addr=%p, mapping-name=\"%s\"", + ft, string, string); + ret = -1; + goto end; + } + + mapping = g_new(struct bt_ctf_enumeration_mapping, 1); + if (!mapping) { + BT_LOGE_STR("Failed to allocate one enumeration mapping."); + ret = -1; + goto error_free; + } + mapping_name = g_quark_from_string(escaped_string); + *mapping = (struct bt_ctf_enumeration_mapping) { + .range_start._signed = range_start, + .range_end._signed = range_end, + .string = mapping_name, + }; + g_ptr_array_add(enum_ft->entries, mapping); + g_ptr_array_sort(enum_ft->entries, + (GCompareFunc) compare_enumeration_mappings_signed); + BT_LOGV("Added mapping to signed enumeration field type: addr=%p, " + "name=\"%s\", range-start=%" PRId64 ", " + "range-end=%" PRId64, + ft, string, range_start, range_end); + +error_free: + free(escaped_string); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_unsigned_add_mapping( + struct bt_ctf_field_type_common *ft, const char *string, + uint64_t range_start, uint64_t range_end) +{ + int ret = 0; + GQuark mapping_name; + struct bt_ctf_enumeration_mapping *mapping; + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + char *escaped_string; + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (!string) { + BT_LOGW_STR("Invalid parameter: string is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { + BT_LOGW("Invalid parameter: field type is not an enumeration field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (range_end < range_start) { + BT_LOGW("Invalid parameter: range's end is lesser than range's start: " + "addr=%p, range-start=%" PRIu64 ", range-end=%" PRIu64, + ft, range_start, range_end); + ret = -1; + goto end; + } + + if (strlen(string) == 0) { + BT_LOGW("Invalid parameter: mapping name is an empty string: " + "enum-ft-addr=%p, mapping-name-addr=%p", ft, + string); + ret = -1; + goto end; + } + + escaped_string = g_strescape(string, NULL); + if (!escaped_string) { + BT_LOGE("Cannot escape mapping name: enum-ft-addr=%p, " + "mapping-name-addr=%p, mapping-name=\"%s\"", + ft, string, string); + ret = -1; + goto end; + } + + mapping = g_new(struct bt_ctf_enumeration_mapping, 1); + if (!mapping) { + BT_LOGE_STR("Failed to allocate one enumeration mapping."); + ret = -1; + goto error_free; + } + mapping_name = g_quark_from_string(escaped_string); + *mapping = (struct bt_ctf_enumeration_mapping) { + .range_start._unsigned = range_start, + .range_end._unsigned = range_end, + .string = mapping_name, + }; + g_ptr_array_add(enum_ft->entries, mapping); + g_ptr_array_sort(enum_ft->entries, + (GCompareFunc) compare_enumeration_mappings_unsigned); + BT_LOGV("Added mapping to unsigned enumeration field type: addr=%p, " + "name=\"%s\", range-start=%" PRIu64 ", " + "range-end=%" PRIu64, + ft, string, range_start, range_end); + +error_free: + free(escaped_string); + +end: + return ret; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_enumeration_get_mapping_count( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ENUM, "Field type"); + return (int64_t) enum_ft->entries->len; +} + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_get_exponent_digits( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_FLOAT, + "Field type"); + return (int) flt_ft->exp_dig; +} + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_set_exponent_digits( + struct bt_ctf_field_type_common *ft, + unsigned int exponent_digits) +{ + int ret = 0; + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_FLOAT) { + BT_LOGW("Invalid parameter: field type is not a floating point number field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if ((exponent_digits != sizeof(float) * CHAR_BIT - FLT_MANT_DIG) && + (exponent_digits != sizeof(double) * CHAR_BIT - DBL_MANT_DIG) && + (exponent_digits != + sizeof(long double) * CHAR_BIT - LDBL_MANT_DIG)) { + BT_LOGW("Invalid parameter: invalid exponent size: " + "addr=%p, exp-size=%u", ft, exponent_digits); + ret = -1; + goto end; + } + + flt_ft->exp_dig = exponent_digits; + BT_LOGV("Set floating point number field type's exponent size: addr=%p, " + "exp-size=%u", ft, exponent_digits); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_get_mantissa_digits( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_FLOAT, + "Field type"); + return (int) flt_ft->mant_dig; +} + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_set_mantissa_digits( + struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits) +{ + int ret = 0; + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_FLOAT) { + BT_LOGW("Invalid parameter: field type is not a floating point number field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if ((mantissa_digits != FLT_MANT_DIG) && + (mantissa_digits != DBL_MANT_DIG) && + (mantissa_digits != LDBL_MANT_DIG)) { + BT_LOGW("Invalid parameter: invalid mantissa size: " + "addr=%p, mant-size=%u", ft, mantissa_digits); + ret = -1; + goto end; + } + + flt_ft->mant_dig = mantissa_digits; + BT_LOGV("Set floating point number field type's mantissa size: addr=%p, " + "mant-size=%u", ft, mantissa_digits); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_replace_field( + struct bt_ctf_field_type_common *ft, + const char *field_name, + struct bt_ctf_field_type_common *field_type) +{ + int ret = 0; + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + GQuark name_quark; + uint64_t i; + + BT_ASSERT(ft); + BT_ASSERT(field_name); + BT_ASSERT(field_type); + BT_ASSERT(ft->id == BT_CTF_FIELD_TYPE_ID_STRUCT); + name_quark = g_quark_from_string(field_name); + + for (i = 0; i < struct_ft->fields->len; i++) { + struct bt_ctf_field_type_common_structure_field *field = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, i); + + if (field->name == name_quark) { + bt_ctf_object_put_ref(field->type); + field->type = bt_ctf_object_get_ref(field_type); + } + } + + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *field_type, + const char *field_name) +{ + int ret = 0; + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + + /* + * TODO: check that `field_type` does not contain `type`, + * recursively. + */ + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (!field_name) { + BT_LOGW_STR("Invalid parameter: field name is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid parameter: field type is not a structure field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (ft == field_type) { + BT_LOGW("Invalid parameter: structure field type and field type to add are the same: " + "addr=%p", ft); + ret = -1; + goto end; + } + + if (add_structure_variant_member(struct_ft->fields, + struct_ft->field_name_to_index, field_type, field_name, + false)) { + BT_LOGW("Cannot add field to structure field type: " + "struct-ft-addr=%p, field-ft-addr=%p, field-name=\"%s\"", + ft, field_type, field_name); + ret = -1; + goto end; + } + + BT_LOGV("Added structure field type field: struct-ft-addr=%p, " + "field-ft-addr=%p, field-name=\"%s\"", ft, + field_type, field_name); + +end: + return ret; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_structure_get_field_count( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, + "Field type"); + return (int64_t) struct_ft->fields->len; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_borrow_field_by_index( + struct bt_ctf_field_type_common *ft, + const char **field_name, + struct bt_ctf_field_type_common **field_type, uint64_t index) +{ + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common_structure_field *field; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, + "Field type"); + BT_CTF_ASSERT_PRE(index < struct_ft->fields->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u, ft-addr=%p", + index, struct_ft->fields->len, ft); + field = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(struct_ft, index); + + if (field_type) { + *field_type = field->type; + } + + if (field_name) { + *field_name = g_quark_to_string(field->name); + BT_ASSERT(*field_name); + } + + return 0; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_structure_borrow_field_type_by_name( + struct bt_ctf_field_type_common *ft, const char *name) +{ + size_t index; + GQuark name_quark; + struct bt_ctf_field_type_common_structure_field *field; + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common *field_type = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, + "Field type"); + name_quark = g_quark_try_string(name); + if (!name_quark) { + BT_LOGV("No such structure field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + goto end; + } + + if (!g_hash_table_lookup_extended(struct_ft->field_name_to_index, + GUINT_TO_POINTER(name_quark), NULL, (gpointer *) &index)) { + BT_LOGV("No such structure field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + goto end; + } + + field = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, index); + field_type = field->type; + +end: + return field_type; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_tag_field_type( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common *tag_ft = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + + if (!var_ft->tag_ft) { + BT_LOGV("Variant field type has no tag field type: " + "addr=%p", ft); + goto end; + } + + tag_ft = BT_CTF_TO_COMMON(var_ft->tag_ft); + +end: + return tag_ft; +} + +BT_HIDDEN +const char *bt_ctf_field_type_common_variant_get_tag_name( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + const char *tag_name = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + + if (var_ft->tag_name->len == 0) { + BT_LOGV("Variant field type has no tag field name: " + "addr=%p", ft); + goto end; + } + + tag_name = var_ft->tag_name->str; + +end: + return tag_name; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_name( + struct bt_ctf_field_type_common *ft, const char *name) +{ + int ret = 0; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { + BT_LOGW("Invalid parameter: field type is not a variant field type: " + "addr=%p, ft-id=%s", ft, bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (!bt_ctf_identifier_is_valid(name)) { + BT_LOGW("Invalid parameter: tag field name is not a valid CTF identifier: " + "variant-ft-addr=%p, tag-field-name=\"%s\"", + ft, name); + ret = -1; + goto end; + } + + g_string_assign(var_ft->tag_name, name); + BT_LOGV("Set variant field type's tag field name: addr=%p, " + "tag-field-name=\"%s\"", ft, name); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *field_type, + const char *field_name) +{ + size_t i; + int ret = 0; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + GQuark field_name_quark = g_quark_from_string(field_name); + + /* + * TODO: check that `field_type` does not contain `type`, + * recursively. + */ + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { + BT_LOGW("Invalid parameter: field type is not a variant field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (ft == field_type) { + BT_LOGW("Invalid parameter: variant field type and field type to add are the same: " + "addr=%p", ft); + ret = -1; + goto end; + } + + /* The user has explicitly provided a tag; validate against it. */ + if (var_ft->tag_ft) { + int name_found = 0; + + /* Make sure this name is present in the enum tag */ + for (i = 0; i < var_ft->tag_ft->entries->len; i++) { + struct bt_ctf_enumeration_mapping *mapping = + g_ptr_array_index(var_ft->tag_ft->entries, i); + + if (mapping->string == field_name_quark) { + name_found = 1; + break; + } + } + + if (!name_found) { + /* Validation failed */ + BT_LOGW("Invalid parameter: field name does not name a tag field type's mapping: " + "variant-ft-addr=%p, tag-ft-addr=%p, " + "tag-field-name=\"%s\"" + "field-ft-addr=%p, field-name=\"%s\"", + ft, var_ft->tag_ft, var_ft->tag_name->str, + field_type, field_name); + ret = -1; + goto end; + } + } + + if (add_structure_variant_member(var_ft->choices, + var_ft->choice_name_to_index, field_type, + field_name, true)) { + BT_LOGW("Cannot add field to variant field type: " + "variant-ft-addr=%p, field-ft-addr=%p, field-name=\"%s\"", + ft, field_type, field_name); + ret = -1; + goto end; + } + + BT_LOGV("Added variant field type field: variant-ft-addr=%p, " + "field-ft-addr=%p, field-name=\"%s\"", ft, + field_type, field_name); + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_by_name( + struct bt_ctf_field_type_common *ft, + const char *field_name) +{ + size_t index; + GQuark name_quark; + struct bt_ctf_field_type_common_variant_choice *choice; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common *field_type = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_NON_NULL(field_name, "Name"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + name_quark = g_quark_try_string(field_name); + if (!name_quark) { + BT_LOGV("No such variant field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, field_name); + goto end; + } + + if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, + GUINT_TO_POINTER(name_quark), NULL, (gpointer *) &index)) { + BT_LOGV("No such variant field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, field_name); + goto end; + } + + choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, index); + field_type = choice->type; + +end: + return field_type; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_variant_get_field_count( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Variant field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + return (int64_t) var_ft->choices->len; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_borrow_field_by_index( + struct bt_ctf_field_type_common *ft, + const char **field_name, + struct bt_ctf_field_type_common **field_type, uint64_t index) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common_variant_choice *choice; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + BT_CTF_ASSERT_PRE(index < var_ft->choices->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u, ft-addr=%p", + index, var_ft->choices->len, ft); + choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, index); + + if (field_type) { + *field_type = choice->type; + } + + if (field_name) { + *field_name = g_quark_to_string(choice->name); + BT_ASSERT(*field_name); + } + + return 0; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_variant_find_choice_index( + struct bt_ctf_field_type_common *ft, uint64_t uval, + bool is_signed) +{ + int64_t ret; + uint64_t i; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + BT_ASSERT(ft); + BT_ASSERT(ft->id == BT_CTF_FIELD_TYPE_ID_VARIANT); + + if (bt_ctf_field_type_common_variant_update_choices(ft)) { + ret = INT64_C(-1); + goto end; + } + + for (i = 0; i < var_ft->choices->len; i++) { + uint64_t range_i; + struct bt_ctf_field_type_common_variant_choice *choice = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + var_ft, i); + + for (range_i = 0; range_i < choice->ranges->len; range_i++) { + struct bt_ctf_field_type_common_variant_choice_range *range = + &g_array_index( + choice->ranges, + struct bt_ctf_field_type_common_variant_choice_range, + range_i); + + if (is_signed) { + int64_t tag_ival = (int64_t) uval; + + if (tag_ival >= range->lower.i && + tag_ival <= range->upper.i) { + goto found; + } + } else { + if (uval >= range->lower.u && + uval <= range->upper.u) { + goto found; + } + } + } + } + + /* Range not found */ + ret = INT64_C(-1); + goto end; + +found: + ret = (int64_t) i; + +end: + return ret; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_array_borrow_element_field_type( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ARRAY, + "Field type"); + BT_ASSERT(array_ft && array_ft->element_ft); + return array_ft->element_ft; +} + +BT_HIDDEN +int bt_ctf_field_type_common_array_set_element_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: array field type is NULL."); + ret = -1; + goto end; + } + + if (!element_ft) { + BT_LOGW_STR("Invalid parameter: element field type is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_ARRAY) { + BT_LOGW("Invalid parameter: field type is not an array field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (array_ft->element_ft) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(array_ft->element_ft); + } + + array_ft->element_ft = bt_ctf_object_get_ref(element_ft); + BT_LOGV("Set array field type's element field type: array-ft-addr=%p, " + "element-ft-addr=%p", ft, element_ft); + +end: + return ret; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_ARRAY, + "Field type"); + return (int64_t) array_ft->length; +} + +BT_HIDDEN +struct bt_ctf_field_type_common *bt_ctf_field_type_common_sequence_borrow_element_field_type( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, + "Field type"); + return seq_ft->element_ft; +} + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_set_element_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: sequence field type is NULL."); + ret = -1; + goto end; + } + + if (!element_ft) { + BT_LOGW_STR("Invalid parameter: element field type is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_SEQUENCE) { + BT_LOGW("Invalid parameter: field type is not a sequence field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (seq_ft->element_ft) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(seq_ft->element_ft); + } + + seq_ft->element_ft = element_ft; + bt_ctf_object_get_ref(seq_ft->element_ft); + BT_LOGV("Set sequence field type's element field type: sequence-ft-addr=%p, " + "element-ft-addr=%p", ft, element_ft); + +end: + return ret; +} + +BT_HIDDEN +const char *bt_ctf_field_type_common_sequence_get_length_field_name( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, + "Field type"); + return seq_ft->length_field_name ? + seq_ft->length_field_name->str : NULL; +} + +BT_HIDDEN +enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRING, + "Field type"); + return string_ft->encoding; +} + +BT_HIDDEN +int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft, + enum bt_ctf_string_encoding encoding) +{ + int ret = 0; + struct bt_ctf_field_type_common_string *string_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_STRING) { + BT_LOGW("Invalid parameter: field type is not a string field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + if (encoding != BT_CTF_STRING_ENCODING_UTF8 && + encoding != BT_CTF_STRING_ENCODING_ASCII) { + BT_LOGW("Invalid parameter: unknown string encoding: " + "addr=%p, encoding=%d", ft, encoding); + ret = -1; + goto end; + } + + string_ft->encoding = encoding; + BT_LOGV("Set string field type's encoding: addr=%p, encoding=%s", + ft, bt_ctf_string_encoding_string(encoding)); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *ft) +{ + int ret; + enum bt_ctf_field_type_id type_id; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + + if (ft->frozen) { + ret = (int) ft->alignment; + goto end; + } + + type_id = bt_ctf_field_type_common_get_type_id(ft); + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + { + struct bt_ctf_field_type_common *element_ft = + bt_ctf_field_type_common_sequence_borrow_element_field_type(ft); + + BT_ASSERT(element_ft); + ret = bt_ctf_field_type_common_get_alignment(element_ft); + break; + } + case BT_CTF_FIELD_TYPE_ID_ARRAY: + { + struct bt_ctf_field_type_common *element_ft = + bt_ctf_field_type_common_array_borrow_element_field_type(ft); + + BT_ASSERT(element_ft); + ret = bt_ctf_field_type_common_get_alignment(element_ft); + break; + } + case BT_CTF_FIELD_TYPE_ID_STRUCT: + { + int64_t i, element_count; + + element_count = bt_ctf_field_type_common_structure_get_field_count( + ft); + BT_ASSERT(element_count >= 0); + + for (i = 0; i < element_count; i++) { + struct bt_ctf_field_type_common *field = NULL; + int field_alignment; + + ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + ft, NULL, &field, i); + BT_ASSERT(ret == 0); + BT_ASSERT(field); + field_alignment = bt_ctf_field_type_common_get_alignment( + field); + if (field_alignment < 0) { + ret = field_alignment; + goto end; + } + + ft->alignment = MAX(field_alignment, ft->alignment); + } + ret = (int) ft->alignment; + break; + } + case BT_CTF_FIELD_TYPE_ID_UNKNOWN: + BT_LOGW("Invalid parameter: unknown field type ID: " + "addr=%p, ft-id=%d", ft, type_id); + ret = -1; + break; + default: + ret = (int) ft->alignment; + break; + } + +end: + return ret; +} + +static inline +int is_power_of_two(unsigned int value) +{ + return ((value & (value - 1)) == 0) && value > 0; +} + +BT_HIDDEN +int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft, + unsigned int alignment) +{ + int ret = 0; + enum bt_ctf_field_type_id type_id; + + /* Alignment must be a power of two */ + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (!is_power_of_two(alignment)) { + BT_LOGW("Invalid parameter: alignment is not a power of two: " + "addr=%p, align=%u", ft, alignment); + ret = -1; + goto end; + } + + type_id = bt_ctf_field_type_common_get_type_id(ft); + if (type_id == BT_CTF_FIELD_TYPE_ID_UNKNOWN) { + BT_LOGW("Invalid parameter: unknown field type ID: " + "addr=%p, ft-id=%d", ft, type_id); + ret = -1; + goto end; + } + + if (ft->id == BT_CTF_FIELD_TYPE_ID_STRING && alignment != CHAR_BIT) { + BT_LOGW("Invalid parameter: alignment must be %u for a string field type: " + "addr=%p, align=%u", CHAR_BIT, ft, alignment); + ret = -1; + goto end; + } + + if (type_id == BT_CTF_FIELD_TYPE_ID_VARIANT || + type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE || + type_id == BT_CTF_FIELD_TYPE_ID_ARRAY) { + /* Setting an alignment on these types makes no sense */ + BT_LOGW("Invalid parameter: cannot set the alignment of this field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + ft->alignment = alignment; + ret = 0; + BT_LOGV("Set field type's alignment: addr=%p, align=%u", + ft, alignment); + +end: + return ret; +} + +BT_HIDDEN +enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order( + struct bt_ctf_field_type_common *ft) +{ + enum bt_ctf_byte_order ret = BT_CTF_BYTE_ORDER_UNKNOWN; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_INTEGER: + { + struct bt_ctf_field_type_common_integer *integer = + BT_CTF_FROM_COMMON(ft); + + ret = integer->user_byte_order; + break; + } + case BT_CTF_FIELD_TYPE_ID_ENUM: + { + struct bt_ctf_field_type_common_enumeration *enum_ft = + BT_CTF_FROM_COMMON(ft); + + ret = bt_ctf_field_type_common_get_byte_order( + BT_CTF_TO_COMMON(enum_ft->container_ft)); + break; + } + case BT_CTF_FIELD_TYPE_ID_FLOAT: + { + struct bt_ctf_field_type_common_floating_point *floating_point = + BT_CTF_FROM_COMMON(ft); + ret = floating_point->user_byte_order; + break; + } + default: + BT_LOGW("Invalid parameter: cannot get the byte order of this field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + goto end; + } + + BT_ASSERT(ret == BT_CTF_BYTE_ORDER_NATIVE || + ret == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN || + ret == BT_CTF_BYTE_ORDER_BIG_ENDIAN || + ret == BT_CTF_BYTE_ORDER_NETWORK); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order) +{ + int ret = 0; + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->frozen) { + BT_LOGW("Invalid parameter: field type is frozen: addr=%p", + ft); + ret = -1; + goto end; + } + + if (byte_order != BT_CTF_BYTE_ORDER_NATIVE && + byte_order != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN && + byte_order != BT_CTF_BYTE_ORDER_BIG_ENDIAN && + byte_order != BT_CTF_BYTE_ORDER_NETWORK) { + BT_LOGW("Invalid parameter: invalid byte order: " + "addr=%p, bo=%s", ft, + bt_ctf_byte_order_string(byte_order)); + ret = -1; + goto end; + } + + if (ft->methods->set_byte_order) { + ft->methods->set_byte_order(ft, byte_order); + } + + BT_LOGV("Set field type's byte order: addr=%p, bo=%s", + ft, bt_ctf_byte_order_string(byte_order)); + +end: + return ret; +} + +BT_HIDDEN +enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id( + struct bt_ctf_field_type_common *ft) +{ + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + return ft->id; +} + +BT_HIDDEN +void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft) +{ + if (!ft || ft->frozen) { + return; + } + + BT_ASSERT(ft->methods->freeze); + ft->methods->freeze(ft); +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_signed( + struct bt_ctf_field_type_common_variant *var_ft, + int64_t tag_value) +{ + struct bt_ctf_field_type_common *field_type = NULL; + GQuark field_name_quark; + gpointer index; + struct bt_ctf_field_type_common_variant_choice *choice; + struct range_overlap_query query = { + .range_start._signed = tag_value, + .range_end._signed = tag_value, + .mapping_name = 0, + .overlaps = 0, + }; + + g_ptr_array_foreach(var_ft->tag_ft->entries, check_ranges_overlap, + &query); + if (!query.overlaps) { + goto end; + } + + field_name_quark = query.mapping_name; + if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, + GUINT_TO_POINTER(field_name_quark), NULL, &index)) { + goto end; + } + + choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, + (size_t) index); + field_type = choice->type; + +end: + return field_type; +} + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_unsigned( + struct bt_ctf_field_type_common_variant *var_ft, + uint64_t tag_value) +{ + struct bt_ctf_field_type_common *field_type = NULL; + GQuark field_name_quark; + gpointer index; + struct bt_ctf_field_type_common_variant_choice *choice; + struct range_overlap_query query = { + .range_start._unsigned = tag_value, + .range_end._unsigned = tag_value, + .mapping_name = 0, + .overlaps = 0, + }; + + g_ptr_array_foreach(var_ft->tag_ft->entries, + check_ranges_overlap_unsigned, &query); + if (!query.overlaps) { + goto end; + } + + field_name_quark = query.mapping_name; + if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, + GUINT_TO_POINTER(field_name_quark), NULL, &index)) { + goto end; + } + + choice = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, + (size_t) index); + field_type = choice->type; + +end: + return field_type; +} + +BT_HIDDEN +struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common *ft_copy = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_ASSERT(ft->methods->copy); + ft_copy = ft->methods->copy(ft); + if (!ft_copy) { + BT_LOGE_STR("Cannot copy field type."); + goto end; + } + + ft_copy->alignment = ft->alignment; + +end: + return ft_copy; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_get_field_name_index( + struct bt_ctf_field_type_common *ft, const char *name) +{ + int ret; + size_t index; + GQuark name_quark; + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_STRUCT, + "Field type"); + + name_quark = g_quark_try_string(name); + if (!name_quark) { + BT_LOGV("No such structure field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + ret = -1; + goto end; + } + + if (!g_hash_table_lookup_extended(struct_ft->field_name_to_index, + GUINT_TO_POINTER(name_quark), + NULL, (gpointer *) &index)) { + BT_LOGV("No such structure field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + ret = -1; + goto end; + } + + ret = (int) index; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_get_field_name_index( + struct bt_ctf_field_type_common *ft, const char *name) +{ + int ret; + size_t index; + GQuark name_quark; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + name_quark = g_quark_try_string(name); + if (!name_quark) { + BT_LOGV("No such variant field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + ret = -1; + goto end; + } + + if (!g_hash_table_lookup_extended(var_ft->choice_name_to_index, + GUINT_TO_POINTER(name_quark), + NULL, (gpointer *) &index)) { + BT_LOGV("No such variant field type field name: " + "ft-addr=%p, field-name=\"%s\"", + ft, name); + ret = -1; + goto end; + } + + ret = (int) index; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_set_length_field_path( + struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path) +{ + int ret = 0; + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_SEQUENCE) { + BT_LOGW("Invalid parameter: field type is not a sequence field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + bt_ctf_object_get_ref(path); + BT_CTF_OBJECT_MOVE_REF(seq_ft->length_field_path, path); + BT_LOGV("Set sequence field type's length field path: ft-addr=%p, " + "field-path-addr=%p", ft, path); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_field_path( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_path *path) +{ + int ret = 0; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + ret = -1; + goto end; + } + + if (ft->id != BT_CTF_FIELD_TYPE_ID_VARIANT) { + BT_LOGW("Invalid parameter: field type is not a variant field type: " + "addr=%p, ft-id=%s", ft, + bt_ctf_field_type_id_string(ft->id)); + ret = -1; + goto end; + } + + bt_ctf_object_get_ref(path); + BT_CTF_OBJECT_MOVE_REF(var_ft->tag_field_path, path); + BT_LOGV("Set variant field type's tag field path: ft-addr=%p, " + "field-path-addr=%p", ft, path); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *tag_ft) +{ + int ret = 0; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + if (!ft) { + BT_LOGW_STR("Invalid parameter: variant field type is NULL."); + ret = -1; + goto end; + } + + if (!tag_ft) { + BT_LOGW_STR("Invalid parameter: tag field type is NULL."); + ret = -1; + goto end; + } + + if (tag_ft->id != BT_CTF_FIELD_TYPE_ID_ENUM) { + BT_LOGW("Invalid parameter: tag field type is not an enumeration field type: " + "addr=%p, ft-id=%s", tag_ft, + bt_ctf_field_type_id_string(tag_ft->id)); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(var_ft->tag_ft); + var_ft->tag_ft = bt_ctf_object_get_ref(tag_ft); + BT_LOGV("Set variant field type's tag field type: variant-ft-addr=%p, " + "tag-ft-addr=%p", ft, tag_ft); + +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft) +{ + ft->frozen = 1; +} + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_freeze_recursive( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + BT_LOGD("Freezing enumeration field type object: addr=%p", ft); + bt_ctf_field_type_common_enumeration_set_range_overlap(enum_ft); + bt_ctf_field_type_common_generic_freeze(ft); + BT_LOGD("Freezing enumeration field type object's container field type: int-ft-addr=%p", + enum_ft->container_ft); + bt_ctf_field_type_common_freeze(BT_CTF_TO_COMMON(enum_ft->container_ft)); +} + +BT_HIDDEN +void bt_ctf_field_type_common_structure_freeze_recursive( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + uint64_t i; + + /* Cache the alignment */ + BT_LOGD("Freezing structure field type object: addr=%p", ft); + ft->alignment = bt_ctf_field_type_common_get_alignment(ft); + bt_ctf_field_type_common_generic_freeze(ft); + + for (i = 0; i < struct_ft->fields->len; i++) { + struct bt_ctf_field_type_common_structure_field *field = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(ft, i); + + BT_LOGD("Freezing structure field type field: " + "ft-addr=%p, name=\"%s\"", + field->type, g_quark_to_string(field->name)); + bt_ctf_field_type_common_freeze(field->type); + } +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_update_choices(struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + uint64_t i; + int ret = 0; + bool is_signed; + + if (ft->frozen && var_ft->choices_up_to_date) { + goto end; + } + + BT_ASSERT(var_ft->tag_ft); + is_signed = !!var_ft->tag_ft->container_ft->is_signed; + + for (i = 0; i < var_ft->choices->len; i++) { + struct bt_ctf_field_type_common_variant_choice *choice = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, i); + const char *choice_name = g_quark_to_string(choice->name); + struct bt_ctf_field_type_enumeration_mapping_iterator *iter = + bt_ctf_field_type_common_enumeration_find_mappings_by_name( + BT_CTF_TO_COMMON(var_ft->tag_ft), choice_name); + + if (!iter) { + ret = -1; + goto end; + } + + BT_ASSERT(choice->ranges); + g_array_set_size(choice->ranges, 0); + + while (bt_ctf_field_type_enumeration_mapping_iterator_next(iter) == 0) { + struct bt_ctf_field_type_common_variant_choice_range range; + + if (is_signed) { + ret = bt_ctf_field_type_enumeration_mapping_iterator_signed_get( + iter, NULL, + &range.lower.i, &range.upper.i); + } else { + ret = bt_ctf_field_type_enumeration_mapping_iterator_unsigned_get( + iter, NULL, + &range.lower.u, &range.upper.u); + } + + BT_ASSERT(ret == 0); + g_array_append_val(choice->ranges, range); + } + + bt_ctf_object_put_ref(iter); + } + + var_ft->choices_up_to_date = true; + +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_field_type_common_variant_freeze_recursive( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + uint64_t i; + + BT_LOGD("Freezing variant field type object: addr=%p", ft); + bt_ctf_field_type_common_generic_freeze(ft); + + for (i = 0; i < var_ft->choices->len; i++) { + struct bt_ctf_field_type_common_variant_choice *choice = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(ft, i); + + BT_LOGD("Freezing variant field type member: " + "ft-addr=%p, name=\"%s\"", + choice->type, g_quark_to_string(choice->name)); + bt_ctf_field_type_common_freeze(choice->type); + } +} + +BT_HIDDEN +void bt_ctf_field_type_common_array_freeze_recursive( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + /* Cache the alignment */ + BT_LOGD("Freezing array field type object: addr=%p", ft); + ft->alignment = bt_ctf_field_type_common_get_alignment(ft); + bt_ctf_field_type_common_generic_freeze(ft); + BT_LOGD("Freezing array field type object's element field type: element-ft-addr=%p", + array_ft->element_ft); + bt_ctf_field_type_common_freeze(array_ft->element_ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_freeze_recursive( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + /* Cache the alignment */ + BT_LOGD("Freezing sequence field type object: addr=%p", ft); + ft->alignment = bt_ctf_field_type_common_get_alignment(ft); + bt_ctf_field_type_common_generic_freeze(ft); + BT_LOGD("Freezing sequence field type object's element field type: element-ft-addr=%p", + seq_ft->element_ft); + bt_ctf_field_type_common_freeze(seq_ft->element_ft); +} + +BT_HIDDEN +void bt_ctf_field_type_common_integer_set_byte_order( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) +{ + struct bt_ctf_field_type_common_integer *int_ft = BT_CTF_FROM_COMMON(ft); + + int_ft->user_byte_order = byte_order; +} + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = BT_CTF_FROM_COMMON(ft); + + bt_ctf_field_type_common_set_byte_order(BT_CTF_TO_COMMON(enum_ft->container_ft), + byte_order); +} + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_set_byte_order( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order) +{ + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + + flt_ft->user_byte_order = byte_order; +} + +BT_HIDDEN +void bt_ctf_field_type_common_structure_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order) +{ + int i; + struct bt_ctf_field_type_common_structure *struct_ft = BT_CTF_FROM_COMMON(ft); + + for (i = 0; i < struct_ft->fields->len; i++) { + struct bt_ctf_field_type_common_structure_field *field = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + struct_ft, i); + struct bt_ctf_field_type_common *field_type = field->type; + + bt_ctf_field_type_common_set_byte_order(field_type, byte_order); + } +} + +BT_HIDDEN +void bt_ctf_field_type_common_variant_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order) +{ + int i; + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + for (i = 0; i < var_ft->choices->len; i++) { + struct bt_ctf_field_type_common_variant_choice *choice = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + var_ft, i); + struct bt_ctf_field_type_common *field_type = choice->type; + + bt_ctf_field_type_common_set_byte_order(field_type, byte_order); + } +} + +BT_HIDDEN +void bt_ctf_field_type_common_array_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order) +{ + struct bt_ctf_field_type_common_array *array_ft = BT_CTF_FROM_COMMON(ft); + + bt_ctf_field_type_common_set_byte_order(array_ft->element_ft, byte_order); +} + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + bt_ctf_field_type_common_set_byte_order(seq_ft->element_ft, byte_order); +} + + +BT_HIDDEN +int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + struct bt_ctf_field_type_common_integer *int_ft_a = BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_integer *int_ft_b = BT_CTF_FROM_COMMON(ft_b); + + /* Length */ + if (int_ft_a->size != int_ft_b->size) { + BT_LOGV("Integer field types differ: different sizes: " + "ft-a-size=%u, ft-b-size=%u", + int_ft_a->size, int_ft_b->size); + goto end; + } + + /* Byte order */ + if (int_ft_a->user_byte_order != int_ft_b->user_byte_order) { + BT_LOGV("Integer field types differ: different byte orders: " + "ft-a-bo=%s, ft-b-bo=%s", + bt_ctf_byte_order_string(int_ft_a->user_byte_order), + bt_ctf_byte_order_string(int_ft_b->user_byte_order)); + goto end; + } + + /* Signedness */ + if (int_ft_a->is_signed != int_ft_b->is_signed) { + BT_LOGV("Integer field types differ: different signedness: " + "ft-a-is-signed=%d, ft-b-is-signed=%d", + int_ft_a->is_signed, + int_ft_b->is_signed); + goto end; + } + + /* Base */ + if (int_ft_a->base != int_ft_b->base) { + BT_LOGV("Integer field types differ: different bases: " + "ft-a-base=%s, ft-b-base=%s", + bt_ctf_integer_base_string(int_ft_a->base), + bt_ctf_integer_base_string(int_ft_b->base)); + goto end; + } + + /* Encoding */ + if (int_ft_a->encoding != int_ft_b->encoding) { + BT_LOGV("Integer field types differ: different encodings: " + "ft-a-encoding=%s, ft-b-encoding=%s", + bt_ctf_string_encoding_string(int_ft_a->encoding), + bt_ctf_string_encoding_string(int_ft_b->encoding)); + goto end; + } + + /* Mapped clock class */ + if (int_ft_a->mapped_clock_class) { + if (!int_ft_b->mapped_clock_class) { + BT_LOGV_STR("Integer field types differ: field type A " + "has a mapped clock class, but field type B " + "does not."); + goto end; + } + + if (bt_ctf_clock_class_compare(int_ft_a->mapped_clock_class, + int_ft_b->mapped_clock_class) != 0) { + BT_LOGV_STR("Integer field types differ: different " + "mapped clock classes."); + } + } else { + if (int_ft_b->mapped_clock_class) { + BT_LOGV_STR("Integer field types differ: field type A " + "has no description, but field type B has one."); + goto end; + } + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_compare( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + struct bt_ctf_field_type_common_floating_point *flt_ft_a = + BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_floating_point *flt_ft_b = + BT_CTF_FROM_COMMON(ft_b); + + /* Byte order */ + if (flt_ft_a->user_byte_order != flt_ft_b->user_byte_order) { + BT_LOGV("Floating point number field types differ: different byte orders: " + "ft-a-bo=%s, ft-b-bo=%s", + bt_ctf_byte_order_string(flt_ft_a->user_byte_order), + bt_ctf_byte_order_string(flt_ft_b->user_byte_order)); + goto end; + } + + /* Exponent length */ + if (flt_ft_a->exp_dig != flt_ft_b->exp_dig) { + BT_LOGV("Floating point number field types differ: different exponent sizes: " + "ft-a-exp-size=%u, ft-b-exp-size=%u", + flt_ft_a->exp_dig, flt_ft_b->exp_dig); + goto end; + } + + /* Mantissa length */ + if (flt_ft_a->mant_dig != flt_ft_b->mant_dig) { + BT_LOGV("Floating point number field types differ: different mantissa sizes: " + "ft-a-mant-size=%u, ft-b-mant-size=%u", + flt_ft_a->mant_dig, flt_ft_b->mant_dig); + goto end; + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +static +int compare_enumeration_mappings(struct bt_ctf_enumeration_mapping *mapping_a, + struct bt_ctf_enumeration_mapping *mapping_b) +{ + int ret = 1; + + /* Label */ + if (mapping_a->string != mapping_b->string) { + BT_LOGV("Enumeration field type mappings differ: different names: " + "mapping-a-name=\"%s\", mapping-b-name=\"%s\"", + g_quark_to_string(mapping_a->string), + g_quark_to_string(mapping_b->string)); + goto end; + } + + /* Range start */ + if (mapping_a->range_start._unsigned != + mapping_b->range_start._unsigned) { + BT_LOGV("Enumeration field type mappings differ: different starts of range: " + "mapping-a-range-start-unsigned=%" PRIu64 ", " + "mapping-b-range-start-unsigned=%" PRIu64, + mapping_a->range_start._unsigned, + mapping_b->range_start._unsigned); + goto end; + } + + /* Range end */ + if (mapping_a->range_end._unsigned != + mapping_b->range_end._unsigned) { + BT_LOGV("Enumeration field type mappings differ: different ends of range: " + "mapping-a-range-end-unsigned=%" PRIu64 ", " + "mapping-b-range-end-unsigned=%" PRIu64, + mapping_a->range_end._unsigned, + mapping_b->range_end._unsigned); + goto end; + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + int i; + struct bt_ctf_field_type_common_enumeration *enum_ft_a = + BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_enumeration *enum_ft_b = + BT_CTF_FROM_COMMON(ft_b); + + /* Container field type */ + ret = bt_ctf_field_type_common_compare( + BT_CTF_TO_COMMON(enum_ft_a->container_ft), + BT_CTF_TO_COMMON(enum_ft_b->container_ft)); + if (ret) { + BT_LOGV("Enumeration field types differ: different container field types: " + "ft-a-container-ft-addr=%p, ft-b-container-ft-addr=%p", + enum_ft_a->container_ft, enum_ft_b->container_ft); + goto end; + } + + ret = 1; + + /* Entries */ + if (enum_ft_a->entries->len != enum_ft_b->entries->len) { + goto end; + } + + for (i = 0; i < enum_ft_a->entries->len; ++i) { + struct bt_ctf_enumeration_mapping *mapping_a = + g_ptr_array_index(enum_ft_a->entries, i); + struct bt_ctf_enumeration_mapping *mapping_b = + g_ptr_array_index(enum_ft_b->entries, i); + + if (compare_enumeration_mappings(mapping_a, mapping_b)) { + BT_LOGV("Enumeration field types differ: different mappings: " + "ft-a-mapping-addr=%p, ft-b-mapping-addr=%p, " + "ft-a-mapping-name=\"%s\", ft-b-mapping-name=\"%s\"", + mapping_a, mapping_b, + g_quark_to_string(mapping_a->string), + g_quark_to_string(mapping_b->string)); + goto end; + } + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + struct bt_ctf_field_type_common_string *string_ft_a = BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_string *string_ft_b = BT_CTF_FROM_COMMON(ft_b); + + /* Encoding */ + if (string_ft_a->encoding != string_ft_b->encoding) { + BT_LOGV("String field types differ: different encodings: " + "ft-a-encoding=%s, ft-b-encoding=%s", + bt_ctf_string_encoding_string(string_ft_a->encoding), + bt_ctf_string_encoding_string(string_ft_b->encoding)); + goto end; + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +static +int compare_structure_variant_members( + struct bt_ctf_field_type_common *member_a_ft, + struct bt_ctf_field_type_common *member_b_ft, + GQuark member_a_name, GQuark member_b_name) +{ + int ret = 1; + + /* Label */ + if (member_a_name != member_b_name) { + BT_LOGV("Structure/variant field type fields differ: different names: " + "field-a-name=%s, field-b-name=%s", + g_quark_to_string(member_a_name), + g_quark_to_string(member_b_name)); + goto end; + } + + /* Type */ + ret = bt_ctf_field_type_common_compare(member_a_ft, member_b_ft); + if (ret == 1) { + BT_LOGV("Structure/variant field type fields differ: different field types: " + "field-name=\"%s\", field-a-ft-addr=%p, field-b-ft-addr=%p", + g_quark_to_string(member_a_name), + member_a_ft, member_b_ft); + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_structure_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + int i; + struct bt_ctf_field_type_common_structure *struct_ft_a = + BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_structure *struct_ft_b = + BT_CTF_FROM_COMMON(ft_b); + + /* Alignment */ + if (bt_ctf_field_type_common_get_alignment(ft_a) != + bt_ctf_field_type_common_get_alignment(ft_b)) { + BT_LOGV("Structure field types differ: different alignments: " + "ft-a-align=%u, ft-b-align=%u", + bt_ctf_field_type_common_get_alignment(ft_a), + bt_ctf_field_type_common_get_alignment(ft_b)); + goto end; + } + + /* Fields */ + if (struct_ft_a->fields->len != struct_ft_b->fields->len) { + BT_LOGV("Structure field types differ: different field counts: " + "ft-a-field-count=%u, ft-b-field-count=%u", + struct_ft_a->fields->len, struct_ft_b->fields->len); + goto end; + } + + for (i = 0; i < struct_ft_a->fields->len; ++i) { + struct bt_ctf_field_type_common_structure_field *field_a = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + struct_ft_a, i); + struct bt_ctf_field_type_common_structure_field *field_b = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + struct_ft_b, i); + + ret = compare_structure_variant_members(field_a->type, + field_b->type, field_a->name, field_b->name); + if (ret) { + /* compare_structure_variant_members() logs what differs */ + BT_LOGV_STR("Structure field types differ: different fields."); + goto end; + } + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_variant_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + int i; + struct bt_ctf_field_type_common_variant *var_ft_a = BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_variant *var_ft_b = BT_CTF_FROM_COMMON(ft_b); + + /* Tag name */ + if (strcmp(var_ft_a->tag_name->str, var_ft_b->tag_name->str)) { + BT_LOGV("Variant field types differ: different tag field names: " + "ft-a-tag-field-name=\"%s\", ft-b-tag-field-name=\"%s\"", + var_ft_a->tag_name->str, var_ft_b->tag_name->str); + goto end; + } + + /* Tag type */ + ret = bt_ctf_field_type_common_compare(BT_CTF_TO_COMMON(var_ft_a->tag_ft), + BT_CTF_TO_COMMON(var_ft_b->tag_ft)); + if (ret) { + BT_LOGV("Variant field types differ: different tag field types: " + "ft-a-tag-ft-addr=%p, ft-b-tag-ft-addr=%p", + var_ft_a->tag_ft, var_ft_b->tag_ft); + goto end; + } + + ret = 1; + + /* Fields */ + if (var_ft_a->choices->len != var_ft_b->choices->len) { + BT_LOGV("Variant field types differ: different field counts: " + "ft-a-field-count=%u, ft-b-field-count=%u", + var_ft_a->choices->len, var_ft_b->choices->len); + goto end; + } + + for (i = 0; i < var_ft_a->choices->len; ++i) { + struct bt_ctf_field_type_common_variant_choice *choice_a = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + var_ft_a, i); + struct bt_ctf_field_type_common_variant_choice *choice_b = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + var_ft_b, i); + + ret = compare_structure_variant_members(choice_a->type, + choice_b->type, choice_a->name, choice_b->name); + if (ret) { + /* compare_structure_variant_members() logs what differs */ + BT_LOGV_STR("Variant field types differ: different fields."); + goto end; + } + } + + /* Equal */ + ret = 0; + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_array_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + struct bt_ctf_field_type_common_array *array_ft_a = BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_array *array_ft_b = BT_CTF_FROM_COMMON(ft_b); + + /* Length */ + if (array_ft_a->length != array_ft_b->length) { + BT_LOGV("Structure field types differ: different lengths: " + "ft-a-length=%u, ft-b-length=%u", + array_ft_a->length, array_ft_b->length); + goto end; + } + + /* Element type */ + ret = bt_ctf_field_type_common_compare(array_ft_a->element_ft, + array_ft_b->element_ft); + if (ret == 1) { + BT_LOGV("Array field types differ: different element field types: " + "ft-a-element-ft-addr=%p, ft-b-element-ft-addr=%p", + array_ft_a->element_ft, array_ft_b->element_ft); + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = -1; + struct bt_ctf_field_type_common_sequence *seq_ft_a = BT_CTF_FROM_COMMON(ft_a); + struct bt_ctf_field_type_common_sequence *seq_ft_b = BT_CTF_FROM_COMMON(ft_b); + + /* Length name */ + if (strcmp(seq_ft_a->length_field_name->str, + seq_ft_b->length_field_name->str)) { + BT_LOGV("Sequence field types differ: different length field names: " + "ft-a-length-field-name=\"%s\", " + "ft-b-length-field-name=\"%s\"", + seq_ft_a->length_field_name->str, + seq_ft_b->length_field_name->str); + goto end; + } + + /* Element type */ + ret = bt_ctf_field_type_common_compare(seq_ft_a->element_ft, + seq_ft_b->element_ft); + if (ret == 1) { + BT_LOGV("Sequence field types differ: different element field types: " + "ft-a-element-ft-addr=%p, ft-b-element-ft-addr=%p", + seq_ft_a->element_ft, seq_ft_b->element_ft); + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b) +{ + int ret = 1; + + BT_CTF_ASSERT_PRE_NON_NULL(ft_a, "Field type A"); + BT_CTF_ASSERT_PRE_NON_NULL(ft_b, "Field type B"); + + if (ft_a == ft_b) { + /* Same reference: equal (even if both are NULL) */ + ret = 0; + goto end; + } + + if (!ft_a) { + BT_LOGW_STR("Invalid parameter: field type A is NULL."); + ret = -1; + goto end; + } + + if (!ft_b) { + BT_LOGW_STR("Invalid parameter: field type B is NULL."); + ret = -1; + goto end; + } + + if (ft_a->id != ft_b->id) { + /* Different type IDs */ + BT_LOGV("Field types differ: different IDs: " + "ft-a-addr=%p, ft-b-addr=%p, " + "ft-a-id=%s, ft-b-id=%s", + ft_a, ft_b, + bt_ctf_field_type_id_string(ft_a->id), + bt_ctf_field_type_id_string(ft_b->id)); + goto end; + } + + if (ft_a->id == BT_CTF_FIELD_TYPE_ID_UNKNOWN) { + /* Both have unknown type IDs */ + BT_LOGW_STR("Invalid parameter: field type IDs are unknown."); + goto end; + } + + BT_ASSERT(ft_a->methods->compare); + ret = ft_a->methods->compare(ft_a, ft_b); + if (ret == 1) { + BT_LOGV("Field types differ: ft-a-addr=%p, ft-b-addr=%p", + ft_a, ft_b); + } + +end: + return ret; +} + +BT_HIDDEN +int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft) +{ + int64_t field_count = -1; + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_STRUCT: + field_count = + bt_ctf_field_type_common_structure_get_field_count(ft); + break; + case BT_CTF_FIELD_TYPE_ID_VARIANT: + field_count = + bt_ctf_field_type_common_variant_get_field_count(ft); + break; + case BT_CTF_FIELD_TYPE_ID_ARRAY: + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + /* + * Array and sequence types always contain a single member + * (the element type). + */ + field_count = 1; + break; + default: + break; + } + + return field_count; +} + +BT_HIDDEN +struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index( + struct bt_ctf_field_type_common *ft, int index) +{ + struct bt_ctf_field_type_common *field_type = NULL; + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_STRUCT: + { + int ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + ft, NULL, &field_type, index); + if (ret) { + field_type = NULL; + goto end; + } + break; + } + case BT_CTF_FIELD_TYPE_ID_VARIANT: + { + int ret = bt_ctf_field_type_common_variant_borrow_field_by_index( + ft, NULL, &field_type, index); + if (ret) { + field_type = NULL; + goto end; + } + break; + } + case BT_CTF_FIELD_TYPE_ID_ARRAY: + field_type = + bt_ctf_field_type_common_array_borrow_element_field_type(ft); + break; + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + field_type = + bt_ctf_field_type_common_sequence_borrow_element_field_type(ft); + break; + default: + break; + } + +end: + return field_type; +} + +BT_HIDDEN +int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft, + const char *name) +{ + int field_index = -1; + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_STRUCT: + field_index = bt_ctf_field_type_common_structure_get_field_name_index( + ft, name); + break; + case BT_CTF_FIELD_TYPE_ID_VARIANT: + field_index = bt_ctf_field_type_common_variant_get_field_name_index( + ft, name); + break; + default: + break; + } + + return field_index; +} + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_variant *var_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + return var_ft->tag_field_path; +} + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path( + struct bt_ctf_field_type_common *ft) +{ + struct bt_ctf_field_type_common_sequence *seq_ft = BT_CTF_FROM_COMMON(ft); + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_SEQUENCE, + "Field type"); + return seq_ft->length_field_path; +} + +BT_HIDDEN +int bt_ctf_field_type_common_validate_single_clock_class( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class **expected_clock_class) +{ + int ret = 0; + + if (!ft) { + goto end; + } + + BT_ASSERT(expected_clock_class); + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_INTEGER: + { + struct bt_ctf_clock_class *mapped_clock_class = + bt_ctf_field_type_common_integer_borrow_mapped_clock_class(ft); + + if (!mapped_clock_class) { + goto end; + } + + if (!*expected_clock_class) { + /* Move reference to output parameter */ + *expected_clock_class = bt_ctf_object_get_ref(mapped_clock_class); + mapped_clock_class = NULL; + BT_LOGV("Setting expected clock class: " + "expected-clock-class-addr=%p", + *expected_clock_class); + } else { + if (mapped_clock_class != *expected_clock_class) { + BT_LOGW("Integer field type is not mapped to " + "the expected clock class: " + "mapped-clock-class-addr=%p, " + "mapped-clock-class-name=\"%s\", " + "expected-clock-class-addr=%p, " + "expected-clock-class-name=\"%s\"", + mapped_clock_class, + bt_ctf_clock_class_get_name(mapped_clock_class), + *expected_clock_class, + bt_ctf_clock_class_get_name(*expected_clock_class)); + bt_ctf_object_put_ref(mapped_clock_class); + ret = -1; + goto end; + } + } + + break; + } + case BT_CTF_FIELD_TYPE_ID_ENUM: + case BT_CTF_FIELD_TYPE_ID_ARRAY: + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + { + struct bt_ctf_field_type_common *sub_ft = NULL; + + switch (ft->id) { + case BT_CTF_FIELD_TYPE_ID_ENUM: + sub_ft = bt_ctf_field_type_common_enumeration_borrow_container_field_type( + ft); + break; + case BT_CTF_FIELD_TYPE_ID_ARRAY: + sub_ft = bt_ctf_field_type_common_array_borrow_element_field_type( + ft); + break; + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + sub_ft = bt_ctf_field_type_common_sequence_borrow_element_field_type( + ft); + break; + default: + BT_LOGF("Unexpected field type ID: id=%d", ft->id); + abort(); + } + + BT_ASSERT(sub_ft); + ret = bt_ctf_field_type_common_validate_single_clock_class(sub_ft, + expected_clock_class); + break; + } + case BT_CTF_FIELD_TYPE_ID_STRUCT: + { + uint64_t i; + int64_t count = bt_ctf_field_type_common_structure_get_field_count( + ft); + + for (i = 0; i < count; i++) { + const char *name; + struct bt_ctf_field_type_common *member_type; + + ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + ft, &name, &member_type, i); + BT_ASSERT(ret == 0); + ret = bt_ctf_field_type_common_validate_single_clock_class( + member_type, expected_clock_class); + if (ret) { + BT_LOGW("Structure field type's field's type " + "is not recursively mapped to the " + "expected clock class: " + "field-ft-addr=%p, field-name=\"%s\"", + member_type, name); + goto end; + } + } + break; + } + case BT_CTF_FIELD_TYPE_ID_VARIANT: + { + uint64_t i; + int64_t count = bt_ctf_field_type_common_variant_get_field_count( + ft); + + for (i = 0; i < count; i++) { + const char *name; + struct bt_ctf_field_type_common *member_type; + + ret = bt_ctf_field_type_common_variant_borrow_field_by_index( + ft, &name, &member_type, i); + BT_ASSERT(ret == 0); + ret = bt_ctf_field_type_common_validate_single_clock_class( + member_type, expected_clock_class); + if (ret) { + BT_LOGW("Variant field type's field's type " + "is not recursively mapped to the " + "expected clock class: " + "field-ft-addr=%p, field-name=\"%s\"", + member_type, name); + goto end; + } + } + break; + } + default: + break; + } + +end: + return ret; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_integer_copy( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_enumeration_copy_recursive( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_floating_point_copy( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_structure_copy_recursive( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_variant_copy_recursive( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_array_copy_recursive( + struct bt_ctf_field_type *ft); + +static +struct bt_ctf_field_type *bt_ctf_field_type_sequence_copy_recursive( + struct bt_ctf_field_type *type); + +static +struct bt_ctf_field_type *bt_ctf_field_type_string_copy( + struct bt_ctf_field_type *type); + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_integer_methods = { + .freeze = bt_ctf_field_type_common_generic_freeze, + .validate = bt_ctf_field_type_common_integer_validate, + .set_byte_order = bt_ctf_field_type_common_integer_set_byte_order, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_integer_copy, + .compare = bt_ctf_field_type_common_integer_compare, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_floating_point_methods = { + .freeze = bt_ctf_field_type_common_generic_freeze, + .validate = NULL, + .set_byte_order = bt_ctf_field_type_common_floating_point_set_byte_order, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_floating_point_copy, + .compare = bt_ctf_field_type_common_floating_point_compare, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_enumeration_methods = { + .freeze = bt_ctf_field_type_common_enumeration_freeze_recursive, + .validate = bt_ctf_field_type_common_enumeration_validate_recursive, + .set_byte_order = bt_ctf_field_type_common_enumeration_set_byte_order_recursive, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_enumeration_copy_recursive, + .compare = bt_ctf_field_type_common_enumeration_compare_recursive, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_string_methods = { + .freeze = bt_ctf_field_type_common_generic_freeze, + .validate = NULL, + .set_byte_order = NULL, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_string_copy, + .compare = bt_ctf_field_type_common_string_compare, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_array_methods = { + .freeze = bt_ctf_field_type_common_array_freeze_recursive, + .validate = bt_ctf_field_type_common_array_validate_recursive, + .set_byte_order = bt_ctf_field_type_common_array_set_byte_order_recursive, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_array_copy_recursive, + .compare = bt_ctf_field_type_common_array_compare_recursive, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_sequence_methods = { + .freeze = bt_ctf_field_type_common_sequence_freeze_recursive, + .validate = bt_ctf_field_type_common_sequence_validate_recursive, + .set_byte_order = bt_ctf_field_type_common_sequence_set_byte_order_recursive, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_sequence_copy_recursive, + .compare = bt_ctf_field_type_common_sequence_compare_recursive, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_structure_methods = { + .freeze = bt_ctf_field_type_common_structure_freeze_recursive, + .validate = bt_ctf_field_type_common_structure_validate_recursive, + .set_byte_order = bt_ctf_field_type_common_structure_set_byte_order_recursive, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_structure_copy_recursive, + .compare = bt_ctf_field_type_common_structure_compare_recursive, +}; + +static struct bt_ctf_field_type_common_methods bt_ctf_field_type_variant_methods = { + .freeze = bt_ctf_field_type_common_variant_freeze_recursive, + .validate = bt_ctf_field_type_common_variant_validate_recursive, + .set_byte_order = bt_ctf_field_type_common_variant_set_byte_order_recursive, + .copy = (bt_ctf_field_type_common_method_copy) + bt_ctf_field_type_variant_copy_recursive, + .compare = bt_ctf_field_type_common_variant_compare_recursive, +}; + +typedef int (*bt_ctf_field_type_serialize_func)(struct bt_ctf_field_type_common *, + struct metadata_context *); + +BT_HIDDEN +int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type, + struct metadata_context *context) +{ + int ret; + struct bt_ctf_field_type_common *type_common = (void *) type; + bt_ctf_field_type_serialize_func serialize_func; + + BT_ASSERT(type); + BT_ASSERT(context); + + /* Make sure field type is valid before serializing it */ + ret = bt_ctf_field_type_common_validate((void *) type); + if (ret) { + BT_LOGW("Cannot serialize field type's metadata: field type is invalid: " + "addr=%p", type); + goto end; + } + + serialize_func = type_common->spec.writer.serialize_func; + ret = serialize_func((void *) type, context); + +end: + return ret; +} + +static +const char *get_encoding_string(enum bt_ctf_string_encoding encoding) +{ + const char *encoding_string; + + switch (encoding) { + case BT_CTF_STRING_ENCODING_NONE: + encoding_string = "none"; + break; + case BT_CTF_STRING_ENCODING_ASCII: + encoding_string = "ASCII"; + break; + case BT_CTF_STRING_ENCODING_UTF8: + encoding_string = "UTF8"; + break; + default: + encoding_string = "unknown"; + break; + } + + return encoding_string; +} + +static +const char *get_integer_base_string(enum bt_ctf_integer_base base) +{ + const char *base_string; + + switch (base) { + case BT_CTF_INTEGER_BASE_DECIMAL: + case BT_CTF_INTEGER_BASE_UNSPECIFIED: + base_string = "decimal"; + break; + case BT_CTF_INTEGER_BASE_HEXADECIMAL: + base_string = "hexadecimal"; + break; + case BT_CTF_INTEGER_BASE_OCTAL: + base_string = "octal"; + break; + case BT_CTF_INTEGER_BASE_BINARY: + base_string = "binary"; + break; + default: + base_string = "unknown"; + break; + } + + return base_string; +} + +static +void append_field_name(struct metadata_context *context, + const char *name) +{ + g_string_append_c(context->string, ' '); + + if (!bt_ctf_identifier_is_valid(name) || *name == '_') { + g_string_append_c(context->string, '_'); + } + + g_string_append(context->string, name); +} + +static +int bt_ctf_field_type_integer_serialize(struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + struct bt_ctf_field_type_common_integer *integer = BT_CTF_FROM_COMMON(type); + int ret = 0; + + BT_LOGD("Serializing CTF writer integer field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + g_string_append_printf(context->string, + "integer { size = %u; align = %u; signed = %s; encoding = %s; base = %s; byte_order = %s", + integer->size, type->alignment, + (integer->is_signed ? "true" : "false"), + get_encoding_string(integer->encoding), + get_integer_base_string(integer->base), + bt_ctf_get_byte_order_string(integer->user_byte_order)); + if (integer->mapped_clock_class) { + const char *clock_name = bt_ctf_clock_class_get_name( + integer->mapped_clock_class); + + BT_ASSERT(clock_name); + g_string_append_printf(context->string, + "; map = clock.%s.value", clock_name); + } + + g_string_append(context->string, "; }"); + return ret; +} + +static +int bt_ctf_field_type_enumeration_serialize_recursive( + struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + size_t entry; + int ret; + struct bt_ctf_field_type_common_enumeration *enumeration = + BT_CTF_FROM_COMMON(type); + struct bt_ctf_field_type_common *container_type; + int container_signed; + + BT_LOGD("Serializing CTF writer enumeration field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + container_type = + bt_ctf_field_type_common_enumeration_borrow_container_field_type(type); + BT_ASSERT(container_type); + container_signed = bt_ctf_field_type_common_integer_is_signed( + container_type); + BT_ASSERT(container_signed >= 0); + g_string_append(context->string, "enum : "); + BT_LOGD_STR("Serializing CTF writer enumeration field type's container field type's metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) enumeration->container_ft, context); + if (ret) { + BT_LOGW("Cannot serialize CTF writer enumeration field type's container field type's metadata: " + "container-ft-addr=%p", enumeration->container_ft); + goto end; + } + + g_string_append(context->string, " { "); + for (entry = 0; entry < enumeration->entries->len; entry++) { + struct bt_ctf_enumeration_mapping *mapping = + enumeration->entries->pdata[entry]; + const char *label = g_quark_to_string(mapping->string); + + g_string_append(context->string, "\""); + + if (!bt_ctf_identifier_is_valid(label) || label[0] == '_') { + g_string_append(context->string, "_"); + } + + g_string_append_printf(context->string, "%s\" = ", label); + + if (container_signed) { + if (mapping->range_start._signed == + mapping->range_end._signed) { + g_string_append_printf(context->string, + "%" PRId64, + mapping->range_start._signed); + } else { + g_string_append_printf(context->string, + "%" PRId64 " ... %" PRId64, + mapping->range_start._signed, + mapping->range_end._signed); + } + } else { + if (mapping->range_start._unsigned == + mapping->range_end._unsigned) { + g_string_append_printf(context->string, + "%" PRIu64, + mapping->range_start._unsigned); + } else { + g_string_append_printf(context->string, + "%" PRIu64 " ... %" PRIu64, + mapping->range_start._unsigned, + mapping->range_end._unsigned); + } + } + + g_string_append(context->string, + ((entry != (enumeration->entries->len - 1)) ? + ", " : " }")); + } + + if (context->field_name->len) { + append_field_name(context, + context->field_name->str); + g_string_assign(context->field_name, ""); + } + +end: + return ret; +} + +static +int bt_ctf_field_type_floating_point_serialize(struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + struct bt_ctf_field_type_common_floating_point *floating_point = + BT_CTF_FROM_COMMON(type); + + BT_LOGD("Serializing CTF writer floating point number field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + g_string_append_printf(context->string, + "floating_point { exp_dig = %u; mant_dig = %u; byte_order = %s; align = %u; }", + floating_point->exp_dig, + floating_point->mant_dig, + bt_ctf_get_byte_order_string(floating_point->user_byte_order), + type->alignment); + return 0; +} + +static +int bt_ctf_field_type_structure_serialize_recursive( + struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + size_t i; + unsigned int indent; + int ret = 0; + struct bt_ctf_field_type_common_structure *structure = BT_CTF_FROM_COMMON(type); + GString *structure_field_name = context->field_name; + + BT_LOGD("Serializing CTF writer structure field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + context->field_name = g_string_new(""); + + context->current_indentation_level++; + g_string_append(context->string, "struct {\n"); + + for (i = 0; i < structure->fields->len; i++) { + struct bt_ctf_field_type_common_structure_field *field = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + structure, i); + + BT_LOGD("Serializing CTF writer structure field type's field metadata: " + "index=%zu, " + "field-ft-addr=%p, field-name=\"%s\"", + i, field, g_quark_to_string(field->name)); + + for (indent = 0; indent < context->current_indentation_level; + indent++) { + g_string_append_c(context->string, '\t'); + } + + g_string_assign(context->field_name, + g_quark_to_string(field->name)); + ret = bt_ctf_field_type_serialize_recursive( + (void *) field->type, context); + if (ret) { + BT_LOGW("Cannot serialize CTF writer structure field type's field's metadata: " + "index=%zu, " + "field-ft-addr=%p, field-name=\"%s\"", + i, field->type, + g_quark_to_string(field->name)); + goto end; + } + + if (context->field_name->len) { + append_field_name(context, + context->field_name->str); + } + g_string_append(context->string, ";\n"); + } + + context->current_indentation_level--; + for (indent = 0; indent < context->current_indentation_level; + indent++) { + g_string_append_c(context->string, '\t'); + } + + g_string_append_printf(context->string, "} align(%u)", + type->alignment); + +end: + g_string_free(context->field_name, TRUE); + context->field_name = structure_field_name; + return ret; +} + +static +int bt_ctf_field_type_variant_serialize_recursive( + struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + size_t i; + unsigned int indent; + int ret = 0; + struct bt_ctf_field_type_common_variant *variant = BT_CTF_FROM_COMMON(type); + GString *variant_field_name = context->field_name; + + BT_LOGD("Serializing CTF writer variant field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + context->field_name = g_string_new(""); + if (variant->tag_name->len > 0) { + g_string_append(context->string, "variant <"); + append_field_name(context, variant->tag_name->str); + g_string_append(context->string, "> {\n"); + } else { + g_string_append(context->string, "variant {\n"); + } + + context->current_indentation_level++; + for (i = 0; i < variant->choices->len; i++) { + struct bt_ctf_field_type_common_variant_choice *field = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + variant, i); + + BT_LOGD("Serializing CTF writer variant field type's field metadata: " + "index=%zu, " + "field-ft-addr=%p, field-name=\"%s\"", + i, field, g_quark_to_string(field->name)); + + g_string_assign(context->field_name, + g_quark_to_string(field->name)); + for (indent = 0; indent < context->current_indentation_level; + indent++) { + g_string_append_c(context->string, '\t'); + } + + g_string_assign(context->field_name, + g_quark_to_string(field->name)); + ret = bt_ctf_field_type_serialize_recursive( + (void *) field->type, context); + if (ret) { + BT_LOGW("Cannot serialize CTF writer variant field type's field's metadata: " + "index=%zu, " + "field-ft-addr=%p, field-name=\"%s\"", + i, field->type, + g_quark_to_string(field->name)); + goto end; + } + + if (context->field_name->len) { + append_field_name(context, + context->field_name->str); + g_string_append_c(context->string, ';'); + } + + g_string_append_c(context->string, '\n'); + } + + context->current_indentation_level--; + for (indent = 0; indent < context->current_indentation_level; + indent++) { + g_string_append_c(context->string, '\t'); + } + + g_string_append(context->string, "}"); + +end: + g_string_free(context->field_name, TRUE); + context->field_name = variant_field_name; + return ret; +} + +static +int bt_ctf_field_type_array_serialize_recursive( + struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + int ret = 0; + struct bt_ctf_field_type_common_array *array = BT_CTF_FROM_COMMON(type); + + BT_LOGD("Serializing CTF writer array field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + BT_LOGD_STR("Serializing CTF writer array field type's element field type's metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) array->element_ft, context); + if (ret) { + BT_LOGW("Cannot serialize CTF writer array field type's element field type's metadata: " + "element-ft-addr=%p", array->element_ft); + goto end; + } + + if (context->field_name->len) { + append_field_name(context, + context->field_name->str); + + g_string_append_printf(context->string, "[%u]", array->length); + g_string_assign(context->field_name, ""); + } else { + g_string_append_printf(context->string, "[%u]", array->length); + } + +end: + return ret; +} + +static +int bt_ctf_field_type_sequence_serialize_recursive( + struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + int ret = 0; + struct bt_ctf_field_type_common_sequence *sequence = BT_CTF_FROM_COMMON(type); + + BT_LOGD("Serializing CTF writer sequence field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + BT_LOGD_STR("Serializing CTF writer sequence field type's element field type's metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) sequence->element_ft, context); + if (ret) { + BT_LOGW("Cannot serialize CTF writer sequence field type's element field type's metadata: " + "element-ft-addr=%p", sequence->element_ft); + goto end; + } + + if (context->field_name->len) { + append_field_name(context, context->field_name->str); + g_string_assign(context->field_name, ""); + } + g_string_append(context->string, "["); + append_field_name(context, sequence->length_field_name->str); + g_string_append(context->string, "]"); + +end: + return ret; +} + +static +int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type_common *type, + struct metadata_context *context) +{ + struct bt_ctf_field_type_common_string *string = BT_CTF_FROM_COMMON(type); + + BT_LOGD("Serializing CTF writer string field type's metadata: " + "ft-addr=%p, metadata-context-addr=%p", type, context); + g_string_append_printf(context->string, + "string { encoding = %s; }", + get_encoding_string(string->encoding)); + return 0; +} + +struct bt_ctf_field_type *bt_ctf_field_type_integer_create(unsigned int size) +{ + struct bt_ctf_field_type_common_integer *integer = NULL; + + BT_LOGD("Creating CTF writer integer field type object: size=%u", size); + + if (size == 0 || size > 64) { + BT_LOGW("Invalid parameter: size must be between 1 and 64: " + "size=%u", size); + goto error; + } + + integer = g_new0(struct bt_ctf_field_type_common_integer, 1); + if (!integer) { + BT_LOGE_STR("Failed to allocate one integer field type."); + goto error; + } + + bt_ctf_field_type_common_integer_initialize(BT_CTF_TO_COMMON(integer), + size, bt_ctf_field_type_common_integer_destroy, + &bt_ctf_field_type_integer_methods); + integer->common.spec.writer.serialize_func = + bt_ctf_field_type_integer_serialize; + BT_LOGD("Created CTF writer integer field type object: addr=%p, size=%u", + integer, size); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(integer); + +end: + return (void *) integer; +} + +int bt_ctf_field_type_integer_get_size(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_integer_get_size((void *) ft); +} + +bt_bool bt_ctf_field_type_integer_is_signed(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_integer_is_signed((void *) ft); +} + +int bt_ctf_field_type_integer_set_is_signed(struct bt_ctf_field_type *ft, + bt_bool is_signed) +{ + return bt_ctf_field_type_common_integer_set_is_signed((void *) ft, + is_signed); +} + +int bt_ctf_field_type_integer_set_size(struct bt_ctf_field_type *ft, + unsigned int size) +{ + return bt_ctf_field_type_common_integer_set_size((void *) ft, size); +} + +enum bt_ctf_integer_base bt_ctf_field_type_integer_get_base( + struct bt_ctf_field_type *ft) +{ + return (int) bt_ctf_field_type_common_integer_get_base((void *) ft); +} + +int bt_ctf_field_type_integer_set_base(struct bt_ctf_field_type *ft, + enum bt_ctf_integer_base base) +{ + return bt_ctf_field_type_common_integer_set_base((void *) ft, + (int) base); +} + +enum bt_ctf_string_encoding bt_ctf_field_type_integer_get_encoding( + struct bt_ctf_field_type *ft) +{ + return (int) bt_ctf_field_type_common_integer_get_encoding((void *) ft); +} + +int bt_ctf_field_type_integer_set_encoding(struct bt_ctf_field_type *ft, + enum bt_ctf_string_encoding encoding) +{ + return bt_ctf_field_type_common_integer_set_encoding((void *) ft, + (int) encoding); +} + +struct bt_ctf_clock_class *bt_ctf_field_type_integer_get_mapped_clock_class( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_integer_borrow_mapped_clock_class( + (void *) ft)); +} + +int bt_ctf_field_type_integer_set_mapped_clock_class( + struct bt_ctf_field_type *ft, + struct bt_ctf_clock_class *clock_class) +{ + return bt_ctf_field_type_common_integer_set_mapped_clock_class((void *) ft, + clock_class); +} + +int bt_ctf_field_type_enumeration_signed_get_mapping_by_index( + struct bt_ctf_field_type *ft, uint64_t index, + const char **mapping_name, int64_t *range_begin, + int64_t *range_end) +{ + return bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( + (void *) ft, index, mapping_name, range_begin, range_end); +} + +int bt_ctf_field_type_enumeration_unsigned_get_mapping_by_index( + struct bt_ctf_field_type *ft, uint64_t index, + const char **mapping_name, uint64_t *range_begin, + uint64_t *range_end) +{ + return bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( + (void *) ft, index, mapping_name, range_begin, range_end); +} + +struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create( + struct bt_ctf_field_type *container_ft) +{ + struct bt_ctf_field_type_common_enumeration *enumeration = NULL; + struct bt_ctf_field_type_common *int_ft = (void *) container_ft; + + BT_LOGD("Creating CTF writer enumeration field type object: int-ft-addr=%p", + container_ft); + + if (!container_ft) { + BT_LOGW_STR("Invalid parameter: field type is NULL."); + goto error; + } + + if (int_ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid parameter: container field type is not an integer field type: " + "container-ft-addr=%p, container-ft-id=%s", + container_ft, bt_ctf_field_type_id_string(int_ft->id)); + goto error; + } + + enumeration = g_new0(struct bt_ctf_field_type_common_enumeration, 1); + if (!enumeration) { + BT_LOGE_STR("Failed to allocate one enumeration field type."); + goto error; + } + + bt_ctf_field_type_common_enumeration_initialize(BT_CTF_TO_COMMON(enumeration), + int_ft, bt_ctf_field_type_common_enumeration_destroy_recursive, + &bt_ctf_field_type_enumeration_methods); + enumeration->common.spec.writer.serialize_func = + bt_ctf_field_type_enumeration_serialize_recursive; + BT_LOGD("Created CTF writer enumeration field type object: addr=%p, " + "int-ft-addr=%p, int-ft-size=%u", + enumeration, container_ft, + bt_ctf_field_type_integer_get_size(container_ft)); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(enumeration); + +end: + return (void *) enumeration; +} + +struct bt_ctf_field_type *bt_ctf_field_type_enumeration_get_container_field_type( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_object_get_ref( + bt_ctf_field_type_common_enumeration_borrow_container_field_type( + (void *) ft)); +} + +int bt_ctf_field_type_enumeration_signed_add_mapping( + struct bt_ctf_field_type *ft, const char *string, + int64_t range_start, int64_t range_end) +{ + return bt_ctf_field_type_common_enumeration_signed_add_mapping( + (void *) ft, string, range_start, range_end); +} + +int bt_ctf_field_type_enumeration_unsigned_add_mapping( + struct bt_ctf_field_type *ft, const char *string, + uint64_t range_start, uint64_t range_end) +{ + return bt_ctf_field_type_common_enumeration_unsigned_add_mapping( + (void *) ft, string, range_start, range_end); +} + +int64_t bt_ctf_field_type_enumeration_get_mapping_count( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_enumeration_get_mapping_count((void *) ft); +} + +struct bt_ctf_field_type *bt_ctf_field_type_floating_point_create(void) +{ + struct bt_ctf_field_type_common_floating_point *floating_point = + g_new0(struct bt_ctf_field_type_common_floating_point, 1); + + BT_LOGD_STR("Creating CTF writer floating point number field type object."); + + if (!floating_point) { + BT_LOGE_STR("Failed to allocate one floating point number field type."); + goto end; + } + + bt_ctf_field_type_common_floating_point_initialize( + BT_CTF_TO_COMMON(floating_point), + bt_ctf_field_type_common_floating_point_destroy, + &bt_ctf_field_type_floating_point_methods); + floating_point->common.spec.writer.serialize_func = + bt_ctf_field_type_floating_point_serialize; + BT_LOGD("Created CTF writer floating point number field type object: addr=%p, " + "exp-size=%u, mant-size=%u", floating_point, + floating_point->exp_dig, floating_point->mant_dig); + +end: + return (void *) floating_point; +} + +int bt_ctf_field_type_floating_point_get_exponent_digits( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_floating_point_get_exponent_digits( + (void *) ft); +} + +int bt_ctf_field_type_floating_point_set_exponent_digits( + struct bt_ctf_field_type *ft, unsigned int exponent_digits) +{ + return bt_ctf_field_type_common_floating_point_set_exponent_digits( + (void *) ft, exponent_digits); +} + +int bt_ctf_field_type_floating_point_get_mantissa_digits( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_floating_point_get_mantissa_digits( + (void *) ft); +} + +int bt_ctf_field_type_floating_point_set_mantissa_digits( + struct bt_ctf_field_type *ft, unsigned int mantissa_digits) +{ + return bt_ctf_field_type_common_floating_point_set_mantissa_digits( + (void *) ft, mantissa_digits); +} + +struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void) +{ + struct bt_ctf_field_type_common_structure *structure = + g_new0(struct bt_ctf_field_type_common_structure, 1); + + BT_LOGD_STR("Creating CTF writer structure field type object."); + + if (!structure) { + BT_LOGE_STR("Failed to allocate one structure field type."); + goto error; + } + + bt_ctf_field_type_common_structure_initialize(BT_CTF_TO_COMMON(structure), + bt_ctf_field_type_common_structure_destroy_recursive, + &bt_ctf_field_type_structure_methods); + structure->common.spec.writer.serialize_func = + bt_ctf_field_type_structure_serialize_recursive; + BT_LOGD("Created CTF writer structure field type object: addr=%p", + structure); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(structure); + +end: + return (void *) structure; +} + +int bt_ctf_field_type_structure_add_field(struct bt_ctf_field_type *ft, + struct bt_ctf_field_type *field_type, + const char *field_name) +{ + return bt_ctf_field_type_common_structure_add_field((void *) ft, + (void *) field_type, field_name); +} + +int64_t bt_ctf_field_type_structure_get_field_count(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_structure_get_field_count((void *) ft); +} + +int bt_ctf_field_type_structure_get_field_by_index( + struct bt_ctf_field_type *ft, + const char **field_name, + struct bt_ctf_field_type **field_type, uint64_t index) +{ + int ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + (void *) ft, field_name, (void *) field_type, index); + + if (ret == 0 && field_type) { + bt_ctf_object_get_ref(*field_type); + } + + return ret; +} + +struct bt_ctf_field_type *bt_ctf_field_type_structure_get_field_type_by_name( + struct bt_ctf_field_type *ft, const char *name) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_structure_borrow_field_type_by_name( + (void *) ft, name)); +} + +struct bt_ctf_field_type *bt_ctf_field_type_variant_create( + struct bt_ctf_field_type *tag_ft, const char *tag_name) +{ + struct bt_ctf_field_type_common_variant *var_ft = NULL; + + BT_LOGD("Creating CTF writer variant field type object: " + "tag-ft-addr=%p, tag-field-name=\"%s\"", + tag_ft, tag_name); + + if (tag_name && !bt_ctf_identifier_is_valid(tag_name)) { + BT_LOGW("Invalid parameter: tag field name is not a valid CTF identifier: " + "tag-ft-addr=%p, tag-field-name=\"%s\"", + tag_ft, tag_name); + goto error; + } + + var_ft = g_new0(struct bt_ctf_field_type_common_variant, 1); + if (!var_ft) { + BT_LOGE_STR("Failed to allocate one variant field type."); + goto error; + } + + bt_ctf_field_type_common_variant_initialize(BT_CTF_TO_COMMON(var_ft), + (void *) tag_ft, tag_name, + bt_ctf_field_type_common_variant_destroy_recursive, + &bt_ctf_field_type_variant_methods); + var_ft->common.spec.writer.serialize_func = + bt_ctf_field_type_variant_serialize_recursive; + BT_LOGD("Created CTF writer variant field type object: addr=%p, " + "tag-ft-addr=%p, tag-field-name=\"%s\"", + var_ft, tag_ft, tag_name); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(var_ft); + +end: + return (void *) var_ft; +} + +struct bt_ctf_field_type *bt_ctf_field_type_variant_get_tag_field_type( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_variant_borrow_tag_field_type( + (void *) ft)); +} + +const char *bt_ctf_field_type_variant_get_tag_name(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_variant_get_tag_name((void *) ft); +} + +int bt_ctf_field_type_variant_set_tag_name( + struct bt_ctf_field_type *ft, const char *name) +{ + return bt_ctf_field_type_common_variant_set_tag_name((void *) ft, name); +} + +int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *ft, + struct bt_ctf_field_type *field_type, + const char *field_name) +{ + return bt_ctf_field_type_common_variant_add_field((void *) ft, + (void *) field_type, field_name); +} + +struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_by_name( + struct bt_ctf_field_type *ft, + const char *field_name) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_variant_borrow_field_type_by_name( + (void *) ft, field_name)); +} + +struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type_from_tag( + struct bt_ctf_field_type *ft, + struct bt_ctf_field *tag_field) +{ + int ret; + int64_t choice_index; + struct bt_ctf_field *container; + struct bt_ctf_field_type_common_variant *var_ft = (void *) ft; + struct bt_ctf_field_type *ret_ft = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(ft, "Field type"); + BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(ft, BT_CTF_FIELD_TYPE_ID_VARIANT, + "Field type"); + BT_CTF_ASSERT_PRE_NON_NULL(tag_field, "Tag field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( + (struct bt_ctf_field_common *) tag_field, + BT_CTF_FIELD_TYPE_ID_ENUM, "Tag field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((struct bt_ctf_field_common *) tag_field, + "Tag field"); + + container = bt_ctf_field_enumeration_borrow_container(tag_field); + BT_ASSERT(container); + + if (var_ft->tag_ft->container_ft->is_signed) { + int64_t val; + + ret = bt_ctf_field_integer_signed_get_value(container, + &val); + BT_ASSERT(ret == 0); + choice_index = bt_ctf_field_type_common_variant_find_choice_index( + (void *) ft, (uint64_t) val, true); + } else { + uint64_t val; + + ret = bt_ctf_field_integer_unsigned_get_value(container, + &val); + BT_ASSERT(ret == 0); + choice_index = bt_ctf_field_type_common_variant_find_choice_index( + (void *) ft, val, false); + } + + if (choice_index < 0) { + BT_LOGW("Cannot find variant field type's field: " + "var-ft-addr=%p, tag-field-addr=%p", ft, tag_field); + goto end; + } + + ret = bt_ctf_field_type_variant_get_field_by_index(ft, NULL, + &ret_ft, choice_index); + BT_ASSERT(ret == 0); + +end: + return ret_ft; +} + +int64_t bt_ctf_field_type_variant_get_field_count(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_variant_get_field_count((void *) ft); +} + +int bt_ctf_field_type_variant_get_field_by_index(struct bt_ctf_field_type *ft, + const char **field_name, struct bt_ctf_field_type **field_type, + uint64_t index) +{ + int ret = bt_ctf_field_type_common_variant_borrow_field_by_index( + (void *) ft, field_name, (void *) field_type, index); + + if (ret == 0 && field_type) { + bt_ctf_object_get_ref(*field_type); + } + + return ret; +} + +struct bt_ctf_field_type *bt_ctf_field_type_array_create( + struct bt_ctf_field_type *element_ft, unsigned int length) +{ + struct bt_ctf_field_type_common_array *array = NULL; + + BT_LOGD("Creating CTF writer array field type object: element-ft-addr=%p, " + "length=%u", element_ft, length); + + if (!element_ft) { + BT_LOGW_STR("Invalid parameter: element field type is NULL."); + goto error; + } + + if (length == 0) { + BT_LOGW_STR("Invalid parameter: length is zero."); + goto error; + } + + array = g_new0(struct bt_ctf_field_type_common_array, 1); + if (!array) { + BT_LOGE_STR("Failed to allocate one array field type."); + goto error; + } + + bt_ctf_field_type_common_array_initialize(BT_CTF_TO_COMMON(array), + (void *) element_ft, length, + bt_ctf_field_type_common_array_destroy_recursive, + &bt_ctf_field_type_array_methods); + array->common.spec.writer.serialize_func = + bt_ctf_field_type_array_serialize_recursive; + BT_LOGD("Created CTF writer array field type object: addr=%p, " + "element-ft-addr=%p, length=%u", + array, element_ft, length); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(array); + +end: + return (void *) array; +} + +struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_field_type( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_array_borrow_element_field_type( + (void *) ft)); +} + +int64_t bt_ctf_field_type_array_get_length(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_array_get_length((void *) ft); +} + +struct bt_ctf_field_type *bt_ctf_field_type_sequence_create( + struct bt_ctf_field_type *element_ft, + const char *length_field_name) +{ + struct bt_ctf_field_type_common_sequence *sequence = NULL; + + BT_LOGD("Creating CTF writer sequence field type object: element-ft-addr=%p, " + "length-field-name=\"%s\"", element_ft, length_field_name); + + if (!element_ft) { + BT_LOGW_STR("Invalid parameter: element field type is NULL."); + goto error; + } + + if (!bt_ctf_identifier_is_valid(length_field_name)) { + BT_LOGW("Invalid parameter: length field name is not a valid CTF identifier: " + "length-field-name=\"%s\"", length_field_name); + goto error; + } + + sequence = g_new0(struct bt_ctf_field_type_common_sequence, 1); + if (!sequence) { + BT_LOGE_STR("Failed to allocate one sequence field type."); + goto error; + } + + bt_ctf_field_type_common_sequence_initialize(BT_CTF_TO_COMMON(sequence), + (void *) element_ft, length_field_name, + bt_ctf_field_type_common_sequence_destroy_recursive, + &bt_ctf_field_type_sequence_methods); + sequence->common.spec.writer.serialize_func = + bt_ctf_field_type_sequence_serialize_recursive; + BT_LOGD("Created CTF writer sequence field type object: addr=%p, " + "element-ft-addr=%p, length-field-name=\"%s\"", + sequence, element_ft, length_field_name); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(sequence); + +end: + return (void *) sequence; +} + +struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_field_type( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_object_get_ref(bt_ctf_field_type_common_sequence_borrow_element_field_type( + (void *) ft)); +} + +const char *bt_ctf_field_type_sequence_get_length_field_name( + struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_sequence_get_length_field_name((void *) ft); +} + +struct bt_ctf_field_type *bt_ctf_field_type_string_create(void) +{ + struct bt_ctf_field_type_common_string *string = + g_new0(struct bt_ctf_field_type_common_string, 1); + + BT_LOGD_STR("Creating CTF writer string field type object."); + + if (!string) { + BT_LOGE_STR("Failed to allocate one string field type."); + return NULL; + } + + bt_ctf_field_type_common_string_initialize(BT_CTF_TO_COMMON(string), + bt_ctf_field_type_common_string_destroy, + &bt_ctf_field_type_string_methods); + string->common.spec.writer.serialize_func = + bt_ctf_field_type_string_serialize; + BT_LOGD("Created CTF writer string field type object: addr=%p", string); + return (void *) string; +} + +enum bt_ctf_string_encoding bt_ctf_field_type_string_get_encoding( + struct bt_ctf_field_type *ft) +{ + return (int) bt_ctf_field_type_common_string_get_encoding((void *) ft); +} + +int bt_ctf_field_type_string_set_encoding(struct bt_ctf_field_type *ft, + enum bt_ctf_string_encoding encoding) +{ + return bt_ctf_field_type_common_string_set_encoding((void *) ft, + (int) encoding); +} + +int bt_ctf_field_type_get_alignment(struct bt_ctf_field_type *ft) +{ + return bt_ctf_field_type_common_get_alignment((void *) ft); +} + +int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *ft, + unsigned int alignment) +{ + return bt_ctf_field_type_common_set_alignment((void *) ft, alignment); +} + +enum bt_ctf_byte_order bt_ctf_field_type_get_byte_order( + struct bt_ctf_field_type *ft) +{ + return (int) bt_ctf_field_type_common_get_byte_order((void *) ft); +} + +int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *ft, + enum bt_ctf_byte_order byte_order) +{ + return bt_ctf_field_type_common_set_byte_order((void *) ft, + (int) byte_order); +} + +enum bt_ctf_field_type_id bt_ctf_field_type_get_type_id( + struct bt_ctf_field_type *ft) +{ + return (int) bt_ctf_field_type_common_get_type_id((void *) ft); +} + +BT_HIDDEN +struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft) +{ + return (void *) bt_ctf_field_type_common_copy((void *) ft); +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_integer_copy( + struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_type_common_integer *int_ft = (void *) ft; + struct bt_ctf_field_type_common_integer *copy_ft; + + BT_LOGD("Copying CTF writer integer field type's: addr=%p", ft); + copy_ft = (void *) bt_ctf_field_type_integer_create(int_ft->size); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer integer field type."); + goto end; + } + + copy_ft->mapped_clock_class = bt_ctf_object_get_ref(int_ft->mapped_clock_class); + copy_ft->user_byte_order = int_ft->user_byte_order; + copy_ft->is_signed = int_ft->is_signed; + copy_ft->size = int_ft->size; + copy_ft->base = int_ft->base; + copy_ft->encoding = int_ft->encoding; + BT_LOGD("Copied CTF writer integer field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + return (void *) copy_ft; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_enumeration_copy_recursive( + struct bt_ctf_field_type *ft) +{ + size_t i; + struct bt_ctf_field_type_common_enumeration *enum_ft = (void *) ft; + struct bt_ctf_field_type_common_enumeration *copy_ft = NULL; + struct bt_ctf_field_type_common_enumeration *container_copy_ft; + + BT_LOGD("Copying CTF writer enumeration field type's: addr=%p", ft); + + /* Copy the source enumeration's container */ + BT_LOGD_STR("Copying CTF writer enumeration field type's container field type."); + container_copy_ft = BT_CTF_FROM_COMMON(bt_ctf_field_type_common_copy( + BT_CTF_TO_COMMON(enum_ft->container_ft))); + if (!container_copy_ft) { + BT_LOGE_STR("Cannot copy CTF writer enumeration field type's container field type."); + goto end; + } + + copy_ft = (void *) bt_ctf_field_type_enumeration_create( + (void *) container_copy_ft); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer enumeration field type."); + goto end; + } + + /* Copy all enumaration entries */ + for (i = 0; i < enum_ft->entries->len; i++) { + struct bt_ctf_enumeration_mapping *mapping = g_ptr_array_index( + enum_ft->entries, i); + struct bt_ctf_enumeration_mapping *copy_mapping = g_new0( + struct bt_ctf_enumeration_mapping, 1); + + if (!copy_mapping) { + BT_LOGE_STR("Failed to allocate one enumeration mapping."); + goto error; + } + + *copy_mapping = *mapping; + g_ptr_array_add(copy_ft->entries, copy_mapping); + } + + BT_LOGD("Copied CTF writer enumeration field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + bt_ctf_object_put_ref(container_copy_ft); + return (void *) copy_ft; + +error: + bt_ctf_object_put_ref(container_copy_ft); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); + return (void *) copy_ft; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_floating_point_copy( + struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_type_common_floating_point *flt_ft = BT_CTF_FROM_COMMON(ft); + struct bt_ctf_field_type_common_floating_point *copy_ft; + + BT_LOGD("Copying CTF writer floating point number field type's: addr=%p", ft); + copy_ft = (void *) bt_ctf_field_type_floating_point_create(); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer floating point number field type."); + goto end; + } + + copy_ft->user_byte_order = flt_ft->user_byte_order; + copy_ft->exp_dig = flt_ft->exp_dig; + copy_ft->mant_dig = flt_ft->mant_dig; + BT_LOGD("Copied CTF writer floating point number field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + return (void *) copy_ft; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_structure_copy_recursive( + struct bt_ctf_field_type *ft) +{ + int64_t i; + GHashTableIter iter; + gpointer key, value; + struct bt_ctf_field_type_common_structure *struct_ft = (void *) ft; + struct bt_ctf_field_type_common_structure *copy_ft; + + BT_LOGD("Copying CTF writer structure field type's: addr=%p", ft); + copy_ft = (void *) bt_ctf_field_type_structure_create(); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer structure field type."); + goto end; + } + + /* Copy field_name_to_index */ + g_hash_table_iter_init(&iter, struct_ft->field_name_to_index); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_hash_table_insert(copy_ft->field_name_to_index, + key, value); + } + + g_array_set_size(copy_ft->fields, struct_ft->fields->len); + + for (i = 0; i < struct_ft->fields->len; i++) { + struct bt_ctf_field_type_common_structure_field *entry, *copy_entry; + struct bt_ctf_field_type_common *field_ft_copy; + + entry = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + struct_ft, i); + copy_entry = BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + copy_ft, i); + BT_LOGD("Copying CTF writer structure field type's field: " + "index=%" PRId64 ", " + "field-ft-addr=%p, field-name=\"%s\"", + i, entry, g_quark_to_string(entry->name)); + + field_ft_copy = (void *) bt_ctf_field_type_copy( + (void *) entry->type); + if (!field_ft_copy) { + BT_LOGE("Cannot copy CTF writer structure field type's field: " + "index=%" PRId64 ", " + "field-ft-addr=%p, field-name=\"%s\"", + i, entry, g_quark_to_string(entry->name)); + goto error; + } + + copy_entry->name = entry->name; + copy_entry->type = field_ft_copy; + } + + BT_LOGD("Copied CTF writer structure field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + return (void *) copy_ft; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); + return NULL; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_variant_copy_recursive( + struct bt_ctf_field_type *ft) +{ + int64_t i; + GHashTableIter iter; + gpointer key, value; + struct bt_ctf_field_type_common *tag_ft_copy = NULL; + struct bt_ctf_field_type_common_variant *var_ft = (void *) ft; + struct bt_ctf_field_type_common_variant *copy_ft = NULL; + + BT_LOGD("Copying CTF writer variant field type's: addr=%p", ft); + if (var_ft->tag_ft) { + BT_LOGD_STR("Copying CTF writer variant field type's tag field type."); + tag_ft_copy = bt_ctf_field_type_common_copy( + BT_CTF_TO_COMMON(var_ft->tag_ft)); + if (!tag_ft_copy) { + BT_LOGE_STR("Cannot copy CTF writer variant field type's tag field type."); + goto end; + } + } + + copy_ft = (void *) bt_ctf_field_type_variant_create( + (void *) tag_ft_copy, + var_ft->tag_name->len ? var_ft->tag_name->str : NULL); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer variant field type."); + goto end; + } + + /* Copy field_name_to_index */ + g_hash_table_iter_init(&iter, var_ft->choice_name_to_index); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_hash_table_insert(copy_ft->choice_name_to_index, + key, value); + } + + g_array_set_size(copy_ft->choices, var_ft->choices->len); + + for (i = 0; i < var_ft->choices->len; i++) { + struct bt_ctf_field_type_common_variant_choice *entry, *copy_entry; + struct bt_ctf_field_type_common *field_ft_copy; + uint64_t range_i; + + entry = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(var_ft, i); + copy_entry = BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + copy_ft, i); + BT_LOGD("Copying CTF writer variant field type's field: " + "index=%" PRId64 ", " + "field-ft-addr=%p, field-name=\"%s\"", + i, entry, g_quark_to_string(entry->name)); + + field_ft_copy = (void *) bt_ctf_field_type_copy( + (void *) entry->type); + if (!field_ft_copy) { + BT_LOGE("Cannot copy CTF writer variant field type's field: " + "index=%" PRId64 ", " + "field-ft-addr=%p, field-name=\"%s\"", + i, entry, g_quark_to_string(entry->name)); + g_free(copy_entry); + goto error; + } + + copy_entry->name = entry->name; + copy_entry->type = field_ft_copy; + + /* Copy ranges */ + copy_entry->ranges = g_array_new(FALSE, TRUE, + sizeof(struct bt_ctf_field_type_common_variant_choice_range)); + BT_ASSERT(copy_entry->ranges); + g_array_set_size(copy_entry->ranges, entry->ranges->len); + + for (range_i = 0; range_i < entry->ranges->len; range_i++) { + copy_entry->ranges[range_i] = entry->ranges[range_i]; + } + } + + if (var_ft->tag_field_path) { + BT_LOGD_STR("Copying CTF writer variant field type's tag field path."); + copy_ft->tag_field_path = bt_ctf_field_path_copy( + var_ft->tag_field_path); + if (!copy_ft->tag_field_path) { + BT_LOGE_STR("Cannot copy CTF writer variant field type's tag field path."); + goto error; + } + } + + copy_ft->choices_up_to_date = var_ft->choices_up_to_date; + BT_LOGD("Copied CTF writer variant field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + bt_ctf_object_put_ref(tag_ft_copy); + return (void *) copy_ft; + +error: + bt_ctf_object_put_ref(tag_ft_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); + return NULL; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_array_copy_recursive( + struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_type_common *container_ft_copy = NULL; + struct bt_ctf_field_type_common_array *array_ft = (void *) ft; + struct bt_ctf_field_type_common_array *copy_ft = NULL; + + BT_LOGD("Copying CTF writer array field type's: addr=%p", ft); + BT_LOGD_STR("Copying CTF writer array field type's element field type."); + container_ft_copy = bt_ctf_field_type_common_copy(array_ft->element_ft); + if (!container_ft_copy) { + BT_LOGE_STR("Cannot copy CTF writer array field type's element field type."); + goto end; + } + + copy_ft = (void *) bt_ctf_field_type_array_create( + (void *) container_ft_copy, array_ft->length); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer array field type."); + goto end; + } + + BT_LOGD("Copied CTF writer array field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + bt_ctf_object_put_ref(container_ft_copy); + return (void *) copy_ft; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_sequence_copy_recursive( + struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_type_common *container_ft_copy = NULL; + struct bt_ctf_field_type_common_sequence *seq_ft = (void *) ft; + struct bt_ctf_field_type_common_sequence *copy_ft = NULL; + + BT_LOGD("Copying CTF writer sequence field type's: addr=%p", ft); + BT_LOGD_STR("Copying CTF writer sequence field type's element field type."); + container_ft_copy = bt_ctf_field_type_common_copy(seq_ft->element_ft); + if (!container_ft_copy) { + BT_LOGE_STR("Cannot copy CTF writer sequence field type's element field type."); + goto end; + } + + copy_ft = (void *) bt_ctf_field_type_sequence_create( + (void *) container_ft_copy, + seq_ft->length_field_name->len ? + seq_ft->length_field_name->str : NULL); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer sequence field type."); + goto end; + } + + if (seq_ft->length_field_path) { + BT_LOGD_STR("Copying CTF writer sequence field type's length field path."); + copy_ft->length_field_path = bt_ctf_field_path_copy( + seq_ft->length_field_path); + if (!copy_ft->length_field_path) { + BT_LOGE_STR("Cannot copy CTF writer sequence field type's length field path."); + goto error; + } + } + + BT_LOGD("Copied CTF writer sequence field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + bt_ctf_object_put_ref(container_ft_copy); + return (void *) copy_ft; +error: + bt_ctf_object_put_ref(container_ft_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_ft); + return NULL; +} + +static +struct bt_ctf_field_type *bt_ctf_field_type_string_copy(struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_type_common_string *string_ft = (void *) ft; + struct bt_ctf_field_type_common_string *copy_ft = NULL; + + BT_LOGD("Copying CTF writer string field type's: addr=%p", ft); + copy_ft = (void *) bt_ctf_field_type_string_create(); + if (!copy_ft) { + BT_LOGE_STR("Cannot create CTF writer string field type."); + goto end; + } + + copy_ft->encoding = string_ft->encoding; + BT_LOGD("Copied CTF writer string field type: original-ft-addr=%p, copy-ft-addr=%p", + ft, copy_ft); + +end: + return (void *) copy_ft; +} diff --git a/src/ctf-writer/field-types.h b/src/ctf-writer/field-types.h new file mode 100644 index 00000000..e1f75fd6 --- /dev/null +++ b/src/ctf-writer/field-types.h @@ -0,0 +1,792 @@ +#ifndef BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H +#define BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include +#include + +#include +#include + +#include "common/babeltrace.h" + +#include "assert-pre.h" +#include "clock-class.h" +#include "object.h" +#include "writer.h" + +#define BT_CTF_ASSERT_PRE_CTF_FT_COMMON_HAS_ID(_ft, _type_id, _name) \ + BT_CTF_ASSERT_PRE(((struct bt_ctf_field_type_common *) (_ft))->id == (_type_id), \ + _name " has the wrong type ID: expected-type-id=%s, " \ + "ft-addr=%p", bt_ctf_field_type_id_string(_type_id), (_ft)) + +#define BT_CTF_ASSERT_PRE_CTF_FT_HOT(_ft, _name) \ + BT_CTF_ASSERT_PRE_HOT((_ft), (_name), ": ft-addr=%p", (_ft)) + +#define BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX(_ft, _index) \ + (&g_array_index(((struct bt_ctf_field_type_common_structure *) (_ft))->fields, \ + struct bt_ctf_field_type_common_structure_field, (_index))) + +#define BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX(_ft, _index) \ + (&g_array_index(((struct bt_ctf_field_type_common_variant *) (_ft))->choices, \ + struct bt_ctf_field_type_common_variant_choice, (_index))) + +struct bt_ctf_field_common; +struct bt_ctf_field_type_common; + +typedef void (*bt_ctf_field_type_common_method_freeze)( + struct bt_ctf_field_type_common *); +typedef int (*bt_ctf_field_type_common_method_validate)( + struct bt_ctf_field_type_common *); +typedef void (*bt_ctf_field_type_common_method_set_byte_order)( + struct bt_ctf_field_type_common *, enum bt_ctf_byte_order); +typedef struct bt_ctf_field_type_common *(*bt_ctf_field_type_common_method_copy)( + struct bt_ctf_field_type_common *); +typedef int (*bt_ctf_field_type_common_method_compare)( + struct bt_ctf_field_type_common *, + struct bt_ctf_field_type_common *); + +struct bt_ctf_field_type_common_methods { + bt_ctf_field_type_common_method_freeze freeze; + bt_ctf_field_type_common_method_validate validate; + bt_ctf_field_type_common_method_set_byte_order set_byte_order; + bt_ctf_field_type_common_method_copy copy; + bt_ctf_field_type_common_method_compare compare; +}; + +struct bt_ctf_field_type_common { + struct bt_ctf_object base; + enum bt_ctf_field_type_id id; + unsigned int alignment; + + /* Virtual table */ + struct bt_ctf_field_type_common_methods *methods; + + /* + * A type can't be modified once it is added to an event or after a + * a field has been instanciated from it. + */ + int frozen; + + /* + * This flag indicates if the field type is valid. A valid + * field type is _always_ frozen. All the nested field types of + * a valid field type are also valid (and thus frozen). + */ + int valid; + + /* + * Specialized data for either CTF IR or CTF writer APIs. + * Having this here ensures that: + * + * * The type-specific common data is always found at the same + * offset when the common API has a `struct + * bt_ctf_field_type_common *` so that you can cast it to `struct + * bt_ctf_field_type_common_integer *` for example and access the + * common integer field type fields. + * + * * The specific CTF IR and CTF writer APIs can access their + * specific field type fields in this union at an offset known + * at build time. This avoids a pointer to specific data so + * that all the fields, common or specific, of a CTF IR + * integer field type or of a CTF writer integer field type, + * for example, are contained within the same contiguous block + * of memory. + */ + union { + struct { + } ir; + struct { + void *serialize_func; + } writer; + } spec; +}; + +struct bt_ctf_field_type_common_integer { + struct bt_ctf_field_type_common common; + + /* Owned by this */ + struct bt_ctf_clock_class *mapped_clock_class; + + enum bt_ctf_byte_order user_byte_order; + bt_bool is_signed; + unsigned int size; + enum bt_ctf_integer_base base; + enum bt_ctf_string_encoding encoding; +}; + +struct bt_ctf_enumeration_mapping { + union { + uint64_t _unsigned; + int64_t _signed; + } range_start; + union { + uint64_t _unsigned; + int64_t _signed; + } range_end; + GQuark string; +}; + +struct bt_ctf_field_type_common_enumeration { + struct bt_ctf_field_type_common common; + + /* Owned by this */ + struct bt_ctf_field_type_common_integer *container_ft; + + /* Array of `struct bt_ctf_enumeration_mapping *`, owned by this */ + GPtrArray *entries; + + /* Only set during validation */ + bt_bool has_overlapping_ranges; +}; + +enum bt_ctf_field_type_enumeration_mapping_iterator_type { + CTF_ITERATOR_BY_NAME, + CTF_ITERATOR_BY_SIGNED_VALUE, + CTF_ITERATOR_BY_UNSIGNED_VALUE, +}; + +struct bt_ctf_field_type_enumeration_mapping_iterator { + struct bt_ctf_object base; + + /* Owned by this */ + struct bt_ctf_field_type_common_enumeration *enumeration_ft; + + enum bt_ctf_field_type_enumeration_mapping_iterator_type type; + int index; + union { + GQuark name_quark; + int64_t signed_value; + uint64_t unsigned_value; + } u; +}; + +struct bt_ctf_field_type_common_floating_point { + struct bt_ctf_field_type_common common; + enum bt_ctf_byte_order user_byte_order; + unsigned int exp_dig; + unsigned int mant_dig; +}; + +struct bt_ctf_field_type_common_structure_field { + GQuark name; + + /* Owned by this */ + struct bt_ctf_field_type_common *type; +}; + +struct bt_ctf_field_type_common_structure { + struct bt_ctf_field_type_common common; + GHashTable *field_name_to_index; + + /* + * Array of `struct bt_ctf_field_type_common_structure_field`, + * owned by this + */ + GArray *fields; +}; + +struct bt_ctf_field_type_common_variant_choice_range { + union { + int64_t i; + uint64_t u; + } lower; + union { + int64_t i; + uint64_t u; + } upper; +}; + +struct bt_ctf_field_type_common_variant_choice { + GQuark name; + + /* Owned by this */ + struct bt_ctf_field_type_common *type; + + /* Array of `struct bt_ctf_field_type_common_variant_choice_range` */ + GArray *ranges; +}; + +struct bt_ctf_field_type_common_variant { + struct bt_ctf_field_type_common common; + GString *tag_name; + bool choices_up_to_date; + + /* Owned by this */ + struct bt_ctf_field_type_common_enumeration *tag_ft; + + /* Owned by this */ + struct bt_ctf_field_path *tag_field_path; + + GHashTable *choice_name_to_index; + + /* + * Array of `struct bt_ctf_field_type_common_variant_choice`, + * owned by this */ + GArray *choices; +}; + +struct bt_ctf_field_type_common_array { + struct bt_ctf_field_type_common common; + + /* Owned by this */ + struct bt_ctf_field_type_common *element_ft; + + unsigned int length; +}; + +struct bt_ctf_field_type_common_sequence { + struct bt_ctf_field_type_common common; + + /* Owned by this */ + struct bt_ctf_field_type_common *element_ft; + + GString *length_field_name; + + /* Owned by this */ + struct bt_ctf_field_path *length_field_path; +}; + +struct bt_ctf_field_type_common_string { + struct bt_ctf_field_type_common common; + enum bt_ctf_string_encoding encoding; +}; + +typedef struct bt_ctf_field_common *(* bt_ctf_field_common_create_func)( + struct bt_ctf_field_type_common *); + +BT_HIDDEN +void bt_ctf_field_type_common_initialize(struct bt_ctf_field_type_common *ft, + bool init_bo, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_integer_initialize( + struct bt_ctf_field_type_common *ft, + unsigned int size, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *container_ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_string_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_structure_initialize( + struct bt_ctf_field_type_common *ft, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_array_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft, + unsigned int length, bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft, + const char *length_field_name, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_variant_initialize( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *tag_ft, + const char *tag_name, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_type_common_methods *methods); + +BT_HIDDEN +void bt_ctf_field_type_common_integer_destroy(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_destroy(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_destroy_recursive(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_string_destroy(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_structure_destroy_recursive(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_array_destroy_recursive(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_destroy_recursive(struct bt_ctf_object *obj); + +BT_HIDDEN +void bt_ctf_field_type_common_variant_destroy_recursive(struct bt_ctf_object *obj); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_validate(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_validate_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_validate_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_array_validate_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_validate_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_validate_recursive( + struct bt_ctf_field_type_common *type); + +BT_HIDDEN +int bt_ctf_field_type_common_validate(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_get_size(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +bt_bool bt_ctf_field_type_common_integer_is_signed(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_is_signed(struct bt_ctf_field_type_common *ft, + bt_bool is_signed); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_size(struct bt_ctf_field_type_common *ft, + unsigned int size); + +BT_HIDDEN +enum bt_ctf_integer_base bt_ctf_field_type_common_integer_get_base( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_base(struct bt_ctf_field_type_common *ft, + enum bt_ctf_integer_base base); + +BT_HIDDEN +enum bt_ctf_string_encoding bt_ctf_field_type_common_integer_get_encoding( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_encoding(struct bt_ctf_field_type_common *ft, + enum bt_ctf_string_encoding encoding); + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_field_type_common_integer_borrow_mapped_clock_class( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_set_mapped_clock_class( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_find_mappings_by_name( + struct bt_ctf_field_type_common *ft, const char *name); + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_signed_find_mappings_by_value( + struct bt_ctf_field_type_common *ft, int64_t value); + +BT_HIDDEN +struct bt_ctf_field_type_enumeration_mapping_iterator * +bt_ctf_field_type_common_enumeration_unsigned_find_mappings_by_value( + struct bt_ctf_field_type_common *ft, uint64_t value); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_signed_get_mapping_by_index( + struct bt_ctf_field_type_common *ft, uint64_t index, + const char **mapping_name, int64_t *range_begin, + int64_t *range_end); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_unsigned_get_mapping_by_index( + struct bt_ctf_field_type_common *ft, uint64_t index, + const char **mapping_name, uint64_t *range_begin, + uint64_t *range_end); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_enumeration_borrow_container_field_type( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_signed_add_mapping( + struct bt_ctf_field_type_common *ft, const char *string, + int64_t range_start, int64_t range_end); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_unsigned_add_mapping( + struct bt_ctf_field_type_common *ft, const char *string, + uint64_t range_start, uint64_t range_end); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_enumeration_get_mapping_count( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_get_exponent_digits( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_set_exponent_digits( + struct bt_ctf_field_type_common *ft, + unsigned int exponent_digits); + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_get_mantissa_digits( + struct bt_ctf_field_type_common *type); + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_set_mantissa_digits( + struct bt_ctf_field_type_common *ft, unsigned int mantissa_digits); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_replace_field( + struct bt_ctf_field_type_common *ft, + const char *field_name, + struct bt_ctf_field_type_common *field_type); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_add_field(struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *field_type, + const char *field_name); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_structure_get_field_count( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_borrow_field_by_index( + struct bt_ctf_field_type_common *ft, + const char **field_name, + struct bt_ctf_field_type_common **field_type, uint64_t index); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_structure_borrow_field_type_by_name( + struct bt_ctf_field_type_common *ft, const char *name); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_tag_field_type( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +const char *bt_ctf_field_type_common_variant_get_tag_name( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_name( + struct bt_ctf_field_type_common *ft, const char *name); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_add_field(struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *field_type, + const char *field_name); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_update_choices( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_by_name( + struct bt_ctf_field_type_common *ft, + const char *field_name); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_variant_get_field_count( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_borrow_field_by_index( + struct bt_ctf_field_type_common *ft, + const char **field_name, + struct bt_ctf_field_type_common **field_type, uint64_t index); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_array_borrow_element_field_type( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_array_set_element_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_array_get_length(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_sequence_borrow_element_field_type( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_set_element_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *element_ft); + +BT_HIDDEN +const char *bt_ctf_field_type_common_sequence_get_length_field_name( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +enum bt_ctf_string_encoding bt_ctf_field_type_common_string_get_encoding( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_string_set_encoding(struct bt_ctf_field_type_common *ft, + enum bt_ctf_string_encoding encoding); + +BT_HIDDEN +int bt_ctf_field_type_common_get_alignment(struct bt_ctf_field_type_common *type); + +BT_HIDDEN +int bt_ctf_field_type_common_set_alignment(struct bt_ctf_field_type_common *ft, + unsigned int alignment); + +BT_HIDDEN +enum bt_ctf_byte_order bt_ctf_field_type_common_get_byte_order( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_set_byte_order(struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +enum bt_ctf_field_type_id bt_ctf_field_type_common_get_type_id( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_freeze(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_signed( + struct bt_ctf_field_type_common_variant *var_ft, + int64_t tag_value); + +BT_HIDDEN +struct bt_ctf_field_type_common * +bt_ctf_field_type_common_variant_borrow_field_type_unsigned( + struct bt_ctf_field_type_common_variant *var_ft, + uint64_t tag_value); + +BT_HIDDEN +struct bt_ctf_field_type_common *bt_ctf_field_type_common_copy( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_get_field_name_index( + struct bt_ctf_field_type_common *ft, const char *name); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_get_field_name_index( + struct bt_ctf_field_type_common *ft, const char *name); + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_set_length_field_path( + struct bt_ctf_field_type_common *ft, struct bt_ctf_field_path *path); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_field_path( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_path *path); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_set_tag_field_type( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_field_type_common *tag_ft); + +BT_HIDDEN +void bt_ctf_field_type_common_generic_freeze(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_freeze_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_structure_freeze_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_variant_freeze_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_array_freeze_recursive( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_freeze_recursive( + struct bt_ctf_field_type_common *type); + +BT_HIDDEN +void bt_ctf_field_type_common_integer_set_byte_order( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_enumeration_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_floating_point_set_byte_order( + struct bt_ctf_field_type_common *ft, enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_structure_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_variant_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_array_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_field_type_common_sequence_set_byte_order_recursive( + struct bt_ctf_field_type_common *ft, + enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +int bt_ctf_field_type_common_integer_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_floating_point_compare( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_enumeration_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_string_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_structure_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_variant_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_array_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_sequence_compare_recursive( + struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int bt_ctf_field_type_common_compare(struct bt_ctf_field_type_common *ft_a, + struct bt_ctf_field_type_common *ft_b); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_get_field_count(struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +struct bt_ctf_field_type_common *bt_ctf_field_type_common_borrow_field_at_index( + struct bt_ctf_field_type_common *ft, int index); + +BT_HIDDEN +int bt_ctf_field_type_common_get_field_index(struct bt_ctf_field_type_common *ft, + const char *name); + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_type_common_variant_borrow_tag_field_path( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +struct bt_ctf_field_path *bt_ctf_field_type_common_sequence_borrow_length_field_path( + struct bt_ctf_field_type_common *ft); + +BT_HIDDEN +int bt_ctf_field_type_common_validate_single_clock_class( + struct bt_ctf_field_type_common *ft, + struct bt_ctf_clock_class **expected_clock_class); + +BT_HIDDEN +int64_t bt_ctf_field_type_common_variant_find_choice_index( + struct bt_ctf_field_type_common *ft, uint64_t uval, + bool is_signed); + +BT_HIDDEN +int bt_ctf_field_type_serialize_recursive(struct bt_ctf_field_type *type, + struct metadata_context *context); + +BT_HIDDEN +struct bt_ctf_field_type *bt_ctf_field_type_copy(struct bt_ctf_field_type *ft); + +#endif /* BABELTRACE_CTF_WRITER_FIELD_TYPES_INTERNAL_H */ diff --git a/src/ctf-writer/field-wrapper.c b/src/ctf-writer/field-wrapper.c new file mode 100644 index 00000000..eecd3304 --- /dev/null +++ b/src/ctf-writer/field-wrapper.c @@ -0,0 +1,89 @@ +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-FIELD-WRAPPER" +#include "logging.h" + +#include + +#include "lib/object-pool.h" + +#include "fields.h" +#include "field-wrapper.h" +#include "object.h" + +BT_HIDDEN +struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data) +{ + struct bt_ctf_field_wrapper *field_wrapper = + g_new0(struct bt_ctf_field_wrapper, 1); + + BT_LOGD_STR("Creating empty field wrapper object."); + + if (!field_wrapper) { + BT_LOGE("Failed to allocate one field wrapper."); + goto end; + } + + bt_ctf_object_init_unique(&field_wrapper->base); + BT_LOGD("Created empty field wrapper object: addr=%p", + field_wrapper); + +end: + return field_wrapper; +} + +BT_HIDDEN +void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field_wrapper) +{ + BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper); + BT_ASSERT(!field_wrapper->field); + BT_LOGD_STR("Putting stream class."); + g_free(field_wrapper); +} + +BT_HIDDEN +struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create( + struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft) +{ + struct bt_ctf_field_wrapper *field_wrapper = NULL; + + BT_ASSERT(pool); + BT_ASSERT(ft); + field_wrapper = bt_ctf_object_pool_create_object(pool); + if (!field_wrapper) { + BT_LOGE("Cannot allocate one field wrapper"); + goto error; + } + + BT_ASSERT(field_wrapper->field); + goto end; + +error: + if (field_wrapper) { + bt_ctf_field_wrapper_destroy(field_wrapper); + field_wrapper = NULL; + } + +end: + return field_wrapper; +} diff --git a/src/ctf-writer/field-wrapper.h b/src/ctf-writer/field-wrapper.h new file mode 100644 index 00000000..55aee6cf --- /dev/null +++ b/src/ctf-writer/field-wrapper.h @@ -0,0 +1,49 @@ +#ifndef BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H +#define BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H + +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" + +#include "fields.h" +#include "object.h" +#include "object-pool.h" + +struct bt_ctf_field_wrapper { + struct bt_ctf_object base; + + /* Owned by this */ + struct bt_ctf_field_common *field; +}; + +BT_HIDDEN +struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_new(void *data); + +BT_HIDDEN +void bt_ctf_field_wrapper_destroy(struct bt_ctf_field_wrapper *field); + +BT_HIDDEN +struct bt_ctf_field_wrapper *bt_ctf_field_wrapper_create( + struct bt_ctf_object_pool *pool, struct bt_ctf_field_type *ft); + +#endif /* BABELTRACE_CTF_WRITER_FIELD_WRAPPER_INTERNAL_H */ diff --git a/src/ctf-writer/fields.c b/src/ctf-writer/fields.c new file mode 100644 index 00000000..832f8f2c --- /dev/null +++ b/src/ctf-writer/fields.c @@ -0,0 +1,1862 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-FIELDS" +#include "logging.h" + +#include +#include +#include + +#include + +#include "common/align.h" +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" +#include "compat/fcntl.h" +#include "ctfser/ctfser.h" + +#include "assert-pre.h" +#include "fields.h" +#include "field-types.h" +#include "object.h" + +#define BT_CTF_ASSERT_PRE_CTF_FIELD_IS_INT_OR_ENUM(_field, _name) \ + BT_CTF_ASSERT_PRE((_field)->type->id == BT_CTF_FIELD_TYPE_ID_INTEGER || \ + (_field)->type->id == BT_CTF_FIELD_TYPE_ID_ENUM, \ + _name " is not an integer or an enumeration field: " \ + "field-addr=%p", (_field)) + +BT_HIDDEN +struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common *copy = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT(field_type_common_has_known_id(field->type)); + BT_ASSERT(field->methods->copy); + copy = field->methods->copy(field); + if (!copy) { + BT_LOGW("Cannot create field: ft-addr=%p", field->type); + goto end; + } + + bt_ctf_field_common_set(copy, field->payload_set); + +end: + return copy; +} + +BT_HIDDEN +int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_release_func) +{ + int ret = 0; + struct bt_ctf_field_type_common_structure *structure_type = + BT_CTF_FROM_COMMON(type); + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + size_t i; + + BT_LOGD("Initializing common structure field object: ft-addr=%p", type); + bt_ctf_field_common_initialize(field, type, is_shared, + release_func, methods); + structure->fields = g_ptr_array_new_with_free_func(field_release_func); + g_ptr_array_set_size(structure->fields, structure_type->fields->len); + + /* Create all fields contained in the structure field. */ + for (i = 0; i < structure_type->fields->len; i++) { + struct bt_ctf_field_common *field; + struct bt_ctf_field_type_common_structure_field *struct_field = + BT_CTF_FIELD_TYPE_COMMON_STRUCTURE_FIELD_AT_INDEX( + structure_type, i); + field = field_create_func(struct_field->type); + if (!field) { + BT_LOGE("Failed to create structure field's member: name=\"%s\", index=%zu", + g_quark_to_string(struct_field->name), i); + ret = -1; + goto end; + } + + g_ptr_array_index(structure->fields, i) = field; + } + + BT_LOGD("Initialized common structure field object: addr=%p, ft-addr=%p", + field, type); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_release_func) +{ + int ret = 0; + struct bt_ctf_field_type_common_variant *variant_type = + BT_CTF_FROM_COMMON(type); + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + size_t i; + + BT_LOGD("Initializing common variant field object: ft-addr=%p", type); + bt_ctf_field_common_initialize(field, type, is_shared, + release_func, methods); + ret = bt_ctf_field_type_common_variant_update_choices(type); + if (ret) { + BT_LOGE("Cannot update common variant field type choices: " + "ret=%d", ret); + goto end; + } + + variant->fields = g_ptr_array_new_with_free_func(field_release_func); + g_ptr_array_set_size(variant->fields, variant_type->choices->len); + + /* Create all fields contained in the variant field. */ + for (i = 0; i < variant_type->choices->len; i++) { + struct bt_ctf_field_common *field; + struct bt_ctf_field_type_common_variant_choice *var_choice = + BT_CTF_FIELD_TYPE_COMMON_VARIANT_CHOICE_AT_INDEX( + variant_type, i); + + field = field_create_func(var_choice->type); + if (!field) { + BT_LOGE("Failed to create variant field's member: name=\"%s\", index=%zu", + g_quark_to_string(var_choice->name), i); + ret = -1; + goto end; + } + + g_ptr_array_index(variant->fields, i) = field; + } + + BT_LOGD("Initialized common variant field object: addr=%p, ft-addr=%p", + field, type); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods) +{ + int ret = 0; + struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); + + BT_LOGD("Initializing common string field object: ft-addr=%p", type); + bt_ctf_field_common_initialize(field, type, is_shared, + release_func, methods); + string->buf = g_array_sized_new(FALSE, FALSE, sizeof(char), 1); + if (!string->buf) { + ret = -1; + goto end; + } + + g_array_index(string->buf, char, 0) = '\0'; + BT_LOGD("Initialized common string field object: addr=%p, ft-addr=%p", + field, type); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_destroy_func) +{ + struct bt_ctf_field_type_common_array *array_type = BT_CTF_FROM_COMMON(type); + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + unsigned int array_length; + int ret = 0; + uint64_t i; + + BT_LOGD("Initializing common array field object: ft-addr=%p", type); + BT_ASSERT(type); + bt_ctf_field_common_initialize(field, type, is_shared, + release_func, methods); + array_length = array_type->length; + array->elements = g_ptr_array_sized_new(array_length); + if (!array->elements) { + ret = -1; + goto end; + } + + g_ptr_array_set_free_func(array->elements, field_destroy_func); + g_ptr_array_set_size(array->elements, array_length); + + for (i = 0; i < array_length; i++) { + array->elements->pdata[i] = field_create_func( + array_type->element_ft); + if (!array->elements->pdata[i]) { + ret = -1; + goto end; + } + } + + BT_LOGD("Initialized common array field object: addr=%p, ft-addr=%p", + field, type); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + GDestroyNotify field_destroy_func) +{ + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + int ret = 0; + + BT_LOGD("Initializing common sequence field object: ft-addr=%p", type); + BT_ASSERT(type); + bt_ctf_field_common_initialize(field, type, is_shared, + release_func, methods); + sequence->elements = g_ptr_array_new(); + if (!sequence->elements) { + ret = -1; + goto end; + } + + g_ptr_array_set_free_func(sequence->elements, field_destroy_func); + BT_LOGD("Initialized common sequence field object: addr=%p, ft-addr=%p", + field, type); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field) +{ + return (field && field->payload_set) ? 0 : -1; +} + +BT_HIDDEN +int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field) +{ + int64_t i; + int ret = 0; + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < structure->fields->len; i++) { + ret = bt_ctf_field_common_validate_recursive( + (void *) structure->fields->pdata[i]); + + if (ret) { + int this_ret; + const char *name; + + this_ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + field->type, &name, NULL, i); + BT_ASSERT(this_ret == 0); + BT_CTF_ASSERT_PRE_MSG("Invalid structure field's field: " + "struct-field-addr=%p, field-name=\"%s\", " + "index=%" PRId64 ", field-addr=%p", + field, name, i, structure->fields->pdata[i]); + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field) +{ + int ret = 0; + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + if (!variant->current_field) { + ret = -1; + goto end; + } + + ret = bt_ctf_field_common_validate_recursive(variant->current_field); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field) +{ + int64_t i; + int ret = 0; + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < array->elements->len; i++) { + ret = bt_ctf_field_common_validate_recursive((void *) array->elements->pdata[i]); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid array field's element field: " + "array-field-addr=%p, %" PRId64 ", " + "elem-field-addr=%p", + field, i, array->elements->pdata[i]); + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field) +{ + int64_t i; + int ret = 0; + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < sequence->elements->len; i++) { + ret = bt_ctf_field_common_validate_recursive( + (void *) sequence->elements->pdata[i]); + if (ret) { + BT_CTF_ASSERT_PRE_MSG("Invalid sequence field's element field: " + "seq-field-addr=%p, %" PRId64 ", " + "elem-field-addr=%p", + field, i, sequence->elements->pdata[i]); + goto end; + } + } +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field) +{ + BT_ASSERT(field); + field->payload_set = false; +} + +BT_HIDDEN +void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field) +{ + int64_t i; + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < structure->fields->len; i++) { + struct bt_ctf_field_common *member = structure->fields->pdata[i]; + + if (!member) { + /* + * Structure members are lazily initialized; + * skip if this member has not been allocated + * yet. + */ + continue; + } + + bt_ctf_field_common_reset_recursive(member); + } +} + +BT_HIDDEN +void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + variant->current_field = NULL; +} + +BT_HIDDEN +void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field) +{ + size_t i; + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < array->elements->len; i++) { + struct bt_ctf_field_common *member = array->elements->pdata[i]; + + if (!member) { + /* + * Array elements are lazily initialized; skip + * if this member has not been allocated yet. + */ + continue; + } + + bt_ctf_field_common_reset_recursive(member); + } +} + +BT_HIDDEN +void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + uint64_t i; + + BT_ASSERT(field); + + for (i = 0; i < sequence->elements->len; i++) { + if (sequence->elements->pdata[i]) { + bt_ctf_field_common_reset_recursive( + sequence->elements->pdata[i]); + } + } + + sequence->length = 0; +} + +BT_HIDDEN +void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field, + bool is_frozen) +{ + field->frozen = is_frozen; +} + +BT_HIDDEN +void bt_ctf_field_common_structure_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + uint64_t i; + struct bt_ctf_field_common_structure *structure_field = + BT_CTF_FROM_COMMON(field); + + BT_LOGD("Freezing structure field object: addr=%p", field); + + for (i = 0; i < structure_field->fields->len; i++) { + struct bt_ctf_field_common *struct_field = + g_ptr_array_index(structure_field->fields, i); + + BT_LOGD("Freezing structure field's field: field-addr=%p, index=%" PRId64, + struct_field, i); + bt_ctf_field_common_set_is_frozen_recursive(struct_field, + is_frozen); + } + + bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); +} + +BT_HIDDEN +void bt_ctf_field_common_variant_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + uint64_t i; + struct bt_ctf_field_common_variant *variant_field = BT_CTF_FROM_COMMON(field); + + BT_LOGD("Freezing variant field object: addr=%p", field); + + for (i = 0; i < variant_field->fields->len; i++) { + struct bt_ctf_field_common *var_field = + g_ptr_array_index(variant_field->fields, i); + + BT_LOGD("Freezing variant field's field: field-addr=%p, index=%" PRId64, + var_field, i); + bt_ctf_field_common_set_is_frozen_recursive(var_field, is_frozen); + } + + bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); +} + +BT_HIDDEN +void bt_ctf_field_common_array_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + int64_t i; + struct bt_ctf_field_common_array *array_field = BT_CTF_FROM_COMMON(field); + + BT_LOGD("Freezing array field object: addr=%p", field); + + for (i = 0; i < array_field->elements->len; i++) { + struct bt_ctf_field_common *elem_field = + g_ptr_array_index(array_field->elements, i); + + BT_LOGD("Freezing array field object's element field: " + "element-field-addr=%p, index=%" PRId64, + elem_field, i); + bt_ctf_field_common_set_is_frozen_recursive(elem_field, is_frozen); + } + + bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); +} + +BT_HIDDEN +void bt_ctf_field_common_sequence_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + int64_t i; + struct bt_ctf_field_common_sequence *sequence_field = + BT_CTF_FROM_COMMON(field); + + BT_LOGD("Freezing sequence field object: addr=%p", field); + + for (i = 0; i < sequence_field->length; i++) { + struct bt_ctf_field_common *elem_field = + g_ptr_array_index(sequence_field->elements, i); + + BT_LOGD("Freezing sequence field object's element field: " + "element-field-addr=%p, index=%" PRId64, + elem_field, i); + bt_ctf_field_common_set_is_frozen_recursive(elem_field, is_frozen); + } + + bt_ctf_field_common_generic_set_is_frozen(field, is_frozen); +} + +BT_HIDDEN +void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field, + bool is_frozen) +{ + if (!field) { + goto end; + } + + BT_LOGD("Setting field object's frozen state: addr=%p, is-frozen=%d", + field, is_frozen); + BT_ASSERT(field_type_common_has_known_id(field->type)); + BT_ASSERT(field->methods->set_is_frozen); + field->methods->set_is_frozen(field, is_frozen); + +end: + return; +} + +BT_HIDDEN +bt_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field) +{ + return field && field->payload_set; +} + +BT_HIDDEN +bt_bool bt_ctf_field_common_structure_is_set_recursive( + struct bt_ctf_field_common *field) +{ + bt_bool is_set = BT_FALSE; + size_t i; + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < structure->fields->len; i++) { + is_set = bt_ctf_field_common_is_set_recursive( + structure->fields->pdata[i]); + if (!is_set) { + goto end; + } + } + +end: + return is_set; +} + +BT_HIDDEN +bt_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + bt_bool is_set = BT_FALSE; + + BT_ASSERT(field); + + if (variant->current_field) { + is_set = bt_ctf_field_common_is_set_recursive( + variant->current_field); + } + + return is_set; +} + +BT_HIDDEN +bt_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field) +{ + size_t i; + bt_bool is_set = BT_FALSE; + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + for (i = 0; i < array->elements->len; i++) { + is_set = bt_ctf_field_common_is_set_recursive(array->elements->pdata[i]); + if (!is_set) { + goto end; + } + } + +end: + return is_set; +} + +BT_HIDDEN +bt_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field) +{ + size_t i; + bt_bool is_set = BT_FALSE; + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + + if (!sequence->elements) { + goto end; + } + + for (i = 0; i < sequence->elements->len; i++) { + is_set = bt_ctf_field_common_is_set_recursive( + sequence->elements->pdata[i]); + if (!is_set) { + goto end; + } + } + +end: + return is_set; +} + +static struct bt_ctf_field_common_methods bt_ctf_field_integer_methods = { + .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, + .validate = bt_ctf_field_common_generic_validate, + .copy = NULL, + .is_set = bt_ctf_field_common_generic_is_set, + .reset = bt_ctf_field_common_generic_reset, +}; + +static struct bt_ctf_field_common_methods bt_ctf_field_floating_point_methods = { + .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, + .validate = bt_ctf_field_common_generic_validate, + .copy = NULL, + .is_set = bt_ctf_field_common_generic_is_set, + .reset = bt_ctf_field_common_generic_reset, +}; + +static +void bt_ctf_field_enumeration_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen); + +static +int bt_ctf_field_enumeration_validate_recursive(struct bt_ctf_field_common *field); + +static +bt_bool bt_ctf_field_enumeration_is_set_recursive( + struct bt_ctf_field_common *field); + +static +void bt_ctf_field_enumeration_reset_recursive(struct bt_ctf_field_common *field); + +static struct bt_ctf_field_common_methods bt_ctf_field_enumeration_methods = { + .set_is_frozen = bt_ctf_field_enumeration_set_is_frozen_recursive, + .validate = bt_ctf_field_enumeration_validate_recursive, + .copy = NULL, + .is_set = bt_ctf_field_enumeration_is_set_recursive, + .reset = bt_ctf_field_enumeration_reset_recursive, +}; + +static struct bt_ctf_field_common_methods bt_ctf_field_string_methods = { + .set_is_frozen = bt_ctf_field_common_generic_set_is_frozen, + .validate = bt_ctf_field_common_generic_validate, + .copy = NULL, + .is_set = bt_ctf_field_common_generic_is_set, + .reset = bt_ctf_field_common_generic_reset, +}; + +static struct bt_ctf_field_common_methods bt_ctf_field_structure_methods = { + .set_is_frozen = bt_ctf_field_common_structure_set_is_frozen_recursive, + .validate = bt_ctf_field_common_structure_validate_recursive, + .copy = NULL, + .is_set = bt_ctf_field_common_structure_is_set_recursive, + .reset = bt_ctf_field_common_structure_reset_recursive, +}; + +static struct bt_ctf_field_common_methods bt_ctf_field_sequence_methods = { + .set_is_frozen = bt_ctf_field_common_sequence_set_is_frozen_recursive, + .validate = bt_ctf_field_common_sequence_validate_recursive, + .copy = NULL, + .is_set = bt_ctf_field_common_sequence_is_set_recursive, + .reset = bt_ctf_field_common_sequence_reset_recursive, +}; + +static struct bt_ctf_field_common_methods bt_ctf_field_array_methods = { + .set_is_frozen = bt_ctf_field_common_array_set_is_frozen_recursive, + .validate = bt_ctf_field_common_array_validate_recursive, + .copy = NULL, + .is_set = bt_ctf_field_common_array_is_set_recursive, + .reset = bt_ctf_field_common_array_reset_recursive, +}; + +static +void bt_ctf_field_variant_set_is_frozen_recursive(struct bt_ctf_field_common *field, + bool is_frozen); + +static +int bt_ctf_field_variant_validate_recursive(struct bt_ctf_field_common *field); + +static +bt_bool bt_ctf_field_variant_is_set_recursive(struct bt_ctf_field_common *field); + +static +void bt_ctf_field_variant_reset_recursive(struct bt_ctf_field_common *field); + +static struct bt_ctf_field_common_methods bt_ctf_field_variant_methods = { + .set_is_frozen = bt_ctf_field_variant_set_is_frozen_recursive, + .validate = bt_ctf_field_variant_validate_recursive, + .copy = NULL, + .is_set = bt_ctf_field_variant_is_set_recursive, + .reset = bt_ctf_field_variant_reset_recursive, +}; + +static +struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_enumeration_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_floating_point_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_structure_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_variant_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_array_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_sequence_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *); + +static +struct bt_ctf_field *(* const field_create_funcs[])(struct bt_ctf_field_type *) = { + [BT_CTF_FIELD_TYPE_ID_INTEGER] = bt_ctf_field_integer_create, + [BT_CTF_FIELD_TYPE_ID_ENUM] = bt_ctf_field_enumeration_create, + [BT_CTF_FIELD_TYPE_ID_FLOAT] = bt_ctf_field_floating_point_create, + [BT_CTF_FIELD_TYPE_ID_STRUCT] = bt_ctf_field_structure_create, + [BT_CTF_FIELD_TYPE_ID_VARIANT] = bt_ctf_field_variant_create, + [BT_CTF_FIELD_TYPE_ID_ARRAY] = bt_ctf_field_array_create, + [BT_CTF_FIELD_TYPE_ID_SEQUENCE] = bt_ctf_field_sequence_create, + [BT_CTF_FIELD_TYPE_ID_STRING] = bt_ctf_field_string_create, +}; + +typedef int (*bt_ctf_field_serialize_recursive_func)( + struct bt_ctf_field_common *, struct bt_ctfser *, + enum bt_ctf_byte_order); + +static +void bt_ctf_field_integer_destroy(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer integer field object: addr=%p", field); + bt_ctf_field_common_integer_finalize((void *) field); + g_free(field); +} + +static +void bt_ctf_field_floating_point_destroy(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer floating point field object: addr=%p", + field); + bt_ctf_field_common_floating_point_finalize((void *) field); + g_free(field); +} + +static +void bt_ctf_field_enumeration_destroy_recursive(struct bt_ctf_field *field) +{ + struct bt_ctf_field_enumeration *enumeration = BT_CTF_FROM_COMMON(field); + + BT_LOGD("Destroying CTF writer enumeration field object: addr=%p", + field); + BT_LOGD_STR("Putting container field."); + bt_ctf_object_put_ref(enumeration->container); + bt_ctf_field_common_finalize((void *) field); + g_free(field); +} + +static +void bt_ctf_field_structure_destroy_recursive(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer structure field object: addr=%p", field); + bt_ctf_field_common_structure_finalize_recursive((void *) field); + g_free(field); +} + +static +void bt_ctf_field_variant_destroy_recursive(struct bt_ctf_field *field) +{ + struct bt_ctf_field_variant *variant = BT_CTF_FROM_COMMON(field); + + BT_LOGD("Destroying CTF writer variant field object: addr=%p", field); + BT_LOGD_STR("Putting tag field."); + bt_ctf_object_put_ref(variant->tag); + bt_ctf_field_common_variant_finalize_recursive((void *) field); + g_free(field); +} + +static +void bt_ctf_field_array_destroy_recursive(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer array field object: addr=%p", field); + bt_ctf_field_common_array_finalize_recursive((void *) field); + g_free(field); +} + +static +void bt_ctf_field_sequence_destroy_recursive(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer sequence field object: addr=%p", field); + bt_ctf_field_common_sequence_finalize_recursive((void *) field); + g_free(field); +} + +static +void bt_ctf_field_string_destroy(struct bt_ctf_field *field) +{ + BT_LOGD("Destroying CTF writer string field object: addr=%p", field); + bt_ctf_field_common_string_finalize((void *) field); + g_free(field); +} + +BT_HIDDEN +int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + struct bt_ctf_field_common *field_common = (void *) field; + bt_ctf_field_serialize_recursive_func serialize_func; + + BT_ASSERT(ctfser); + BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT(field_common->spec.writer.serialize_func); + serialize_func = field_common->spec.writer.serialize_func; + return serialize_func(field_common, ctfser, + native_byte_order); +} + +static +int bt_ctf_field_integer_serialize(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int ret; + struct bt_ctf_field_type_common_integer *int_type = + BT_CTF_FROM_COMMON(field->type); + struct bt_ctf_field_common_integer *int_field = + BT_CTF_FROM_COMMON(field); + enum bt_ctf_byte_order byte_order; + + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Integer field"); + BT_LOGV("Serializing CTF writer integer field: addr=%p, native-bo=%s", + field, + bt_ctf_byte_order_string(native_byte_order)); + byte_order = int_type->user_byte_order; + if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { + byte_order = native_byte_order; + } + + if (int_type->is_signed) { + ret = bt_ctfser_write_signed_int(ctfser, + int_field->payload.signd, int_type->common.alignment, + int_type->size, + byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN); + } else { + ret = bt_ctfser_write_unsigned_int(ctfser, + int_field->payload.unsignd, int_type->common.alignment, + int_type->size, + byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN); + } + + if (unlikely(ret)) { + BT_LOGE("Cannot serialize integer field: ret=%d", ret); + goto end; + } + +end: + return ret; +} + +static +int bt_ctf_field_enumeration_serialize_recursive( + struct bt_ctf_field_common *field, struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + BT_LOGV("Serializing enumeration field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string(native_byte_order)); + BT_LOGV_STR("Serializing enumeration field's payload field."); + return bt_ctf_field_serialize_recursive( + (void *) enumeration->container, ctfser, native_byte_order); +} + +static +int bt_ctf_field_floating_point_serialize(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int ret = -1; + struct bt_ctf_field_type_common_floating_point *flt_type = + BT_CTF_FROM_COMMON(field->type); + struct bt_ctf_field_common_floating_point *flt_field = BT_CTF_FROM_COMMON(field); + enum bt_ctf_byte_order byte_order; + + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Floating point number field"); + BT_LOGV("Serializing floating point number field: " + "addr=%p, native-bo=%s", field, + bt_ctf_byte_order_string(native_byte_order)); + + byte_order = flt_type->user_byte_order; + if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { + byte_order = native_byte_order; + } + + if (flt_type->mant_dig == FLT_MANT_DIG) { + ret = bt_ctfser_write_float32(ctfser, flt_field->payload, + flt_type->common.alignment, + byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN); + } else if (flt_type->mant_dig == DBL_MANT_DIG) { + ret = bt_ctfser_write_float64(ctfser, flt_field->payload, + flt_type->common.alignment, + byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN); + } else { + abort(); + } + + if (unlikely(ret)) { + BT_LOGE("Cannot serialize floating point number field: " + "ret=%d", ret); + goto end; + } + +end: + return ret; +} + +static +int bt_ctf_field_structure_serialize_recursive(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int64_t i; + int ret; + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_LOGV("Serializing structure field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string(native_byte_order)); + ret = bt_ctfser_align_offset_in_current_packet(ctfser, + field->type->alignment); + if (unlikely(ret)) { + BT_LOGE("Cannot align offset before serializing structure field: " + "ret=%d", ret); + goto end; + } + + for (i = 0; i < structure->fields->len; i++) { + struct bt_ctf_field_common *member = g_ptr_array_index( + structure->fields, i); + const char *field_name = NULL; + + BT_LOGV("Serializing structure field's field: ser-offset=%" PRIu64 ", " + "field-addr=%p, index=%" PRIu64, + bt_ctfser_get_offset_in_current_packet_bits(ctfser), + member, i); + + if (unlikely(!member)) { + ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + field->type, &field_name, NULL, i); + BT_ASSERT(ret == 0); + BT_LOGW("Cannot serialize structure field's field: field is not set: " + "struct-field-addr=%p, " + "field-name=\"%s\", index=%" PRId64, + field, field_name, i); + ret = -1; + goto end; + } + + ret = bt_ctf_field_serialize_recursive((void *) member, ctfser, + native_byte_order); + if (unlikely(ret)) { + ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + field->type, &field_name, NULL, i); + BT_ASSERT(ret == 0); + BT_LOGW("Cannot serialize structure field's field: " + "struct-field-addr=%p, field-addr=%p, " + "field-name=\"%s\", index=%" PRId64, + field->type, member, field_name, i); + break; + } + } + +end: + return ret; +} + +static +int bt_ctf_field_variant_serialize_recursive(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + + BT_LOGV("Serializing variant field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string(native_byte_order)); + BT_LOGV_STR("Serializing variant field's payload field."); + return bt_ctf_field_serialize_recursive( + (void *) variant->current_field, ctfser, native_byte_order); +} + +static +int bt_ctf_field_array_serialize_recursive(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int64_t i; + int ret = 0; + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_LOGV("Serializing array field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string(native_byte_order)); + + for (i = 0; i < array->elements->len; i++) { + struct bt_ctf_field_common *elem_field = + g_ptr_array_index(array->elements, i); + + BT_LOGV("Serializing array field's element field: " + "ser-offset=%" PRIu64 ", field-addr=%p, index=%" PRId64, + bt_ctfser_get_offset_in_current_packet_bits(ctfser), + elem_field, i); + ret = bt_ctf_field_serialize_recursive( + (void *) elem_field, ctfser, native_byte_order); + if (unlikely(ret)) { + BT_LOGW("Cannot serialize array field's element field: " + "array-field-addr=%p, field-addr=%p, " + "index=%" PRId64, field, elem_field, i); + goto end; + } + } + +end: + return ret; +} + +static +int bt_ctf_field_sequence_serialize_recursive(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int64_t i; + int ret = 0; + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_LOGV("Serializing sequence field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string(native_byte_order)); + + for (i = 0; i < sequence->elements->len; i++) { + struct bt_ctf_field_common *elem_field = + g_ptr_array_index(sequence->elements, i); + + BT_LOGV("Serializing sequence field's element field: " + "ser-offset=%" PRIu64 ", field-addr=%p, index=%" PRId64, + bt_ctfser_get_offset_in_current_packet_bits(ctfser), + elem_field, i); + ret = bt_ctf_field_serialize_recursive( + (void *) elem_field, ctfser, native_byte_order); + if (unlikely(ret)) { + BT_LOGW("Cannot serialize sequence field's element field: " + "sequence-field-addr=%p, field-addr=%p, " + "index=%" PRId64, field, elem_field, i); + goto end; + } + } + +end: + return ret; +} + +static +int bt_ctf_field_string_serialize(struct bt_ctf_field_common *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order) +{ + int ret; + struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "String field"); + BT_LOGV("Serializing string field: addr=%p, native-bo=%s", + field, bt_ctf_byte_order_string((int) native_byte_order)); + ret = bt_ctfser_write_string(ctfser, (const char *) string->buf->data); + if (unlikely(ret)) { + BT_LOGE("Cannot serialize string field: ret=%d", ret); + goto end; + } + +end: + return ret; +} + +struct bt_ctf_field *bt_ctf_field_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field *field = NULL; + enum bt_ctf_field_type_id type_id; + + BT_CTF_ASSERT_PRE_NON_NULL(type, "Field type"); + BT_ASSERT(field_type_common_has_known_id((void *) type)); + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_validate((void *) type) == 0, + "Field type is invalid: ft-addr=%p", type); + type_id = bt_ctf_field_type_get_type_id(type); + field = field_create_funcs[type_id](type); + if (!field) { + goto end; + } + + bt_ctf_field_type_common_freeze((void *) type); + +end: + return field; +} + +struct bt_ctf_field_type *bt_ctf_field_get_type(struct bt_ctf_field *field) +{ + return bt_ctf_object_get_ref(bt_ctf_field_common_borrow_type((void *) field)); +} + +enum bt_ctf_field_type_id bt_ctf_field_get_type_id(struct bt_ctf_field *field) +{ + struct bt_ctf_field_common *field_common = (void *) field; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); + return (int) field_common->type->id; +} + +int bt_ctf_field_sequence_set_length(struct bt_ctf_field *field, + struct bt_ctf_field *length_field) +{ + int ret; + struct bt_ctf_field_common *common_length_field = (void *) length_field; + uint64_t length; + + BT_CTF_ASSERT_PRE_NON_NULL(length_field, "Length field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((void *) length_field, "Length field"); + BT_CTF_ASSERT_PRE(common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_INTEGER || + common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_ENUM, + "Length field must be an integer or enumeration field: field-addr=%p", + length_field); + + if (common_length_field->type->id == BT_CTF_FIELD_TYPE_ID_ENUM) { + struct bt_ctf_field_enumeration *enumeration = (void *) + length_field; + + length_field = (void *) enumeration->container; + } + + ret = bt_ctf_field_integer_unsigned_get_value(length_field, &length); + BT_ASSERT(ret == 0); + return bt_ctf_field_common_sequence_set_length((void *) field, + length, (bt_ctf_field_common_create_func) bt_ctf_field_create); +} + +struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index( + struct bt_ctf_field *field, uint64_t index) +{ + return bt_ctf_object_get_ref(bt_ctf_field_common_structure_borrow_field_by_index( + (void *) field, index)); +} + +struct bt_ctf_field *bt_ctf_field_structure_get_field_by_name( + struct bt_ctf_field *field, const char *name) +{ + return bt_ctf_object_get_ref(bt_ctf_field_common_structure_borrow_field_by_name( + (void *) field, name)); +} + +struct bt_ctf_field *bt_ctf_field_array_get_field( + struct bt_ctf_field *field, uint64_t index) +{ + return bt_ctf_object_get_ref( + bt_ctf_field_common_array_borrow_field((void *) field, index)); +} + +struct bt_ctf_field *bt_ctf_field_sequence_get_field( + struct bt_ctf_field *field, uint64_t index) +{ + return bt_ctf_object_get_ref( + bt_ctf_field_common_sequence_borrow_field((void *) field, index)); +} + +struct bt_ctf_field *bt_ctf_field_variant_get_field(struct bt_ctf_field *field, + struct bt_ctf_field *tag_field) +{ + struct bt_ctf_field_variant *variant_field = (void *) field; + struct bt_ctf_field_enumeration *enum_field = (void *) tag_field; + struct bt_ctf_field_type_common_variant *variant_ft; + struct bt_ctf_field_type_common_enumeration *tag_ft; + struct bt_ctf_field *current_field = NULL; + bt_bool is_signed; + uint64_t tag_uval; + int ret; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Variant field"); + BT_CTF_ASSERT_PRE_NON_NULL(tag_field, "Tag field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET((void *) tag_field, "Tag field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( + (struct bt_ctf_field_common *) tag_field, + BT_CTF_FIELD_TYPE_ID_ENUM, "Tag field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID( + (struct bt_ctf_field_common *) field, + BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); + BT_CTF_ASSERT_PRE( + bt_ctf_field_common_validate_recursive((void *) tag_field) == 0, + "Tag field is invalid: field-addr=%p", tag_field); + variant_ft = BT_CTF_FROM_COMMON(variant_field->common.common.type); + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_compare( + BT_CTF_TO_COMMON(variant_ft->tag_ft), enum_field->common.type) == 0, + "Unexpected tag field's type: expected-ft-addr=%p, " + "tag-ft-addr=%p", variant_ft->tag_ft, + enum_field->common.type); + tag_ft = BT_CTF_FROM_COMMON(enum_field->common.type); + is_signed = tag_ft->container_ft->is_signed; + + if (is_signed) { + int64_t tag_ival; + + ret = bt_ctf_field_integer_signed_get_value( + (void *) enum_field->container, &tag_ival); + tag_uval = (uint64_t) tag_ival; + } else { + ret = bt_ctf_field_integer_unsigned_get_value( + (void *) enum_field->container, &tag_uval); + } + + BT_ASSERT(ret == 0); + ret = bt_ctf_field_common_variant_set_tag((void *) field, tag_uval, + is_signed); + if (ret) { + goto end; + } + + bt_ctf_object_put_ref(variant_field->tag); + variant_field->tag = bt_ctf_object_get_ref(tag_field); + current_field = bt_ctf_field_variant_get_current_field(field); + BT_ASSERT(current_field); + +end: + return current_field; +} + +struct bt_ctf_field *bt_ctf_field_variant_get_current_field( + struct bt_ctf_field *variant_field) +{ + return bt_ctf_object_get_ref(bt_ctf_field_common_variant_borrow_current_field( + (void *) variant_field)); +} + +BT_HIDDEN +struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container( + struct bt_ctf_field *field) +{ + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Enumeration field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID((struct bt_ctf_field_common *) field, + BT_CTF_FIELD_TYPE_ID_ENUM, "Field"); + BT_ASSERT(enumeration->container); + return (void *) enumeration->container; +} + +struct bt_ctf_field *bt_ctf_field_enumeration_get_container( + struct bt_ctf_field *field) +{ + return bt_ctf_object_get_ref(bt_ctf_field_enumeration_borrow_container(field)); +} + +int bt_ctf_field_integer_signed_get_value(struct bt_ctf_field *field, + int64_t *value) +{ + struct bt_ctf_field_common_integer *integer = (void *) field; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(BT_CTF_TO_COMMON(integer), "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), + BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); + BT_CTF_ASSERT_PRE(bt_ctf_field_type_common_integer_is_signed( + integer->common.type), + "Field's type is unsigned: field-addr=%p", field); + *value = integer->payload.signd; + return 0; +} + +int bt_ctf_field_integer_signed_set_value(struct bt_ctf_field *field, + int64_t value) +{ + int ret = 0; + struct bt_ctf_field_common_integer *integer = (void *) field; + struct bt_ctf_field_type_common_integer *integer_type; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(BT_CTF_TO_COMMON(integer), "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), + BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); + integer_type = BT_CTF_FROM_COMMON(integer->common.type); + BT_CTF_ASSERT_PRE( + bt_ctf_field_type_common_integer_is_signed(integer->common.type), + "Field's type is unsigned: field-addr=%p", field); + BT_CTF_ASSERT_PRE(value_is_in_range_signed(integer_type->size, value), + "Value is out of bounds: value=%" PRId64 ", field-addr=%p", + value, field); + integer->payload.signd = value; + bt_ctf_field_common_set(BT_CTF_TO_COMMON(integer), true); + return ret; +} + +int bt_ctf_field_integer_unsigned_get_value(struct bt_ctf_field *field, + uint64_t *value) +{ + struct bt_ctf_field_common_integer *integer = (void *) field; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(BT_CTF_TO_COMMON(integer), "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), + BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); + BT_CTF_ASSERT_PRE( + !bt_ctf_field_type_common_integer_is_signed(integer->common.type), + "Field's type is signed: field-addr=%p", field); + *value = integer->payload.unsignd; + return 0; +} + +int bt_ctf_field_integer_unsigned_set_value(struct bt_ctf_field *field, + uint64_t value) +{ + struct bt_ctf_field_common_integer *integer = (void *) field; + struct bt_ctf_field_type_common_integer *integer_type; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(BT_CTF_TO_COMMON(integer), "Integer field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(BT_CTF_TO_COMMON(integer), + BT_CTF_FIELD_TYPE_ID_INTEGER, "Field"); + integer_type = BT_CTF_FROM_COMMON(integer->common.type); + BT_CTF_ASSERT_PRE( + !bt_ctf_field_type_common_integer_is_signed(integer->common.type), + "Field's type is signed: field-addr=%p", field); + BT_CTF_ASSERT_PRE(value_is_in_range_unsigned(integer_type->size, value), + "Value is out of bounds: value=%" PRIu64 ", field-addr=%p", + value, field); + integer->payload.unsignd = value; + bt_ctf_field_common_set(BT_CTF_TO_COMMON(integer), true); + return 0; +} + +int bt_ctf_field_floating_point_get_value(struct bt_ctf_field *field, + double *value) +{ + return bt_ctf_field_common_floating_point_get_value((void *) field, value); +} + +int bt_ctf_field_floating_point_set_value(struct bt_ctf_field *field, + double value) +{ + return bt_ctf_field_common_floating_point_set_value((void *) field, value); +} + +const char *bt_ctf_field_string_get_value(struct bt_ctf_field *field) +{ + return bt_ctf_field_common_string_get_value((void *) field); +} + +int bt_ctf_field_string_set_value(struct bt_ctf_field *field, const char *value) +{ + return bt_ctf_field_common_string_set_value((void *) field, value); +} + +int bt_ctf_field_string_append(struct bt_ctf_field *field, const char *value) +{ + return bt_ctf_field_common_string_append((void *) field, value); +} + +int bt_ctf_field_string_append_len(struct bt_ctf_field *field, + const char *value, unsigned int length) +{ + return bt_ctf_field_common_string_append_len((void *) field, value, length); +} + +struct bt_ctf_field *bt_ctf_field_copy(struct bt_ctf_field *field) +{ + return (void *) bt_ctf_field_common_copy((void *) field); +} + +static +struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_integer *integer = + g_new0(struct bt_ctf_field_common_integer, 1); + + BT_LOGD("Creating CTF writer integer field object: ft-addr=%p", type); + + if (integer) { + bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(integer), (void *) type, + true, + (bt_ctf_object_release_func) bt_ctf_field_integer_destroy, + &bt_ctf_field_integer_methods); + integer->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_integer_serialize; + BT_LOGD("Created CTF writer integer field object: addr=%p, ft-addr=%p", + integer, type); + } else { + BT_LOGE_STR("Failed to allocate one integer field."); + } + + return (void *) integer; +} + +static +struct bt_ctf_field *bt_ctf_field_enumeration_create( + struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_type_common_enumeration *enum_ft = (void *) type; + struct bt_ctf_field_enumeration *enumeration = g_new0( + struct bt_ctf_field_enumeration, 1); + + BT_LOGD("Creating CTF writer enumeration field object: ft-addr=%p", type); + + if (!enumeration) { + BT_LOGE_STR("Failed to allocate one enumeration field."); + goto end; + } + + bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(enumeration), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_enumeration_destroy_recursive, + &bt_ctf_field_enumeration_methods); + enumeration->container = (void *) bt_ctf_field_create( + BT_CTF_FROM_COMMON(enum_ft->container_ft)); + if (!enumeration->container) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(enumeration); + goto end; + } + + enumeration->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) + bt_ctf_field_enumeration_serialize_recursive; + BT_LOGD("Created CTF writer enumeration field object: addr=%p, ft-addr=%p", + enumeration, type); + +end: + return (void *) enumeration; +} + +static +struct bt_ctf_field *bt_ctf_field_floating_point_create( + struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_floating_point *floating_point; + + BT_LOGD("Creating CTF writer floating point number field object: ft-addr=%p", type); + floating_point = g_new0(struct bt_ctf_field_common_floating_point, 1); + + if (floating_point) { + bt_ctf_field_common_initialize(BT_CTF_TO_COMMON(floating_point), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_floating_point_destroy, + &bt_ctf_field_floating_point_methods); + floating_point->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_floating_point_serialize; + BT_LOGD("Created CTF writer floating point number field object: addr=%p, ft-addr=%p", + floating_point, type); + } else { + BT_LOGE_STR("Failed to allocate one floating point number field."); + } + + return (void *) floating_point; +} + +static +struct bt_ctf_field *bt_ctf_field_structure_create( + struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_structure *structure = g_new0( + struct bt_ctf_field_common_structure, 1); + int iret; + + BT_LOGD("Creating CTF writer structure field object: ft-addr=%p", type); + + if (!structure) { + BT_LOGE_STR("Failed to allocate one structure field."); + goto end; + } + + iret = bt_ctf_field_common_structure_initialize(BT_CTF_TO_COMMON(structure), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_structure_destroy_recursive, + &bt_ctf_field_structure_methods, + (bt_ctf_field_common_create_func) bt_ctf_field_create, + (GDestroyNotify) bt_ctf_object_put_ref); + structure->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_structure_serialize_recursive; + if (iret) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(structure); + goto end; + } + + BT_LOGD("Created CTF writer structure field object: addr=%p, ft-addr=%p", + structure, type); + +end: + return (void *) structure; +} + +static +struct bt_ctf_field *bt_ctf_field_variant_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_type_common_variant *var_ft = (void *) type; + struct bt_ctf_field_variant *variant = g_new0( + struct bt_ctf_field_variant, 1); + + BT_LOGD("Creating CTF writer variant field object: ft-addr=%p", type); + + if (!variant) { + BT_LOGE_STR("Failed to allocate one variant field."); + goto end; + } + + bt_ctf_field_common_variant_initialize(BT_CTF_TO_COMMON(BT_CTF_TO_COMMON(variant)), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_variant_destroy_recursive, + &bt_ctf_field_variant_methods, + (bt_ctf_field_common_create_func) bt_ctf_field_create, + (GDestroyNotify) bt_ctf_object_put_ref); + variant->tag = (void *) bt_ctf_field_create( + BT_CTF_FROM_COMMON(var_ft->tag_ft)); + variant->common.common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_variant_serialize_recursive; + BT_LOGD("Created CTF writer variant field object: addr=%p, ft-addr=%p", + variant, type); + +end: + return (void *) variant; +} + +static +struct bt_ctf_field *bt_ctf_field_array_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_array *array = + g_new0(struct bt_ctf_field_common_array, 1); + int ret; + + BT_LOGD("Creating CTF writer array field object: ft-addr=%p", type); + BT_ASSERT(type); + + if (!array) { + BT_LOGE_STR("Failed to allocate one array field."); + goto end; + } + + ret = bt_ctf_field_common_array_initialize(BT_CTF_TO_COMMON(array), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_array_destroy_recursive, + &bt_ctf_field_array_methods, + (bt_ctf_field_common_create_func) bt_ctf_field_create, + (GDestroyNotify) bt_ctf_object_put_ref); + array->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_array_serialize_recursive; + if (ret) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(array); + goto end; + } + + BT_LOGD("Created CTF writer array field object: addr=%p, ft-addr=%p", + array, type); + +end: + return (void *) array; +} + +static +struct bt_ctf_field *bt_ctf_field_sequence_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_sequence *sequence = g_new0( + struct bt_ctf_field_common_sequence, 1); + + BT_LOGD("Creating CTF writer sequence field object: ft-addr=%p", type); + + if (sequence) { + bt_ctf_field_common_sequence_initialize(BT_CTF_TO_COMMON(sequence), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_sequence_destroy_recursive, + &bt_ctf_field_sequence_methods, + (GDestroyNotify) bt_ctf_object_put_ref); + sequence->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_sequence_serialize_recursive; + BT_LOGD("Created CTF writer sequence field object: addr=%p, ft-addr=%p", + sequence, type); + } else { + BT_LOGE_STR("Failed to allocate one sequence field."); + } + + return (void *) sequence; +} + +static +struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *type) +{ + struct bt_ctf_field_common_string *string = g_new0( + struct bt_ctf_field_common_string, 1); + + BT_LOGD("Creating CTF writer string field object: ft-addr=%p", type); + + if (string) { + bt_ctf_field_common_string_initialize(BT_CTF_TO_COMMON(string), + (void *) type, + true, (bt_ctf_object_release_func) + bt_ctf_field_string_destroy, + &bt_ctf_field_string_methods); + string->common.spec.writer.serialize_func = + (bt_ctf_field_serialize_recursive_func) bt_ctf_field_string_serialize; + BT_LOGD("Created CTF writer string field object: addr=%p, ft-addr=%p", + string, type); + } else { + BT_LOGE_STR("Failed to allocate one string field."); + } + + return (void *) string; +} + +static +void bt_ctf_field_enumeration_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + if (enumeration->container) { + bt_ctf_field_common_set_is_frozen_recursive( + (void *) enumeration->container, is_frozen); + } + + bt_ctf_field_common_generic_set_is_frozen((void *) field, is_frozen); +} + +static +int bt_ctf_field_enumeration_validate_recursive(struct bt_ctf_field_common *field) +{ + int ret = -1; + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + if (enumeration->container) { + ret = bt_ctf_field_common_validate_recursive( + (void *) enumeration->container); + } + + return ret; +} + +static +bt_bool bt_ctf_field_enumeration_is_set_recursive(struct bt_ctf_field_common *field) +{ + bt_bool is_set = BT_FALSE; + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + if (enumeration->container) { + is_set = bt_ctf_field_common_is_set_recursive( + (void *) enumeration->container); + } + + return is_set; +} + +static +void bt_ctf_field_enumeration_reset_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_enumeration *enumeration = (void *) field; + + if (enumeration->container) { + bt_ctf_field_common_reset_recursive( + (void *) enumeration->container); + } + + bt_ctf_field_common_generic_reset((void *) field); +} + +static +void bt_ctf_field_variant_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen) +{ + struct bt_ctf_field_variant *variant = (void *) field; + + if (variant->tag) { + bt_ctf_field_common_set_is_frozen_recursive( + (void *) variant->tag, is_frozen); + } + + bt_ctf_field_common_variant_set_is_frozen_recursive((void *) field, + is_frozen); +} + +static +int bt_ctf_field_variant_validate_recursive(struct bt_ctf_field_common *field) +{ + int ret; + struct bt_ctf_field_variant *variant = (void *) field; + + if (variant->tag) { + ret = bt_ctf_field_common_validate_recursive( + (void *) variant->tag); + if (ret) { + goto end; + } + } + + ret = bt_ctf_field_common_variant_validate_recursive((void *) field); + +end: + return ret; +} + +static +bt_bool bt_ctf_field_variant_is_set_recursive(struct bt_ctf_field_common *field) +{ + bt_bool is_set; + struct bt_ctf_field_variant *variant = (void *) field; + + if (variant->tag) { + is_set = bt_ctf_field_common_is_set_recursive( + (void *) variant->tag); + if (is_set) { + goto end; + } + } + + is_set = bt_ctf_field_common_variant_is_set_recursive((void *) field); + +end: + return is_set; +} + +static +void bt_ctf_field_variant_reset_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_variant *variant = (void *) field; + + if (variant->tag) { + bt_ctf_field_common_reset_recursive( + (void *) variant->tag); + } + + bt_ctf_field_common_variant_reset_recursive((void *) field); +} + +BT_CTF_ASSERT_PRE_FUNC +static inline bool field_to_set_has_expected_type( + struct bt_ctf_field_common *struct_field, + const char *name, struct bt_ctf_field_common *value) +{ + bool ret = true; + struct bt_ctf_field_type_common *expected_field_type = NULL; + + expected_field_type = + bt_ctf_field_type_common_structure_borrow_field_type_by_name( + struct_field->type, name); + + if (bt_ctf_field_type_common_compare(expected_field_type, value->type)) { + BT_CTF_ASSERT_PRE_MSG("Value field's type is different from the expected field type: " + "value-ft-addr=%p, expected-ft-addr=%p", value->type, + expected_field_type); + ret = false; + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field, + const char *name, struct bt_ctf_field *value) +{ + int ret = 0; + GQuark field_quark; + struct bt_ctf_field_common *common_field = (void *) field; + struct bt_ctf_field_common_structure *structure = + BT_CTF_FROM_COMMON(common_field); + struct bt_ctf_field_common *common_value = (void *) value; + size_t index; + GHashTable *field_name_to_index; + struct bt_ctf_field_type_common_structure *structure_ft; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Parent field"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Field name"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(common_field, + BT_CTF_FIELD_TYPE_ID_STRUCT, "Parent field"); + BT_CTF_ASSERT_PRE(field_to_set_has_expected_type(common_field, + name, common_value), + "Value field's type is different from the expected field type."); + field_quark = g_quark_from_string(name); + structure_ft = BT_CTF_FROM_COMMON(common_field->type); + field_name_to_index = structure_ft->field_name_to_index; + if (!g_hash_table_lookup_extended(field_name_to_index, + GUINT_TO_POINTER(field_quark), NULL, + (gpointer *) &index)) { + BT_LOGV("Invalid parameter: no such field in structure field's type: " + "struct-field-addr=%p, struct-ft-addr=%p, " + "field-ft-addr=%p, name=\"%s\"", + field, common_field->type, common_value->type, name); + ret = -1; + goto end; + } + bt_ctf_object_get_ref(value); + BT_CTF_OBJECT_MOVE_REF(structure->fields->pdata[index], value); + +end: + return ret; +} diff --git a/src/ctf-writer/fields.h b/src/ctf-writer/fields.h new file mode 100644 index 00000000..b1525e46 --- /dev/null +++ b/src/ctf-writer/fields.h @@ -0,0 +1,852 @@ +#ifndef BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H +#define BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/babeltrace.h" +#include "common/common.h" +#include "ctfser/ctfser.h" + +#include "assert-pre.h" +#include "field-types.h" +#include "object.h" +#include "utils.h" + +#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(_field, _type_id, _name) \ + BT_CTF_ASSERT_PRE((_field)->type->id == ((int) (_type_id)), \ + _name " has the wrong type ID: expected-type-id=%s, " \ + "field-addr=%p", \ + bt_ctf_field_type_id_string((int) (_type_id)), (_field)) + +#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(_field, _name) \ + BT_CTF_ASSERT_PRE(bt_ctf_field_common_is_set_recursive(_field), \ + _name " is not set: field-addr=%p", (_field)) + +#define BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(_field, _name) \ + BT_CTF_ASSERT_PRE_HOT((_field), (_name), ": field-addr=%p", (_field)) + +struct bt_ctf_field_common; + +typedef void (*bt_ctf_field_common_method_set_is_frozen)(struct bt_ctf_field_common *, + bool); +typedef int (*bt_ctf_field_common_method_validate)(struct bt_ctf_field_common *); +typedef struct bt_ctf_field_common *(*bt_ctf_field_common_method_copy)( + struct bt_ctf_field_common *); +typedef bt_bool (*bt_ctf_field_common_method_is_set)(struct bt_ctf_field_common *); +typedef void (*bt_ctf_field_common_method_reset)(struct bt_ctf_field_common *); + +struct bt_ctf_field_common_methods { + bt_ctf_field_common_method_set_is_frozen set_is_frozen; + bt_ctf_field_common_method_validate validate; + bt_ctf_field_common_method_copy copy; + bt_ctf_field_common_method_is_set is_set; + bt_ctf_field_common_method_reset reset; +}; + +struct bt_ctf_field_common { + struct bt_ctf_object base; + struct bt_ctf_field_type_common *type; + struct bt_ctf_field_common_methods *methods; + bool payload_set; + bool frozen; + + /* + * Specialized data for either CTF IR or CTF writer APIs. + * See comment in `field-types.h` for more details. + */ + union { + struct { + } ir; + struct { + void *serialize_func; + } writer; + } spec; +}; + +struct bt_ctf_field_common_integer { + struct bt_ctf_field_common common; + union { + int64_t signd; + uint64_t unsignd; + } payload; +}; + +struct bt_ctf_field_common_floating_point { + struct bt_ctf_field_common common; + double payload; +}; + +struct bt_ctf_field_common_structure { + struct bt_ctf_field_common common; + + /* Array of `struct bt_ctf_field_common *`, owned by this */ + GPtrArray *fields; +}; + +struct bt_ctf_field_common_variant { + struct bt_ctf_field_common common; + + union { + uint64_t u; + int64_t i; + } tag_value; + + /* Weak: belongs to `choices` below */ + struct bt_ctf_field_common *current_field; + + /* Array of `struct bt_ctf_field_common *`, owned by this */ + GPtrArray *fields; +}; + +struct bt_ctf_field_common_array { + struct bt_ctf_field_common common; + + /* Array of `struct bt_ctf_field_common *`, owned by this */ + GPtrArray *elements; +}; + +struct bt_ctf_field_common_sequence { + struct bt_ctf_field_common common; + + /* + * This is the true sequence field's length: its value can be + * less than `elements->len` below because we never shrink the + * array of elements to avoid reallocation. + */ + uint64_t length; + + /* Array of `struct bt_ctf_field_common *`, owned by this */ + GPtrArray *elements; +}; + +struct bt_ctf_field_common_string { + struct bt_ctf_field_common common; + GArray *buf; + size_t size; +}; + +BT_HIDDEN +struct bt_ctf_field_common *bt_ctf_field_common_copy(struct bt_ctf_field_common *field); + +BT_HIDDEN +int bt_ctf_field_common_structure_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_release_func); + +BT_HIDDEN +int bt_ctf_field_common_array_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_destroy_func); + +BT_HIDDEN +int bt_ctf_field_common_sequence_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + GDestroyNotify field_destroy_func); + +BT_HIDDEN +int bt_ctf_field_common_variant_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods, + bt_ctf_field_common_create_func field_create_func, + GDestroyNotify field_release_func); + +BT_HIDDEN +int bt_ctf_field_common_string_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *type, + bool is_shared, bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods); + +BT_HIDDEN +int bt_ctf_field_common_generic_validate(struct bt_ctf_field_common *field); + +BT_HIDDEN +int bt_ctf_field_common_structure_validate_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +int bt_ctf_field_common_variant_validate_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +int bt_ctf_field_common_array_validate_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +int bt_ctf_field_common_sequence_validate_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_generic_reset(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_structure_reset_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_variant_reset_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_array_reset_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_sequence_reset_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +void bt_ctf_field_common_generic_set_is_frozen(struct bt_ctf_field_common *field, + bool is_frozen); + +BT_HIDDEN +void bt_ctf_field_common_structure_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen); + +BT_HIDDEN +void bt_ctf_field_common_variant_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen); + +BT_HIDDEN +void bt_ctf_field_common_array_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen); + +BT_HIDDEN +void bt_ctf_field_common_sequence_set_is_frozen_recursive( + struct bt_ctf_field_common *field, bool is_frozen); + +BT_HIDDEN +void _bt_ctf_field_common_set_is_frozen_recursive(struct bt_ctf_field_common *field, + bool is_frozen); + +BT_HIDDEN +bt_bool bt_ctf_field_common_generic_is_set(struct bt_ctf_field_common *field); + +BT_HIDDEN +bt_bool bt_ctf_field_common_structure_is_set_recursive( + struct bt_ctf_field_common *field); + +BT_HIDDEN +bt_bool bt_ctf_field_common_variant_is_set_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +bt_bool bt_ctf_field_common_array_is_set_recursive(struct bt_ctf_field_common *field); + +BT_HIDDEN +bt_bool bt_ctf_field_common_sequence_is_set_recursive(struct bt_ctf_field_common *field); + +#ifdef BT_DEV_MODE +# define bt_ctf_field_common_validate_recursive _bt_ctf_field_common_validate_recursive +# define bt_ctf_field_common_set_is_frozen_recursive _bt_ctf_field_common_set_is_frozen_recursive +# define bt_ctf_field_common_is_set_recursive _bt_ctf_field_common_is_set_recursive +# define bt_ctf_field_common_reset_recursive _bt_ctf_field_common_reset_recursive +# define bt_ctf_field_common_set _bt_ctf_field_common_set +#else +# define bt_ctf_field_common_validate_recursive(_field) (-1) +# define bt_ctf_field_common_set_is_frozen_recursive(_field, _is_frozen) +# define bt_ctf_field_common_is_set_recursive(_field) (BT_FALSE) +# define bt_ctf_field_common_reset_recursive(_field) +# define bt_ctf_field_common_set(_field, _val) +#endif + +BT_ASSERT_FUNC +static inline bool field_type_common_has_known_id( + struct bt_ctf_field_type_common *ft) +{ + return (int) ft->id > BT_CTF_FIELD_TYPE_ID_UNKNOWN || + (int) ft->id < BT_CTF_FIELD_TYPE_ID_NR; +} + +static inline +int _bt_ctf_field_common_validate_recursive(struct bt_ctf_field_common *field) +{ + int ret = 0; + + if (!field) { + BT_CTF_ASSERT_PRE_MSG("%s", "Invalid field: field is NULL."); + ret = -1; + goto end; + } + + BT_ASSERT(field_type_common_has_known_id(field->type)); + + if (field->methods->validate) { + ret = field->methods->validate(field); + } + +end: + return ret; +} + +static inline +void _bt_ctf_field_common_reset_recursive(struct bt_ctf_field_common *field) +{ + BT_ASSERT(field); + BT_ASSERT(field->methods->reset); + field->methods->reset(field); +} + +static inline +void _bt_ctf_field_common_set(struct bt_ctf_field_common *field, bool value) +{ + BT_ASSERT(field); + field->payload_set = value; +} + +static inline +bt_bool _bt_ctf_field_common_is_set_recursive(struct bt_ctf_field_common *field) +{ + bt_bool is_set = BT_FALSE; + + if (!field) { + goto end; + } + + BT_ASSERT(field_type_common_has_known_id(field->type)); + BT_ASSERT(field->methods->is_set); + is_set = field->methods->is_set(field); + +end: + return is_set; +} + +static inline +void bt_ctf_field_common_initialize(struct bt_ctf_field_common *field, + struct bt_ctf_field_type_common *ft, bool is_shared, + bt_ctf_object_release_func release_func, + struct bt_ctf_field_common_methods *methods) +{ + BT_ASSERT(field); + BT_ASSERT(ft); + bt_ctf_object_init(&field->base, is_shared, release_func); + field->methods = methods; + field->type = (void *) bt_ctf_object_get_ref(ft); +} + +static inline +struct bt_ctf_field_type_common *bt_ctf_field_common_borrow_type( + struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_type_common *ret = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Field"); + ret = field->type; + return ret; +} + +static inline +int64_t bt_ctf_field_common_sequence_get_length(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_SEQUENCE, + "Field"); + return (int64_t) sequence->length; +} + +static inline +int bt_ctf_field_common_sequence_set_length(struct bt_ctf_field_common *field, + uint64_t length, bt_ctf_field_common_create_func field_create_func) +{ + int ret = 0; + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); + BT_CTF_ASSERT_PRE(((int64_t) length) >= 0, + "Invalid sequence length (too large): length=%" PRId64, + length); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "Sequence field"); + + if (unlikely(length > sequence->elements->len)) { + /* Make more room */ + struct bt_ctf_field_type_common_sequence *sequence_ft; + uint64_t cur_len = sequence->elements->len; + uint64_t i; + + g_ptr_array_set_size(sequence->elements, length); + sequence_ft = BT_CTF_FROM_COMMON(sequence->common.type); + + for (i = cur_len; i < sequence->elements->len; i++) { + struct bt_ctf_field_common *elem_field = + field_create_func(sequence_ft->element_ft); + + if (!elem_field) { + ret = -1; + goto end; + } + + BT_ASSERT(!sequence->elements->pdata[i]); + sequence->elements->pdata[i] = elem_field; + } + } + + sequence->length = length; + +end: + return ret; +} + +static inline +struct bt_ctf_field_common *bt_ctf_field_common_structure_borrow_field_by_name( + struct bt_ctf_field_common *field, const char *name) +{ + struct bt_ctf_field_common *ret = NULL; + GQuark field_quark; + struct bt_ctf_field_type_common_structure *structure_ft; + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + size_t index; + GHashTable *field_name_to_index; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Structure field"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Field name"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRUCT, "Field"); + structure_ft = BT_CTF_FROM_COMMON(field->type); + field_name_to_index = structure_ft->field_name_to_index; + field_quark = g_quark_from_string(name); + if (!g_hash_table_lookup_extended(field_name_to_index, + GUINT_TO_POINTER(field_quark), + NULL, (gpointer *) &index)) { + BT_LOGV("Invalid parameter: no such field in structure field's type: " + "struct-field-addr=%p, struct-ft-addr=%p, name=\"%s\"", + field, field->type, name); + goto error; + } + + ret = structure->fields->pdata[index]; + BT_ASSERT(ret); + +error: + return ret; +} + +static inline +struct bt_ctf_field_common *bt_ctf_field_common_structure_borrow_field_by_index( + struct bt_ctf_field_common *field, uint64_t index) +{ + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Structure field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRUCT, "Field"); + BT_CTF_ASSERT_PRE(index < structure->fields->len, + "Index is out of bound: struct-field-addr=%p, " + "index=%" PRIu64 ", count=%u", field, index, + structure->fields->len); + return structure->fields->pdata[index]; +} + +static inline +struct bt_ctf_field_common *bt_ctf_field_common_array_borrow_field( + struct bt_ctf_field_common *field, uint64_t index) +{ + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Array field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_ARRAY, + "Field"); + BT_CTF_ASSERT_PRE(index < array->elements->len, + "Index is out of bound: array-field-addr=%p, " + "index=%" PRIu64 ", count=%u", field, + index, array->elements->len); + return array->elements->pdata[(size_t) index]; +} + +static inline +struct bt_ctf_field_common *bt_ctf_field_common_sequence_borrow_field( + struct bt_ctf_field_common *field, uint64_t index) +{ + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Sequence field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, BT_CTF_FIELD_TYPE_ID_SEQUENCE, + "Field"); + BT_CTF_ASSERT_PRE(index < sequence->length, + "Index is out of bound: seq-field-addr=%p, " + "index=%" PRIu64 ", count=%u", field, index, + sequence->elements->len); + return sequence->elements->pdata[(size_t) index]; +} + +static inline +int bt_ctf_field_common_variant_set_tag(struct bt_ctf_field_common *variant_field, + uint64_t tag_uval, bool is_signed) +{ + int ret = 0; + int64_t choice_index; + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); + + BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, + BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); + + /* Find matching index in variant field's type */ + choice_index = bt_ctf_field_type_common_variant_find_choice_index( + variant_field->type, tag_uval, is_signed); + if (choice_index < 0) { + ret = -1; + goto end; + } + + /* Select corresponding field */ + BT_ASSERT(choice_index < variant->fields->len); + variant->current_field = variant->fields->pdata[choice_index]; + variant->tag_value.u = tag_uval; + +end: + return ret; +} + +static inline +struct bt_ctf_field_common *bt_ctf_field_common_variant_borrow_current_field( + struct bt_ctf_field_common *variant_field) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); + + BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, + BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); + BT_CTF_ASSERT_PRE(variant->current_field, + "Variant field has no current field: field-addr=%p", variant_field); + return variant->current_field; +} + +static inline +int bt_ctf_field_common_variant_get_tag_signed(struct bt_ctf_field_common *variant_field, + int64_t *tag) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); + + BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, + BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); + BT_CTF_ASSERT_PRE(variant->current_field, + "Variant field has no current field: field-addr=%p", variant_field); + *tag = variant->tag_value.i; + return 0; +} + +static inline +int bt_ctf_field_common_variant_get_tag_unsigned(struct bt_ctf_field_common *variant_field, + uint64_t *tag) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(variant_field); + + BT_CTF_ASSERT_PRE_NON_NULL(variant_field, "Variant field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(variant_field, + BT_CTF_FIELD_TYPE_ID_VARIANT, "Field"); + BT_CTF_ASSERT_PRE(variant->current_field, + "Variant field has no current field: field-addr=%p", variant_field); + *tag = variant->tag_value.u; + return 0; +} + +static inline +int bt_ctf_field_common_floating_point_get_value(struct bt_ctf_field_common *field, + double *value) +{ + struct bt_ctf_field_common_floating_point *floating_point = + BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Floating point number field"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "Floating point number field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_FLOAT, "Field"); + *value = floating_point->payload; + return 0; +} + +static inline +int bt_ctf_field_common_floating_point_set_value(struct bt_ctf_field_common *field, + double value) +{ + struct bt_ctf_field_common_floating_point *floating_point = + BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "Floating point number field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "Floating point number field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_FLOAT, "Field"); + floating_point->payload = value; + bt_ctf_field_common_set(field, true); + return 0; +} + +static inline +const char *bt_ctf_field_common_string_get_value(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_IS_SET(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRING, "Field"); + return (const char *) string->buf->data; +} + +static inline +int bt_ctf_field_common_string_clear(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_string *string_field = BT_CTF_FROM_COMMON(field); + + BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRING, "Field"); + string_field->size = 0; + bt_ctf_field_common_set(field, true); + return 0; +} + +static inline +int bt_ctf_field_common_string_append_len(struct bt_ctf_field_common *field, + const char *value, unsigned int length) +{ + struct bt_ctf_field_common_string *string_field = BT_CTF_FROM_COMMON(field); + char *data; + size_t new_size; + + BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRING, "Field"); + + /* Make sure no null bytes are appended */ + BT_CTF_ASSERT_PRE(memchr(value, '\0', length) == NULL, + "String value to append contains a null character: " + "partial-value=\"%.32s\", length=%u", value, length); + + new_size = string_field->size + length; + + if (unlikely(new_size + 1 > string_field->buf->len)) { + g_array_set_size(string_field->buf, new_size + 1); + } + + data = string_field->buf->data; + memcpy(data + string_field->size, value, length); + ((char *) string_field->buf->data)[new_size] = '\0'; + string_field->size = new_size; + bt_ctf_field_common_set(field, true); + return 0; +} + +static inline +int bt_ctf_field_common_string_append(struct bt_ctf_field_common *field, + const char *value) +{ + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + return bt_ctf_field_common_string_append_len(field, value, + strlen(value)); +} + +static inline +int bt_ctf_field_common_string_set_value(struct bt_ctf_field_common *field, + const char *value) +{ + BT_CTF_ASSERT_PRE_NON_NULL(field, "String field"); + BT_CTF_ASSERT_PRE_NON_NULL(value, "Value"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HOT(field, "String field"); + BT_CTF_ASSERT_PRE_CTF_FIELD_COMMON_HAS_TYPE_ID(field, + BT_CTF_FIELD_TYPE_ID_STRING, "Field"); + bt_ctf_field_common_string_clear(field); + return bt_ctf_field_common_string_append_len(field, + value, strlen(value)); +} + +static inline +void bt_ctf_field_common_finalize(struct bt_ctf_field_common *field) +{ + BT_ASSERT(field); + BT_LOGD_STR("Putting field's type."); + bt_ctf_object_put_ref(field->type); +} + +static inline +void bt_ctf_field_common_integer_finalize(struct bt_ctf_field_common *field) +{ + BT_ASSERT(field); + BT_LOGD("Finalizing common integer field object: addr=%p", field); + bt_ctf_field_common_finalize(field); +} + +static inline +void bt_ctf_field_common_floating_point_finalize(struct bt_ctf_field_common *field) +{ + BT_ASSERT(field); + BT_LOGD("Finalizing common floating point number field object: addr=%p", field); + bt_ctf_field_common_finalize(field); +} + +static inline +void bt_ctf_field_common_structure_finalize_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_structure *structure = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + BT_LOGD("Finalizing common structure field object: addr=%p", field); + bt_ctf_field_common_finalize(field); + + if (structure->fields) { + g_ptr_array_free(structure->fields, TRUE); + } +} + +static inline +void bt_ctf_field_common_variant_finalize_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_variant *variant = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + BT_LOGD("Finalizing common variant field object: addr=%p", field); + bt_ctf_field_common_finalize(field); + + if (variant->fields) { + g_ptr_array_free(variant->fields, TRUE); + } +} + +static inline +void bt_ctf_field_common_array_finalize_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_array *array = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + BT_LOGD("Finalizing common array field object: addr=%p", field); + bt_ctf_field_common_finalize(field); + + if (array->elements) { + g_ptr_array_free(array->elements, TRUE); + } +} + +static inline +void bt_ctf_field_common_sequence_finalize_recursive(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_sequence *sequence = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + BT_LOGD("Finalizing common sequence field object: addr=%p", field); + bt_ctf_field_common_finalize(field); + + if (sequence->elements) { + g_ptr_array_free(sequence->elements, TRUE); + } +} + +static inline +void bt_ctf_field_common_string_finalize(struct bt_ctf_field_common *field) +{ + struct bt_ctf_field_common_string *string = BT_CTF_FROM_COMMON(field); + + BT_ASSERT(field); + BT_LOGD("Finalizing common string field object: addr=%p", field); + bt_ctf_field_common_finalize(field); + + if (string->buf) { + g_array_free(string->buf, TRUE); + } +} + +BT_CTF_ASSERT_PRE_FUNC +static inline bool value_is_in_range_signed(unsigned int size, int64_t value) +{ + bool ret = true; + int64_t min_value, max_value; + + min_value = -(1ULL << (size - 1)); + max_value = (1ULL << (size - 1)) - 1; + if (value < min_value || value > max_value) { + BT_LOGF("Value is out of bounds: value=%" PRId64 ", " + "min-value=%" PRId64 ", max-value=%" PRId64, + value, min_value, max_value); + ret = false; + } + + return ret; +} + +BT_CTF_ASSERT_PRE_FUNC +static inline bool value_is_in_range_unsigned(unsigned int size, uint64_t value) +{ + bool ret = true; + int64_t max_value; + + max_value = (size == 64) ? UINT64_MAX : ((uint64_t) 1 << size) - 1; + if (value > max_value) { + BT_LOGF("Value is out of bounds: value=%" PRIu64 ", " + "max-value=%" PRIu64, + value, max_value); + ret = false; + } + + return ret; +} + +struct bt_ctf_field_enumeration { + struct bt_ctf_field_common common; + struct bt_ctf_field_common_integer *container; +}; + +struct bt_ctf_field_variant { + struct bt_ctf_field_common_variant common; + struct bt_ctf_field_enumeration *tag; +}; + +BT_HIDDEN +int bt_ctf_field_serialize_recursive(struct bt_ctf_field *field, + struct bt_ctfser *ctfser, + enum bt_ctf_byte_order native_byte_order); + +BT_HIDDEN +int bt_ctf_field_structure_set_field_by_name(struct bt_ctf_field *field, + const char *name, struct bt_ctf_field *value); + +BT_HIDDEN +struct bt_ctf_field *bt_ctf_field_enumeration_borrow_container( + struct bt_ctf_field *field); + +static inline +bt_bool bt_ctf_field_is_set_recursive(struct bt_ctf_field *field) +{ + return bt_ctf_field_common_is_set_recursive((void *) field); +} + +#endif /* BABELTRACE_CTF_WRITER_FIELDS_INTERNAL_H */ diff --git a/src/ctf-writer/functor.c b/src/ctf-writer/functor.c new file mode 100644 index 00000000..0b1ecc87 --- /dev/null +++ b/src/ctf-writer/functor.c @@ -0,0 +1,40 @@ +/* + * functor.c + * + * Babeltrace CTF Writer + * + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "functor.h" +#include "utils.h" + +BT_HIDDEN +void value_exists(gpointer element, gpointer search_query) +{ + if (element == ((struct bt_ctf_search_query *)search_query)->value) { + ((struct bt_ctf_search_query *)search_query)->found = 1; + } +} diff --git a/src/ctf-writer/functor.h b/src/ctf-writer/functor.h new file mode 100644 index 00000000..628f63ca --- /dev/null +++ b/src/ctf-writer/functor.h @@ -0,0 +1,34 @@ +#ifndef BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H +#define BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +BT_HIDDEN +void value_exists(gpointer element, gpointer search_query); + +#endif /* BABELTRACE_CTF_WRITER_FUNCTOR_INTERNAL_H */ diff --git a/src/ctf-writer/logging.c b/src/ctf-writer/logging.c new file mode 100644 index 00000000..024d118e --- /dev/null +++ b/src/ctf-writer/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_lib_ctf_writer_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_lib_ctf_writer_log_level, + "BABELTRACE_CTF_WRITER_LOG_LEVEL"); diff --git a/src/ctf-writer/logging.h b/src/ctf-writer/logging.h new file mode 100644 index 00000000..8fb722e2 --- /dev/null +++ b/src/ctf-writer/logging.h @@ -0,0 +1,31 @@ +#ifndef BABELTRACE_CTF_WRITER_LOGGING_H +#define BABELTRACE_CTF_WRITER_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_lib_ctf_writer_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_lib_ctf_writer_log_level); + +#endif /* BABELTRACE_CTF_WRITER_LOGGING_H */ diff --git a/src/ctf-writer/object-pool.c b/src/ctf-writer/object-pool.c new file mode 100644 index 00000000..831297e7 --- /dev/null +++ b/src/ctf-writer/object-pool.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "OBJECT-POOL" +#include "logging.h" + +#include + +#include "common/assert.h" + +#include "object-pool.h" + +int bt_ctf_object_pool_initialize(struct bt_ctf_object_pool *pool, + bt_ctf_object_pool_new_object_func new_object_func, + bt_ctf_object_pool_destroy_object_func destroy_object_func, + void *data) +{ + int ret = 0; + + BT_ASSERT(new_object_func); + BT_ASSERT(destroy_object_func); + BT_LOGD("Initializing object pool: addr=%p, data-addr=%p", + pool, data); + pool->objects = g_ptr_array_new(); + if (!pool->objects) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + pool->funcs.new_object = new_object_func; + pool->funcs.destroy_object = destroy_object_func; + pool->data = data; + pool->size = 0; + BT_LOGD("Initialized object pool."); + goto end; + +error: + if (pool) { + bt_ctf_object_pool_finalize(pool); + } + + ret = -1; + +end: + return ret; +} + +void bt_ctf_object_pool_finalize(struct bt_ctf_object_pool *pool) +{ + uint64_t i; + + BT_ASSERT(pool); + BT_LOGD("Finalizing object pool."); + + if (pool->objects) { + for (i = 0; i < pool->size; i++) { + void *obj = pool->objects->pdata[i]; + + if (obj) { + pool->funcs.destroy_object(obj, pool->data); + } + } + + g_ptr_array_free(pool->objects, TRUE); + pool->objects = NULL; + } +} diff --git a/src/ctf-writer/object-pool.h b/src/ctf-writer/object-pool.h new file mode 100644 index 00000000..c0d8610f --- /dev/null +++ b/src/ctf-writer/object-pool.h @@ -0,0 +1,184 @@ +#ifndef BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H +#define BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H + +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This is a generic object pool to avoid memory allocation/deallocation + * for objects of which the lifespan is typically short, but which are + * created a lot. + * + * The object pool, thanks to two user functions, knows how to allocate + * a brand new object in memory when the pool is empty and how to + * destroy an object when we destroy the pool. + * + * The object pool's user is responsible for: + * + * * Setting whatever references the object needs to keep and reset some + * properties _after_ calling bt_ctf_object_pool_create_object(). This is + * typically done in the bt_*_create() function which calls + * bt_ctf_object_pool_create_object() (which could call the user-provided + * allocation function if the pool is empty) and then sets the + * appropriate properties on the possibly recycled object. + * + * * Releasing whatever references the object keeps _before_ calling + * bt_ctf_object_pool_recycle_object(). This is typically done in a custom + * bt_*_recycle() function which does the necessary before calling + * bt_ctf_object_pool_recycle_object() with an object ready to be reused + * at any time. + */ + +#include + +#include "object.h" + +typedef void *(*bt_ctf_object_pool_new_object_func)(void *data); +typedef void *(*bt_ctf_object_pool_destroy_object_func)(void *obj, void *data); + +struct bt_ctf_object_pool { + /* + * Container of recycled objects, owned by this. The array's size + * is the pool's capacity. + */ + GPtrArray *objects; + + /* + * Pool's size, that is, number of elements in the array above, + * starting at index 0, which exist as recycled objects. + */ + size_t size; + + /* User functions */ + struct { + /* Allocate a new object in memory */ + bt_ctf_object_pool_new_object_func new_object; + + /* Free direct and indirect memory occupied by object */ + bt_ctf_object_pool_destroy_object_func destroy_object; + } funcs; + + /* User data passed to user functions */ + void *data; +}; + +/* + * Initializes an object pool which is already allocated. + */ +int bt_ctf_object_pool_initialize(struct bt_ctf_object_pool *pool, + bt_ctf_object_pool_new_object_func new_object_func, + bt_ctf_object_pool_destroy_object_func destroy_object_func, + void *data); + +/* + * Finalizes an object pool without deallocating it. + */ +void bt_ctf_object_pool_finalize(struct bt_ctf_object_pool *pool); + +/* + * Creates an object from an object pool. If the pool is empty, this + * function calls the "new" user function to allocate a new object + * before returning it. Otherwise this function returns a recycled + * object, removing it from the pool. + * + * The returned object is owned by the caller. + */ +static inline +void *bt_ctf_object_pool_create_object(struct bt_ctf_object_pool *pool) +{ + struct bt_ctf_object *obj; + + BT_ASSERT(pool); + +#ifdef BT_LOGV + BT_LOGV("Creating object from pool: pool-addr=%p, pool-size=%zu, pool-cap=%u", + pool, pool->size, pool->objects->len); +#endif + + if (pool->size > 0) { + /* Pick one from the pool */ + pool->size--; + obj = pool->objects->pdata[pool->size]; + pool->objects->pdata[pool->size] = NULL; + goto end; + } + + /* Pool is empty: create a brand new object */ +#ifdef BT_LOGV + BT_LOGV("Pool is empty: allocating new object: pool-addr=%p", + pool); +#endif + + obj = pool->funcs.new_object(pool->data); + +end: +#ifdef BT_LOGV + BT_LOGV("Created one object from pool: pool-addr=%p, obj-addr=%p", + pool, obj); +#endif + + return obj; +} + +/* + * Recycles an object, that is, puts it back into the pool. + * + * The pool becomes the sole owner of the object to recycle. + */ +static inline +void bt_ctf_object_pool_recycle_object(struct bt_ctf_object_pool *pool, void *obj) +{ + struct bt_ctf_object *bt_obj = obj; + + BT_ASSERT(pool); + BT_ASSERT(obj); + +#ifdef BT_LOGV + BT_LOGV("Recycling object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", + pool, pool->size, pool->objects->len, obj); +#endif + + if (pool->size == pool->objects->len) { + /* Backing array is full: make place for recycled object */ +#ifdef BT_LOGV + BT_LOGV("Object pool is full: increasing object pool capacity: " + "pool-addr=%p, old-pool-cap=%u, new-pool-cap=%u", + pool, pool->objects->len, pool->objects->len + 1); +#endif + g_ptr_array_set_size(pool->objects, pool->size + 1); + } + + /* Reset reference count to 1 since it could be 0 now */ + bt_obj->ref_count = 1; + + /* Back to the pool */ + pool->objects->pdata[pool->size] = obj; + pool->size++; + +#ifdef BT_LOGV + BT_LOGV("Recycled object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", + pool, pool->size, pool->objects->len, obj); +#endif +} + +#endif /* BABELTRACE_CTF_WRITER_OBJECT_POOL_INTERNAL_H */ diff --git a/src/ctf-writer/object.c b/src/ctf-writer/object.c new file mode 100644 index 00000000..616fcf36 --- /dev/null +++ b/src/ctf-writer/object.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "object.h" + +void *bt_ctf_object_get_ref(void *obj) +{ + if (unlikely(!obj)) { + goto end; + } + + bt_ctf_object_get_no_null_check(obj); + +end: + return obj; +} + +void bt_ctf_object_put_ref(void *obj) +{ + if (unlikely(!obj)) { + return; + } + + bt_ctf_object_put_no_null_check(obj); +} diff --git a/src/ctf-writer/object.h b/src/ctf-writer/object.h new file mode 100644 index 00000000..32317218 --- /dev/null +++ b/src/ctf-writer/object.h @@ -0,0 +1,317 @@ +#ifndef BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H +#define BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H + +/* + * Copyright 2015 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "common/assert.h" +#include + +struct bt_ctf_object; + +typedef void (*bt_ctf_object_release_func)(struct bt_ctf_object *); +typedef void (*bt_ctf_object_parent_is_owner_listener_func)( + struct bt_ctf_object *); + +static inline +void *bt_ctf_object_get_no_null_check(struct bt_ctf_object *obj); + +static inline +void bt_ctf_object_put_no_null_check(struct bt_ctf_object *obj); + +/* + * Babeltrace object base. + * + * All objects publicly exposed by Babeltrace APIs must contain this + * object as their first member. + */ +struct bt_ctf_object { + /* + * True if this object is shared, that is, it has a reference + * count. + */ + bool is_shared; + + /* + * Current reference count. + */ + unsigned long long ref_count; + + /* + * Release function called when the object's reference count + * falls to zero. For an object with a parent, this function is + * bt_ctf_object_with_parent_release_func(), which calls + * `spec_release_func` below if there's no current parent. + */ + bt_ctf_object_release_func release_func; + + /* + * Specific release function called by + * bt_ctf_object_with_parent_release_func() or directly by a + * parent object. + */ + bt_ctf_object_release_func spec_release_func; + + /* + * Optional callback for an object with a parent, called by + * bt_ctf_object_with_parent_release_func() to indicate to the + * object that its parent is its owner. + */ + bt_ctf_object_parent_is_owner_listener_func + parent_is_owner_listener_func; + + /* + * Optional parent object. + */ + struct bt_ctf_object *parent; +}; + +static inline +unsigned long long bt_ctf_object_get_ref_count(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + return obj->ref_count; +} + +static inline +struct bt_ctf_object *bt_ctf_object_borrow_parent(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + return obj->parent; +} + +static inline +struct bt_ctf_object *bt_ctf_object_get_parent(struct bt_ctf_object *obj) +{ + struct bt_ctf_object *parent = bt_ctf_object_borrow_parent(obj); + + if (parent) { + bt_ctf_object_get_no_null_check(parent); + } + + return parent; +} + +static inline +void bt_ctf_object_set_parent(struct bt_ctf_object *child, struct bt_ctf_object *parent) +{ + BT_ASSERT(child); + BT_ASSERT(child->is_shared); + +#ifdef BT_LOGV + BT_LOGV("Setting object's parent: addr=%p, parent-addr=%p", + child, parent); +#endif + + /* + * It is assumed that a "child" having a parent is publicly + * reachable. Therefore, a reference to its parent must be + * taken. The reference to the parent will be released once the + * object's reference count falls to zero. + */ + if (parent) { + BT_ASSERT(!child->parent); + child->parent = parent; + bt_ctf_object_get_no_null_check(parent); + } else { + if (child->parent) { + bt_ctf_object_put_no_null_check(child->parent); + } + + child->parent = NULL; + } +} + +static inline +void bt_ctf_object_try_spec_release(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->spec_release_func); + + if (bt_ctf_object_get_ref_count(obj) == 0) { + obj->spec_release_func(obj); + } +} + +static inline +void bt_ctf_object_with_parent_release_func(struct bt_ctf_object *obj) +{ + if (obj->parent) { + /* + * Keep our own copy of the parent address because `obj` + * could be destroyed in + * obj->parent_is_owner_listener_func(). + */ + struct bt_ctf_object *parent = obj->parent; + +#ifdef BT_LOGV + BT_LOGV("Releasing parented object: addr=%p, ref-count=%llu, " + "parent-addr=%p, parent-ref-count=%llu", + obj, obj->ref_count, + parent, parent->ref_count); +#endif + + if (obj->parent_is_owner_listener_func) { + /* + * Object has a chance to destroy itself here + * under certain conditions and notify its + * parent. At this point the parent is + * guaranteed to exist because it's not put yet. + */ + obj->parent_is_owner_listener_func(obj); + } + + /* The release function will be invoked by the parent. */ + bt_ctf_object_put_no_null_check(parent); + } else { + bt_ctf_object_try_spec_release(obj); + } +} + +static inline +void bt_ctf_object_init(struct bt_ctf_object *obj, bool is_shared, + bt_ctf_object_release_func release_func) +{ + BT_ASSERT(obj); + BT_ASSERT(!is_shared || release_func); + obj->is_shared = is_shared; + obj->release_func = release_func; + obj->parent_is_owner_listener_func = NULL; + obj->spec_release_func = NULL; + obj->parent = NULL; + obj->ref_count = 1; +} + +static inline +void bt_ctf_object_init_shared(struct bt_ctf_object *obj, + bt_ctf_object_release_func release_func) +{ + bt_ctf_object_init(obj, true, release_func); +} + +static inline +void bt_ctf_object_init_unique(struct bt_ctf_object *obj) +{ + bt_ctf_object_init(obj, false, NULL); +} + +static inline +void bt_ctf_object_init_shared_with_parent(struct bt_ctf_object *obj, + bt_ctf_object_release_func spec_release_func) +{ + BT_ASSERT(obj); + BT_ASSERT(spec_release_func); + bt_ctf_object_init_shared(obj, bt_ctf_object_with_parent_release_func); + obj->spec_release_func = spec_release_func; +} + +static inline +void bt_ctf_object_set_parent_is_owner_listener_func(struct bt_ctf_object *obj, + bt_ctf_object_parent_is_owner_listener_func func) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->spec_release_func); + ((struct bt_ctf_object *) obj)->parent_is_owner_listener_func = func; +} + +static inline +void bt_ctf_object_inc_ref_count(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + obj->ref_count++; + BT_ASSERT(obj->ref_count != 0); +} + +static inline +void *bt_ctf_object_get_no_null_check_no_parent_check(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + +#ifdef BT_LOGV + BT_LOGV("Incrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count + 1, + obj, obj->ref_count, obj->ref_count + 1); +#endif + + bt_ctf_object_inc_ref_count(obj); + return obj; +} + +static inline +void *bt_ctf_object_get_no_null_check(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + + if (unlikely(obj->parent && bt_ctf_object_get_ref_count(obj) == 0)) { +#ifdef BT_LOGV + BT_LOGV("Incrementing object's parent's reference count: " + "addr=%p, parent-addr=%p", obj, obj->parent); +#endif + + bt_ctf_object_get_no_null_check(obj->parent); + } + +#ifdef BT_LOGV + BT_LOGV("Incrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count + 1, + obj, obj->ref_count, obj->ref_count + 1); +#endif + + bt_ctf_object_inc_ref_count(obj); + return obj; +} + +static inline +void bt_ctf_object_put_no_null_check(struct bt_ctf_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->ref_count > 0); + +#ifdef BT_LOGV + BT_LOGV("Decrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count - 1, + obj, obj->ref_count, obj->ref_count - 1); +#endif + + obj->ref_count--; + + if (obj->ref_count == 0) { + BT_ASSERT(obj->release_func); + obj->release_func(obj); + } +} + +#endif /* BABELTRACE_CTF_WRITER_OBJECT_INTERNAL_H */ diff --git a/src/ctf-writer/resolve.c b/src/ctf-writer/resolve.c new file mode 100644 index 00000000..6e9e2006 --- /dev/null +++ b/src/ctf-writer/resolve.c @@ -0,0 +1,1342 @@ +/* + * resolve.c + * + * Babeltrace - CTF writer: Type resolving internal + * + * Copyright 2015 Jérémie Galarneau + * Copyright 2016 Philippe Proulx + * + * Authors: Jérémie Galarneau + * Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-RESOLVE" +#include "logging.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common/babeltrace.h" +#include "common/assert.h" + +#include "field-path.h" +#include "resolve.h" +#include "utils.h" +#include "values.h" + +typedef GPtrArray type_stack; + +/* + * A stack frame. + * + * `type` contains a compound field type (structure, variant, array, + * or sequence) and `index` indicates the index of the field type in + * the upper frame (-1 for array and sequence field types). + * + * `type` is owned by the stack frame. + */ +struct type_stack_frame { + struct bt_ctf_field_type_common *type; + int index; +}; + +/* + * The current context of the resolving engine. + * + * `scopes` contain the 6 CTF scope field types (see CTF, sect. 7.3.2) + * in the following order: + * + * * Packet header + * * Packet context + * * Event header + * * Stream event context + * * Event context + * * Event payload + */ +struct resolve_context { + struct bt_ctf_private_value *environment; + struct bt_ctf_field_type_common *scopes[6]; + + /* Root scope being visited */ + enum bt_ctf_scope root_scope; + type_stack *type_stack; + struct bt_ctf_field_type_common *cur_field_type; +}; + +/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */ +static const char * const absolute_path_prefixes[] = { + [BT_CTF_SCOPE_ENV] = "env.", + [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = "trace.packet.header.", + [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = "stream.packet.context.", + [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = "stream.event.header.", + [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = "stream.event.context.", + [BT_CTF_SCOPE_EVENT_CONTEXT] = "event.context.", + [BT_CTF_SCOPE_EVENT_FIELDS] = "event.fields.", +}; + +/* Number of path tokens used for the absolute prefixes */ +static const int absolute_path_prefix_ptoken_counts[] = { + [BT_CTF_SCOPE_ENV] = 1, + [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = 3, + [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = 3, + [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = 3, + [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = 3, + [BT_CTF_SCOPE_EVENT_CONTEXT] = 2, + [BT_CTF_SCOPE_EVENT_FIELDS] = 2, +}; + +/* + * Destroys a type stack frame. + */ +static +void type_stack_destroy_notify(gpointer data) +{ + struct type_stack_frame *frame = data; + + BT_CTF_OBJECT_PUT_REF_AND_RESET(frame->type); + g_free(frame); +} + +/* + * Creates a type stack. + * + * Return value is owned by the caller. + */ +static +type_stack *type_stack_create(void) +{ + return g_ptr_array_new_with_free_func(type_stack_destroy_notify); +} + +/* + * Destroys a type stack. + */ +static +void type_stack_destroy(type_stack *stack) +{ + g_ptr_array_free(stack, TRUE); +} + +/* + * Pushes a field type onto a type stack. + * + * `type` is owned by the caller (stack frame gets a new reference). + */ +static +int type_stack_push(type_stack *stack, struct bt_ctf_field_type_common *type) +{ + int ret = 0; + struct type_stack_frame *frame = NULL; + + if (!stack || !type) { + BT_LOGW("Invalid parameter: stack or type is NULL."); + ret = -1; + goto end; + } + + frame = g_new0(struct type_stack_frame, 1); + if (!frame) { + BT_LOGE_STR("Failed to allocate one field type stack frame."); + ret = -1; + goto end; + } + + BT_LOGV("Pushing field type on context's stack: " + "ft-addr=%p, stack-size-before=%u", type, stack->len); + frame->type = bt_ctf_object_get_ref(type); + g_ptr_array_add(stack, frame); + +end: + return ret; +} + +/* + * Checks whether or not `stack` is empty. + */ +static +bt_bool type_stack_empty(type_stack *stack) +{ + return stack->len == 0; +} + +/* + * Returns the number of frames in `stack`. + */ +static +size_t type_stack_size(type_stack *stack) +{ + return stack->len; +} + +/* + * Returns the top frame of `stack`. + * + * Return value is owned by `stack`. + */ +static +struct type_stack_frame *type_stack_peek(type_stack *stack) +{ + struct type_stack_frame *entry = NULL; + + if (!stack || type_stack_empty(stack)) { + goto end; + } + + entry = g_ptr_array_index(stack, stack->len - 1); +end: + return entry; +} + +/* + * Returns the frame at index `index` in `stack`. + * + * Return value is owned by `stack`. + */ +static +struct type_stack_frame *type_stack_at(type_stack *stack, + size_t index) +{ + struct type_stack_frame *entry = NULL; + + if (!stack || index >= stack->len) { + goto end; + } + + entry = g_ptr_array_index(stack, index); + +end: + return entry; +} + +/* + * Removes the top frame of `stack`. + */ +static +void type_stack_pop(type_stack *stack) +{ + if (!type_stack_empty(stack)) { + /* + * This will call the frame's destructor and free it, as + * well as put its contained field type. + */ + BT_LOGV("Popping context's stack: stack-size-before=%u", + stack->len); + g_ptr_array_set_size(stack, stack->len - 1); + } +} + +/* + * Returns the scope field type of `scope` in the context `ctx`. + * + * Return value is owned by `ctx` on success. + */ +static +struct bt_ctf_field_type_common *get_type_from_ctx(struct resolve_context *ctx, + enum bt_ctf_scope scope) +{ + BT_ASSERT(scope >= BT_CTF_SCOPE_TRACE_PACKET_HEADER && + scope <= BT_CTF_SCOPE_EVENT_FIELDS); + + return ctx->scopes[scope - BT_CTF_SCOPE_TRACE_PACKET_HEADER]; +} + +/* + * Returns the CTF scope from a path string. May return + * CTF_NODE_UNKNOWN if the path is found to be relative. + */ +static +enum bt_ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr) +{ + enum bt_ctf_scope scope; + enum bt_ctf_scope ret = BT_CTF_SCOPE_UNKNOWN; + const size_t prefixes_count = sizeof(absolute_path_prefixes) / + sizeof(*absolute_path_prefixes); + + for (scope = BT_CTF_SCOPE_ENV; scope < BT_CTF_SCOPE_ENV + + prefixes_count; scope++) { + /* + * Chech if path string starts with a known absolute + * path prefix. + * + * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES. + */ + if (strncmp(pathstr, absolute_path_prefixes[scope], + strlen(absolute_path_prefixes[scope]))) { + /* Prefix does not match: try the next one */ + BT_LOGV("Prefix does not match: trying the next one: " + "path=\"%s\", path-prefix=\"%s\", scope=%s", + pathstr, absolute_path_prefixes[scope], + bt_ctf_scope_string(scope)); + continue; + } + + /* Found it! */ + ret = scope; + BT_LOGV("Found root scope from absolute path: " + "path=\"%s\", scope=%s", pathstr, + bt_ctf_scope_string(scope)); + goto end; + } + +end: + return ret; +} + +/* + * Destroys a path token. + */ +static +void ptokens_destroy_func(gpointer ptoken, gpointer data) +{ + g_string_free(ptoken, TRUE); +} + +/* + * Destroys a path token list. + */ +static +void ptokens_destroy(GList *ptokens) +{ + if (!ptokens) { + return; + } + + g_list_foreach(ptokens, ptokens_destroy_func, NULL); + g_list_free(ptokens); +} + +/* + * Returns the string contained in a path token. + */ +static +const char *ptoken_get_string(GList *ptoken) +{ + GString *tokenstr = (GString *) ptoken->data; + + return tokenstr->str; +} + +/* + * Converts a path string to a path token list, that is, splits the + * individual words of a path string into a list of individual + * strings. + * + * Return value is owned by the caller on success. + */ +static +GList *pathstr_to_ptokens(const char *pathstr) +{ + const char *at = pathstr; + const char *last = at; + GList *ptokens = NULL; + + for (;;) { + if (*at == '.' || *at == '\0') { + GString *tokenstr; + + if (at == last) { + /* Error: empty token */ + BT_LOGW("Empty path token: path=\"%s\", pos=%u", + pathstr, (int) (at - pathstr)); + goto error; + } + + tokenstr = g_string_new(NULL); + g_string_append_len(tokenstr, last, at - last); + ptokens = g_list_append(ptokens, tokenstr); + last = at + 1; + } + + if (*at == '\0') { + break; + } + + at++; + } + + return ptokens; + +error: + ptokens_destroy(ptokens); + return NULL; +} + +/* + * Converts a path token list to a field path object. The path token + * list is relative from `type`. The index of the source looking for + * its target within `type` is indicated by `src_index`. This can be + * `INT_MAX` if the source is contained in `type`. + * + * `ptokens` is owned by the caller. `field_path` is an output parameter + * owned by the caller that must be filled here. `type` is owned by the + * caller. + */ +static +int ptokens_to_field_path(GList *ptokens, struct bt_ctf_field_path *field_path, + struct bt_ctf_field_type_common *type, int src_index) +{ + int ret = 0; + GList *cur_ptoken = ptokens; + bt_bool first_level_done = BT_FALSE; + + /* Get our own reference */ + bt_ctf_object_get_ref(type); + + /* Locate target */ + while (cur_ptoken) { + int child_index; + struct bt_ctf_field_type_common *child_type; + const char *field_name = ptoken_get_string(cur_ptoken); + enum bt_ctf_field_type_id type_id = + bt_ctf_field_type_common_get_type_id(type); + + BT_LOGV("Current path token: token=\"%s\"", field_name); + + /* Find to which index corresponds the current path token */ + if (type_id == BT_CTF_FIELD_TYPE_ID_ARRAY || + type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE) { + child_index = -1; + } else { + child_index = bt_ctf_field_type_common_get_field_index(type, + field_name); + if (child_index < 0) { + /* + * Error: field name does not exist or + * wrong current type. + */ + BT_LOGW("Cannot get index of field type: " + "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", + field_name, src_index, child_index, first_level_done); + ret = -1; + goto end; + } else if (child_index > src_index && + !first_level_done) { + BT_LOGW("Child field type is located after source field type: " + "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", + field_name, src_index, child_index, first_level_done); + ret = -1; + goto end; + } + + /* Next path token */ + cur_ptoken = g_list_next(cur_ptoken); + first_level_done = BT_TRUE; + } + + /* Create new field path entry */ + g_array_append_val(field_path->indexes, child_index); + + /* Get child field type */ + child_type = bt_ctf_field_type_common_borrow_field_at_index(type, + child_index); + if (!child_type) { + BT_LOGW("Cannot get child field type: " + "field-name=\"%s\", src-index=%d, child-index=%d, first-level-done=%d", + field_name, src_index, child_index, first_level_done); + ret = -1; + goto end; + } + + /* Move child type to current type */ + bt_ctf_object_get_ref(child_type); + BT_CTF_OBJECT_MOVE_REF(type, child_type); + } + +end: + bt_ctf_object_put_ref(type); + return ret; +} + +/* + * Converts a known absolute path token list to a field path object + * within the resolving context `ctx`. + * + * `ptokens` is owned by the caller. `field_path` is an output parameter + * owned by the caller that must be filled here. + */ +static +int absolute_ptokens_to_field_path(GList *ptokens, + struct bt_ctf_field_path *field_path, + struct resolve_context *ctx) +{ + int ret = 0; + GList *cur_ptoken; + struct bt_ctf_field_type_common *type; + + /* Skip absolute path tokens */ + cur_ptoken = g_list_nth(ptokens, + absolute_path_prefix_ptoken_counts[field_path->root]); + + /* Start with root type */ + type = get_type_from_ctx(ctx, field_path->root); + if (!type) { + /* Error: root type is not available */ + BT_LOGW("Root field type is not available: " + "root-scope=%s", + bt_ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + /* Locate target */ + ret = ptokens_to_field_path(cur_ptoken, field_path, type, INT_MAX); + +end: + return ret; +} + +/* + * Converts a known relative path token list to a field path object + * within the resolving context `ctx`. + * + * `ptokens` is owned by the caller. `field_path` is an output parameter + * owned by the caller that must be filled here. + */ +static +int relative_ptokens_to_field_path(GList *ptokens, + struct bt_ctf_field_path *field_path, + struct resolve_context *ctx) +{ + int ret = 0; + int parent_pos_in_stack; + struct bt_ctf_field_path *tail_field_path = bt_ctf_field_path_create(); + + if (!tail_field_path) { + BT_LOGE_STR("Cannot create empty field path."); + ret = -1; + goto end; + } + + parent_pos_in_stack = type_stack_size(ctx->type_stack) - 1; + + while (parent_pos_in_stack >= 0) { + struct bt_ctf_field_type_common *parent_type = + type_stack_at(ctx->type_stack, + parent_pos_in_stack)->type; + int cur_index = type_stack_at(ctx->type_stack, + parent_pos_in_stack)->index; + + BT_LOGV("Locating target field type from current parent field type: " + "parent-pos=%d, parent-ft-addr=%p, cur-index=%d", + parent_pos_in_stack, parent_type, cur_index); + + /* Locate target from current parent type */ + ret = ptokens_to_field_path(ptokens, tail_field_path, + parent_type, cur_index); + if (ret) { + /* Not found... yet */ + BT_LOGV_STR("Not found at this point."); + bt_ctf_field_path_clear(tail_field_path); + } else { + /* Found: stitch tail field path to head field path */ + int i = 0; + int tail_field_path_len = + tail_field_path->indexes->len; + + while (BT_TRUE) { + struct bt_ctf_field_type_common *cur_type = + type_stack_at(ctx->type_stack, i)->type; + int index = type_stack_at( + ctx->type_stack, i)->index; + + if (cur_type == parent_type) { + break; + } + + g_array_append_val(field_path->indexes, + index); + i++; + } + + for (i = 0; i < tail_field_path_len; i++) { + int index = g_array_index( + tail_field_path->indexes, + int, i); + + g_array_append_val(field_path->indexes, + index); + } + break; + } + + parent_pos_in_stack--; + } + + if (parent_pos_in_stack < 0) { + /* Not found: look in previous scopes */ + field_path->root--; + + while (field_path->root >= BT_CTF_SCOPE_TRACE_PACKET_HEADER) { + struct bt_ctf_field_type_common *root_type; + bt_ctf_field_path_clear(field_path); + + BT_LOGV("Looking into potential root scope: scope=%s", + bt_ctf_scope_string(field_path->root)); + root_type = get_type_from_ctx(ctx, field_path->root); + if (!root_type) { + field_path->root--; + continue; + } + + /* Locate target in previous scope */ + ret = ptokens_to_field_path(ptokens, field_path, + root_type, INT_MAX); + if (ret) { + /* Not found yet */ + BT_LOGV_STR("Not found in this scope."); + field_path->root--; + continue; + } + + /* Found */ + BT_LOGV_STR("Found in this scope."); + break; + } + } + +end: + BT_CTF_OBJECT_PUT_REF_AND_RESET(tail_field_path); + return ret; +} + +/* + * Converts a path string to a field path object within the resolving + * context `ctx`. + * + * Return value is owned by the caller on success. + */ +static +struct bt_ctf_field_path *pathstr_to_field_path(const char *pathstr, + struct resolve_context *ctx) +{ + int ret; + enum bt_ctf_scope root_scope; + GList *ptokens = NULL; + struct bt_ctf_field_path *field_path = NULL; + + /* Create field path */ + field_path = bt_ctf_field_path_create(); + if (!field_path) { + BT_LOGE_STR("Cannot create empty field path."); + ret = -1; + goto end; + } + + /* Convert path string to path tokens */ + ptokens = pathstr_to_ptokens(pathstr); + if (!ptokens) { + BT_LOGW("Cannot convert path string to path tokens: " + "path=\"%s\"", pathstr); + ret = -1; + goto end; + } + + /* Absolute or relative path? */ + root_scope = get_root_scope_from_absolute_pathstr(pathstr); + + if (root_scope == BT_CTF_SCOPE_UNKNOWN) { + /* Relative path: start with current root scope */ + field_path->root = ctx->root_scope; + BT_LOGV("Detected relative path: starting with current root scope: " + "scope=%s", bt_ctf_scope_string(field_path->root)); + ret = relative_ptokens_to_field_path(ptokens, field_path, ctx); + if (ret) { + BT_LOGW("Cannot get relative field path of path string: " + "path=\"%s\", start-scope=%s, end-scope=%s", + pathstr, bt_ctf_scope_string(ctx->root_scope), + bt_ctf_scope_string(field_path->root)); + goto end; + } + } else if (root_scope == BT_CTF_SCOPE_ENV) { + BT_LOGW("Sequence field types referring the trace environment are not supported as of this version: " + "path=\"%s\"", pathstr); + ret = -1; + goto end; + } else { + /* Absolute path: use found root scope */ + field_path->root = root_scope; + BT_LOGV("Detected absolute path: using root scope: " + "scope=%s", bt_ctf_scope_string(field_path->root)); + ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx); + if (ret) { + BT_LOGW("Cannot get absolute field path of path string: " + "path=\"%s\", root-scope=%s", + pathstr, bt_ctf_scope_string(root_scope)); + goto end; + } + } + + if (ret == 0) { + GString *field_path_pretty = + bt_ctf_field_path_string(field_path); + const char *field_path_pretty_str = + field_path_pretty ? field_path_pretty->str : NULL; + + BT_LOGV("Found field path: path=\"%s\", field-path=\"%s\"", + pathstr, field_path_pretty_str); + + if (field_path_pretty) { + g_string_free(field_path_pretty, TRUE); + } + } + +end: + if (ret) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); + } + + ptokens_destroy(ptokens); + return field_path; +} + +/* + * Retrieves a field type by following the field path `field_path` in + * the resolving context `ctx`. + * + * Return value is owned by the caller on success. + */ +static +struct bt_ctf_field_type_common *field_path_to_field_type( + struct bt_ctf_field_path *field_path, + struct resolve_context *ctx) +{ + int i; + struct bt_ctf_field_type_common *type; + + /* Start with root type */ + type = get_type_from_ctx(ctx, field_path->root); + bt_ctf_object_get_ref(type); + if (!type) { + /* Error: root type is not available */ + BT_LOGW("Root field type is not available: root-scope=%s", + bt_ctf_scope_string(field_path->root)); + goto error; + } + + /* Locate target */ + for (i = 0; i < field_path->indexes->len; i++) { + struct bt_ctf_field_type_common *child_type; + int child_index = + g_array_index(field_path->indexes, int, i); + + /* Get child field type */ + child_type = bt_ctf_field_type_common_borrow_field_at_index(type, + child_index); + if (!child_type) { + BT_LOGW("Cannot get field type: " + "parent-ft-addr=%p, index=%d", type, i); + goto error; + } + + /* Move child type to current type */ + bt_ctf_object_get_ref(child_type); + BT_CTF_OBJECT_MOVE_REF(type, child_type); + } + + return type; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(type); + return type; +} + +/* + * Returns the equivalent field path object of the context type stack. + * + * Return value is owned by the caller on success. + */ +static +struct bt_ctf_field_path *get_ctx_stack_field_path(struct resolve_context *ctx) +{ + int i; + struct bt_ctf_field_path *field_path; + + /* Create field path */ + field_path = bt_ctf_field_path_create(); + if (!field_path) { + BT_LOGE_STR("Cannot create empty field path."); + goto error; + } + + field_path->root = ctx->root_scope; + + for (i = 0; i < type_stack_size(ctx->type_stack); i++) { + struct type_stack_frame *frame; + + frame = type_stack_at(ctx->type_stack, i); + g_array_append_val(field_path->indexes, frame->index); + } + + return field_path; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(field_path); + return field_path; +} + +/* + * Returns the lowest common ancestor of two field path objects + * having the same root scope. + * + * `field_path1` and `field_path2` are owned by the caller. + */ +static +int get_field_paths_lca_index(struct bt_ctf_field_path *field_path1, + struct bt_ctf_field_path *field_path2) +{ + int lca_index = 0; + int field_path1_len, field_path2_len; + + if (BT_LOG_ON_VERBOSE) { + GString *field_path1_pretty = + bt_ctf_field_path_string(field_path1); + GString *field_path2_pretty = + bt_ctf_field_path_string(field_path2); + const char *field_path1_pretty_str = + field_path1_pretty ? field_path1_pretty->str : NULL; + const char *field_path2_pretty_str = + field_path2_pretty ? field_path2_pretty->str : NULL; + + BT_LOGV("Finding lowest common ancestor (LCA) between two field paths: " + "field-path-1=\"%s\", field-path-2=\"%s\"", + field_path1_pretty_str, field_path2_pretty_str); + + if (field_path1_pretty) { + g_string_free(field_path1_pretty, TRUE); + } + + if (field_path2_pretty) { + g_string_free(field_path2_pretty, TRUE); + } + } + + /* + * Start from both roots and find the first mismatch. + */ + BT_ASSERT(field_path1->root == field_path2->root); + field_path1_len = field_path1->indexes->len; + field_path2_len = field_path2->indexes->len; + + while (BT_TRUE) { + int target_index, ctx_index; + + if (lca_index == field_path2_len || + lca_index == field_path1_len) { + /* + * This means that both field paths never split. + * This is invalid because the target cannot be + * an ancestor of the source. + */ + BT_LOGW("Source field type is an ancestor of target field type or vice versa: " + "lca-index=%d, field-path-1-len=%d, " + "field-path-2-len=%d", + lca_index, field_path1_len, field_path2_len); + lca_index = -1; + break; + } + + target_index = g_array_index(field_path1->indexes, int, + lca_index); + ctx_index = g_array_index(field_path2->indexes, int, + lca_index); + + if (target_index != ctx_index) { + /* LCA index is the previous */ + break; + } + + lca_index++; + } + + BT_LOGV("Found LCA: lca-index=%d", lca_index); + return lca_index; +} + +/* + * Validates a target field path. + * + * `target_field_path` and `target_type` are owned by the caller. + */ +static +int validate_target_field_path(struct bt_ctf_field_path *target_field_path, + struct bt_ctf_field_type_common *target_type, + struct resolve_context *ctx) +{ + int ret = 0; + struct bt_ctf_field_path *ctx_field_path; + int target_field_path_len = target_field_path->indexes->len; + int lca_index; + enum bt_ctf_field_type_id ctx_cur_field_type_id; + enum bt_ctf_field_type_id target_type_id; + + /* Get context field path */ + ctx_field_path = get_ctx_stack_field_path(ctx); + if (!ctx_field_path) { + BT_LOGW_STR("Cannot get field path from context's stack."); + ret = -1; + goto end; + } + + /* + * Make sure the target is not a root. + */ + if (target_field_path_len == 0) { + BT_LOGW_STR("Target field path's length is 0 (targeting the root)."); + ret = -1; + goto end; + } + + /* + * Make sure the root of the target field path is not located + * after the context field path's root. + */ + if (target_field_path->root > ctx_field_path->root) { + BT_LOGW("Target field type is located after source field type: " + "target-root=%s, source-root=%s", + bt_ctf_scope_string(target_field_path->root), + bt_ctf_scope_string(ctx_field_path->root)); + ret = -1; + goto end; + } + + if (target_field_path->root == ctx_field_path->root) { + int target_index, ctx_index; + + /* + * Find the index of the lowest common ancestor of both field + * paths. + */ + lca_index = get_field_paths_lca_index(target_field_path, + ctx_field_path); + if (lca_index < 0) { + BT_LOGW_STR("Cannot get least common ancestor."); + ret = -1; + goto end; + } + + /* + * Make sure the target field path is located before the + * context field path. + */ + target_index = g_array_index(target_field_path->indexes, + int, lca_index); + ctx_index = g_array_index(ctx_field_path->indexes, + int, lca_index); + + if (target_index >= ctx_index) { + BT_LOGW("Target field type's index is greater than or equal to source field type's index in LCA: " + "lca-index=%d, target-index=%d, source-index=%d", + lca_index, target_index, ctx_index); + ret = -1; + goto end; + } + } + + /* + * Make sure the target type has the right type and properties. + */ + ctx_cur_field_type_id = bt_ctf_field_type_common_get_type_id( + ctx->cur_field_type); + target_type_id = bt_ctf_field_type_common_get_type_id(target_type); + + switch (ctx_cur_field_type_id) { + case BT_CTF_FIELD_TYPE_ID_VARIANT: + if (target_type_id != BT_CTF_FIELD_TYPE_ID_ENUM) { + BT_LOGW("Variant field type's tag field type is not an enumeration field type: " + "tag-ft-addr=%p, tag-ft-id=%s", + target_type, + bt_ctf_field_type_id_string(target_type_id)); + ret = -1; + goto end; + } + break; + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + if (target_type_id != BT_CTF_FIELD_TYPE_ID_INTEGER || + bt_ctf_field_type_common_integer_is_signed(target_type)) { + BT_LOGW("Sequence field type's length field type is not an unsigned integer field type: " + "length-ft-addr=%p, length-ft-id=%s", + target_type, + bt_ctf_field_type_id_string(target_type_id)); + ret = -1; + goto end; + } + break; + default: + abort(); + } + +end: + BT_CTF_OBJECT_PUT_REF_AND_RESET(ctx_field_path); + return ret; +} + +/* + * Resolves a variant or sequence field type `type`. + * + * `type` is owned by the caller. + */ +static +int resolve_sequence_or_variant_type(struct bt_ctf_field_type_common *type, + struct resolve_context *ctx) +{ + int ret = 0; + const char *pathstr; + enum bt_ctf_field_type_id type_id = bt_ctf_field_type_common_get_type_id(type); + struct bt_ctf_field_path *target_field_path = NULL; + struct bt_ctf_field_type_common *target_type = NULL; + GString *target_field_path_pretty = NULL; + const char *target_field_path_pretty_str; + + + /* Get path string */ + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + pathstr = + bt_ctf_field_type_common_sequence_get_length_field_name(type); + break; + case BT_CTF_FIELD_TYPE_ID_VARIANT: + pathstr = + bt_ctf_field_type_common_variant_get_tag_name(type); + break; + default: + abort(); + } + + if (!pathstr) { + BT_LOGW_STR("Cannot get path string."); + ret = -1; + goto end; + } + + /* Get target field path out of path string */ + target_field_path = pathstr_to_field_path(pathstr, ctx); + if (!target_field_path) { + BT_LOGW("Cannot get target field path for path string: " + "path=\"%s\"", pathstr); + ret = -1; + goto end; + } + + target_field_path_pretty = bt_ctf_field_path_string(target_field_path); + target_field_path_pretty_str = + target_field_path_pretty ? target_field_path_pretty->str : NULL; + + /* Get target field type */ + target_type = field_path_to_field_type(target_field_path, ctx); + if (!target_type) { + BT_LOGW("Cannot get target field type for path string: " + "path=\"%s\", target-field-path=\"%s\"", + pathstr, target_field_path_pretty_str); + ret = -1; + goto end; + } + + ret = validate_target_field_path(target_field_path, target_type, ctx); + if (ret) { + BT_LOGW("Invalid target field path for path string: " + "path=\"%s\", target-field-path=\"%s\"", + pathstr, target_field_path_pretty_str); + goto end; + } + + /* Set target field path and target field type */ + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + ret = bt_ctf_field_type_common_sequence_set_length_field_path( + type, target_field_path); + if (ret) { + BT_LOGW("Cannot set sequence field type's length field path: " + "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", + ret, type, pathstr, + target_field_path_pretty_str); + goto end; + } + break; + case BT_CTF_FIELD_TYPE_ID_VARIANT: + ret = bt_ctf_field_type_common_variant_set_tag_field_path( + type, target_field_path); + if (ret) { + BT_LOGW("Cannot set varaint field type's tag field path: " + "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", + ret, type, pathstr, + target_field_path_pretty_str); + goto end; + } + + ret = bt_ctf_field_type_common_variant_set_tag_field_type( + type, target_type); + if (ret) { + BT_LOGW("Cannot set varaint field type's tag field type: " + "ret=%d, ft-addr=%p, path=\"%s\", target-field-path=\"%s\"", + ret, type, pathstr, + target_field_path_pretty_str); + goto end; + } + break; + default: + abort(); + } + +end: + if (target_field_path_pretty) { + g_string_free(target_field_path_pretty, TRUE); + } + + BT_CTF_OBJECT_PUT_REF_AND_RESET(target_field_path); + BT_CTF_OBJECT_PUT_REF_AND_RESET(target_type); + return ret; +} + +/* + * Resolves a field type `type`. + * + * `type` is owned by the caller. + */ +static +int resolve_type(struct bt_ctf_field_type_common *type, struct resolve_context *ctx) +{ + int ret = 0; + enum bt_ctf_field_type_id type_id; + + if (!type) { + /* Type is not available; still valid */ + goto end; + } + + type_id = bt_ctf_field_type_common_get_type_id(type); + ctx->cur_field_type = type; + + /* Resolve sequence/variant field type */ + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + case BT_CTF_FIELD_TYPE_ID_VARIANT: + ret = resolve_sequence_or_variant_type(type, ctx); + if (ret) { + BT_LOGW("Cannot resolve sequence field type's length or variant field type's tag: " + "ret=%d, ft-addr=%p", ret, type); + goto end; + } + break; + default: + break; + } + + /* Recurse into compound types */ + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_STRUCT: + case BT_CTF_FIELD_TYPE_ID_VARIANT: + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + case BT_CTF_FIELD_TYPE_ID_ARRAY: + { + int64_t field_count, f_index; + + ret = type_stack_push(ctx->type_stack, type); + if (ret) { + BT_LOGW("Cannot push field type on context's stack: " + "ft-addr=%p", type); + goto end; + } + + field_count = bt_ctf_field_type_common_get_field_count(type); + if (field_count < 0) { + BT_LOGW("Cannot get field type's field count: " + "ret=%" PRId64 ", ft-addr=%p", + field_count, type); + ret = field_count; + goto end; + } + + for (f_index = 0; f_index < field_count; f_index++) { + struct bt_ctf_field_type_common *child_type = + bt_ctf_field_type_common_borrow_field_at_index(type, + f_index); + + if (!child_type) { + BT_LOGW("Cannot get field type's child field: " + "ft-addr=%p, index=%" PRId64 ", " + "count=%" PRId64, type, f_index, + field_count); + ret = -1; + goto end; + } + + if (type_id == BT_CTF_FIELD_TYPE_ID_ARRAY|| + type_id == BT_CTF_FIELD_TYPE_ID_SEQUENCE) { + type_stack_peek(ctx->type_stack)->index = -1; + } else { + type_stack_peek(ctx->type_stack)->index = + f_index; + } + + BT_LOGV("Resolving field type's child field type: " + "parent-ft-addr=%p, child-ft-addr=%p, " + "index=%" PRId64 ", count=%" PRId64, + type, child_type, f_index, field_count); + ret = resolve_type(child_type, ctx); + if (ret) { + goto end; + } + } + + type_stack_pop(ctx->type_stack); + break; + } + default: + break; + } + +end: + return ret; +} + +/* + * Resolves the root field type corresponding to the scope `root_scope`. + */ +static +int resolve_root_type(enum bt_ctf_scope root_scope, struct resolve_context *ctx) +{ + int ret; + + BT_ASSERT(type_stack_size(ctx->type_stack) == 0); + ctx->root_scope = root_scope; + ret = resolve_type(get_type_from_ctx(ctx, root_scope), ctx); + ctx->root_scope = BT_CTF_SCOPE_UNKNOWN; + + return ret; +} + +BT_HIDDEN +int bt_ctf_resolve_types( + struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type, + struct bt_ctf_field_type_common *event_context_type, + struct bt_ctf_field_type_common *event_payload_type, + enum bt_ctf_resolve_flag flags) +{ + int ret = 0; + struct resolve_context ctx = { + .environment = environment, + .scopes = { + packet_header_type, + packet_context_type, + event_header_type, + stream_event_ctx_type, + event_context_type, + event_payload_type, + }, + .root_scope = BT_CTF_SCOPE_UNKNOWN, + }; + + BT_LOGV("Resolving field types: " + "packet-header-ft-addr=%p, " + "packet-context-ft-addr=%p, " + "event-header-ft-addr=%p, " + "stream-event-context-ft-addr=%p, " + "event-context-ft-addr=%p, " + "event-payload-ft-addr=%p", + packet_header_type, packet_context_type, event_header_type, + stream_event_ctx_type, event_context_type, event_payload_type); + + /* Initialize type stack */ + ctx.type_stack = type_stack_create(); + if (!ctx.type_stack) { + BT_LOGE_STR("Cannot create field type stack."); + ret = -1; + goto end; + } + + /* Resolve packet header type */ + if (flags & BT_CTF_RESOLVE_FLAG_PACKET_HEADER) { + ret = resolve_root_type(BT_CTF_SCOPE_TRACE_PACKET_HEADER, &ctx); + if (ret) { + BT_LOGW("Cannot resolve trace packet header field type: " + "ret=%d", ret); + goto end; + } + } + + /* Resolve packet context type */ + if (flags & BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT) { + ret = resolve_root_type(BT_CTF_SCOPE_STREAM_PACKET_CONTEXT, &ctx); + if (ret) { + BT_LOGW("Cannot resolve stream packet context field type: " + "ret=%d", ret); + goto end; + } + } + + /* Resolve event header type */ + if (flags & BT_CTF_RESOLVE_FLAG_EVENT_HEADER) { + ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_HEADER, &ctx); + if (ret) { + BT_LOGW("Cannot resolve stream event header field type: " + "ret=%d", ret); + goto end; + } + } + + /* Resolve stream event context type */ + if (flags & BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX) { + ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_CONTEXT, &ctx); + if (ret) { + BT_LOGW("Cannot resolve stream event context field type: " + "ret=%d", ret); + goto end; + } + } + + /* Resolve event context type */ + if (flags & BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT) { + ret = resolve_root_type(BT_CTF_SCOPE_EVENT_CONTEXT, &ctx); + if (ret) { + BT_LOGW("Cannot resolve event context field type: " + "ret=%d", ret); + goto end; + } + } + + /* Resolve event payload type */ + if (flags & BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD) { + ret = resolve_root_type(BT_CTF_SCOPE_EVENT_FIELDS, &ctx); + if (ret) { + BT_LOGW("Cannot resolve event payload field type: " + "ret=%d", ret); + goto end; + } + } + + BT_LOGV_STR("Resolved field types."); + +end: + type_stack_destroy(ctx.type_stack); + + return ret; +} diff --git a/src/ctf-writer/resolve.h b/src/ctf-writer/resolve.h new file mode 100644 index 00000000..f153d1c9 --- /dev/null +++ b/src/ctf-writer/resolve.h @@ -0,0 +1,71 @@ +#ifndef BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H +#define BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H + +/* + * Copyright 2015 Jérémie Galarneau + * Copyright 2016 Philippe Proulx + * + * Authors: Jérémie Galarneau + * Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include + +#include "field-types.h" +#include "values.h" + +enum bt_ctf_resolve_flag { + BT_CTF_RESOLVE_FLAG_PACKET_HEADER = 0x01, + BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT = 0x02, + BT_CTF_RESOLVE_FLAG_EVENT_HEADER = 0x04, + BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX = 0x08, + BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT = 0x10, + BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD = 0x20, +}; + +/* + * Resolves CTF IR field types: recursively locates the tag and length + * field types of resp. variant and sequence field types. + * + * All `*_type` parameters may be resolved, and may as well serve as + * resolving targets. + * + * Resolving is performed based on the flags in `flags`. + * + * It is expected that, amongst all the provided types, no common + * references to sequence variant field types exist. In other words, + * this function does not copy field types. + * + * All parameters are owned by the caller. + */ +BT_HIDDEN +int bt_ctf_resolve_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type, + struct bt_ctf_field_type_common *event_context_type, + struct bt_ctf_field_type_common *event_payload_type, + enum bt_ctf_resolve_flag flags); + +#endif /* BABELTRACE_CTF_WRITER_RESOLVE_INTERNAL_H */ diff --git a/src/ctf-writer/stream-class.c b/src/ctf-writer/stream-class.c new file mode 100644 index 00000000..8b5e39b4 --- /dev/null +++ b/src/ctf-writer/stream-class.c @@ -0,0 +1,1148 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-STREAM-CLASS" +#include "logging.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "common/align.h" +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" + +#include "assert-pre.h" +#include "clock-class.h" +#include "event-class.h" +#include "event.h" +#include "fields.h" +#include "field-types.h" +#include "field-wrapper.h" +#include "stream-class.h" +#include "utils.h" +#include "validation.h" +#include "visitor.h" +#include "writer.h" + +BT_HIDDEN +int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class, + const char *name, bt_ctf_object_release_func release_func) +{ + BT_LOGD("Initializing common stream class object: name=\"%s\"", name); + + bt_ctf_object_init_shared_with_parent(&stream_class->base, release_func); + stream_class->name = g_string_new(name); + stream_class->event_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_ctf_object_try_spec_release); + if (!stream_class->event_classes) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + stream_class->event_classes_ht = g_hash_table_new_full(g_int64_hash, + g_int64_equal, g_free, NULL); + if (!stream_class->event_classes_ht) { + BT_LOGE_STR("Failed to allocate a GHashTable."); + goto error; + } + + BT_LOGD("Initialized common stream class object: addr=%p, name=\"%s\"", + stream_class, name); + return 0; + +error: + return -1; +} + +BT_HIDDEN +void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class) +{ + BT_LOGD("Finalizing common stream class: addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + bt_ctf_object_put_ref(stream_class->clock_class); + + if (stream_class->event_classes_ht) { + g_hash_table_destroy(stream_class->event_classes_ht); + } + if (stream_class->event_classes) { + BT_LOGD_STR("Destroying event classes."); + g_ptr_array_free(stream_class->event_classes, TRUE); + } + + if (stream_class->name) { + g_string_free(stream_class->name, TRUE); + } + + BT_LOGD_STR("Putting event header field type."); + bt_ctf_object_put_ref(stream_class->event_header_field_type); + BT_LOGD_STR("Putting packet context field type."); + bt_ctf_object_put_ref(stream_class->packet_context_field_type); + BT_LOGD_STR("Putting event context field type."); + bt_ctf_object_put_ref(stream_class->event_context_field_type); +} + +static +void event_class_exists(gpointer element, gpointer query) +{ + struct bt_ctf_event_class_common *event_class_a = element; + struct bt_ctf_search_query *search_query = query; + struct bt_ctf_event_class_common *event_class_b = search_query->value; + int64_t id_a, id_b; + + if (search_query->value == element) { + search_query->found = 1; + goto end; + } + + /* + * Two event classes cannot share the same ID in a given + * stream class. + */ + id_a = bt_ctf_event_class_common_get_id(event_class_a); + id_b = bt_ctf_event_class_common_get_id(event_class_b); + + if (id_a < 0 || id_b < 0) { + /* at least one ID is not set: will be automatically set later */ + goto end; + } + + if (id_a == id_b) { + BT_LOGW("Event class with this ID already exists in the stream class: " + "id=%" PRId64 ", name=\"%s\"", + id_a, bt_ctf_event_class_common_get_name(event_class_a)); + search_query->found = 1; + goto end; + } + +end: + return; +} + +BT_HIDDEN +int bt_ctf_stream_class_common_add_event_class( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_event_class_common *event_class, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) +{ + int ret = 0; + int64_t *event_id = NULL; + struct bt_ctf_trace_common *trace = NULL; + struct bt_ctf_stream_class_common *old_stream_class = NULL; + struct bt_ctf_validation_output validation_output = { 0 }; + struct bt_ctf_field_type_common *packet_header_type = NULL; + struct bt_ctf_field_type_common *packet_context_type = NULL; + struct bt_ctf_field_type_common *event_header_type = NULL; + struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; + struct bt_ctf_field_type_common *event_context_type = NULL; + struct bt_ctf_field_type_common *event_payload_type = NULL; + const enum bt_ctf_validation_flag validation_flags = + BT_CTF_VALIDATION_FLAG_EVENT; + struct bt_ctf_clock_class *expected_clock_class = NULL; + + BT_ASSERT(copy_field_type_func); + + if (!stream_class || !event_class) { + BT_LOGW("Invalid parameter: stream class or event class is NULL: " + "stream-class-addr=%p, event-class-addr=%p", + stream_class, event_class); + ret = -1; + goto end; + } + + BT_LOGD("Adding event class to stream class: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", event-class-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_class, + bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + trace = bt_ctf_stream_class_common_borrow_trace(stream_class); + + if (stream_class->frozen) { + /* + * We only check that the event class to be added has a + * single class which matches the stream class's + * expected clock class if the stream class is frozen. + * If it's not, then this event class is added "as is" + * and the validation will be performed when calling + * either bt_ctf_trace_add_stream_class() or + * bt_ctf_event_create(). This is because the stream class's + * field types (packet context, event header, event + * context) could change before the next call to one of + * those two functions. + */ + expected_clock_class = bt_ctf_object_get_ref(stream_class->clock_class); + + /* + * At this point, `expected_clock_class` can be NULL, + * and bt_ctf_event_class_validate_single_clock_class() + * below can set it. + */ + ret = bt_ctf_event_class_common_validate_single_clock_class( + event_class, &expected_clock_class); + if (ret) { + BT_LOGW("Event class contains a field type which is not " + "recursively mapped to its stream class's " + "expected clock class: " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "stream-class-name=\"%s\", " + "expected-clock-class-addr=%p, " + "expected-clock-class-name=\"%s\"", + stream_class, + bt_ctf_stream_class_common_get_id(stream_class), + bt_ctf_stream_class_common_get_name(stream_class), + expected_clock_class, + expected_clock_class ? + bt_ctf_clock_class_get_name(expected_clock_class) : + NULL); + goto end; + } + } + + event_id = g_new(int64_t, 1); + if (!event_id) { + BT_LOGE_STR("Failed to allocate one int64_t."); + ret = -1; + goto end; + } + + /* Check for duplicate event classes */ + struct bt_ctf_search_query query = { .value = event_class, .found = 0 }; + g_ptr_array_foreach(stream_class->event_classes, event_class_exists, + &query); + if (query.found) { + BT_LOGW_STR("Another event class part of this stream class has the same ID."); + ret = -1; + goto end; + } + + old_stream_class = bt_ctf_event_class_common_borrow_stream_class(event_class); + if (old_stream_class) { + /* Event class is already associated to a stream class. */ + BT_LOGW("Event class is already part of another stream class: " + "event-class-stream-class-addr=%p, " + "event-class-stream-class-name=\"%s\", " + "event-class-stream-class-id=%" PRId64, + old_stream_class, + bt_ctf_stream_class_common_get_name(old_stream_class), + bt_ctf_stream_class_common_get_id(old_stream_class)); + ret = -1; + goto end; + } + + if (trace) { + /* + * If the stream class is associated with a trace, then + * both those objects are frozen. Also, this event class + * is about to be frozen. + * + * Therefore the event class must be validated here. + * The trace and stream class should be valid at this + * point. + */ + BT_ASSERT(trace->valid); + BT_ASSERT(stream_class->valid); + packet_header_type = + bt_ctf_trace_common_borrow_packet_header_field_type(trace); + packet_context_type = + bt_ctf_stream_class_common_borrow_packet_context_field_type( + stream_class); + event_header_type = + bt_ctf_stream_class_common_borrow_event_header_field_type( + stream_class); + stream_event_ctx_type = + bt_ctf_stream_class_common_borrow_event_context_field_type( + stream_class); + event_context_type = + bt_ctf_event_class_common_borrow_context_field_type( + event_class); + event_payload_type = + bt_ctf_event_class_common_borrow_payload_field_type( + event_class); + ret = bt_ctf_validate_class_types( + trace->environment, packet_header_type, + packet_context_type, event_header_type, + stream_event_ctx_type, event_context_type, + event_payload_type, trace->valid, + stream_class->valid, event_class->valid, + &validation_output, validation_flags, + copy_field_type_func); + + if (ret) { + /* + * This means something went wrong during the + * validation process, not that the objects are + * invalid. + */ + BT_LOGE("Failed to validate event class: ret=%d", ret); + goto end; + } + + if ((validation_output.valid_flags & validation_flags) != + validation_flags) { + /* Invalid event class */ + BT_LOGW("Invalid trace, stream class, or event class: " + "valid-flags=0x%x", + validation_output.valid_flags); + ret = -1; + goto end; + } + } + + /* Only set an event ID if none was explicitly set before */ + *event_id = bt_ctf_event_class_common_get_id(event_class); + if (*event_id < 0) { + BT_LOGV("Event class has no ID: automatically setting it: " + "id=%" PRId64, stream_class->next_event_id); + + if (bt_ctf_event_class_common_set_id(event_class, + stream_class->next_event_id)) { + BT_LOGE("Cannot set event class's ID: id=%" PRId64, + stream_class->next_event_id); + ret = -1; + goto end; + } + stream_class->next_event_id++; + *event_id = stream_class->next_event_id; + } + + bt_ctf_object_set_parent(&event_class->base, &stream_class->base); + + if (trace) { + /* + * At this point we know that the function will be + * successful. Therefore we can replace the event + * class's field types with what's in the validation + * output structure and mark this event class as valid. + */ + bt_ctf_validation_replace_types(NULL, NULL, event_class, + &validation_output, validation_flags); + event_class->valid = 1; + + /* + * Put what was not moved in + * bt_ctf_validation_replace_types(). + */ + bt_ctf_validation_output_put_types(&validation_output); + } + + /* Add to the event classes of the stream class */ + g_ptr_array_add(stream_class->event_classes, event_class); + g_hash_table_insert(stream_class->event_classes_ht, event_id, + event_class); + event_id = NULL; + + /* Freeze the event class */ + bt_ctf_event_class_common_freeze(event_class); + + /* + * It is safe to set the stream class's unique clock class + * now if the stream class is frozen. + */ + if (stream_class->frozen && expected_clock_class) { + BT_ASSERT(!stream_class->clock_class || + stream_class->clock_class == expected_clock_class); + BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); + } + + BT_LOGD("Added event class to stream class: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", event-class-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_class, + bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + +end: + bt_ctf_validation_output_put_types(&validation_output); + bt_ctf_object_put_ref(expected_clock_class); + g_free(event_id); + return ret; +} + +static +int64_t get_event_class_count(void *element) +{ + return bt_ctf_stream_class_get_event_class_count( + (struct bt_ctf_stream_class *) element); +} + +static +void *get_event_class(void *element, int i) +{ + return bt_ctf_stream_class_get_event_class_by_index( + (struct bt_ctf_stream_class *) element, i); +} + +static +int visit_event_class(void *object, bt_ctf_visitor visitor,void *data) +{ + struct bt_ctf_visitor_object obj = { + .object = object, + .type = BT_CTF_VISITOR_OBJECT_TYPE_EVENT_CLASS + }; + + return visitor(&obj, data); +} + +BT_HIDDEN +int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class, + bt_ctf_visitor visitor, void *data) +{ + int ret; + struct bt_ctf_visitor_object obj = { + .object = stream_class, + .type = BT_CTF_VISITOR_OBJECT_TYPE_STREAM_CLASS + }; + + if (!stream_class || !visitor) { + BT_LOGW("Invalid parameter: stream class or visitor is NULL: " + "stream-class-addr=%p, visitor=%p", + stream_class, visitor); + ret = -1; + goto end; + } + + ret = bt_ctf_visitor_helper(&obj, get_event_class_count, + get_event_class, + visit_event_class, visitor, data); + BT_LOGV("bt_ctf_visitor_helper() returned: ret=%d", ret); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class, + bt_ctf_visitor visitor, void *data) +{ + return bt_ctf_stream_class_common_visit(BT_CTF_FROM_COMMON(stream_class), + visitor, data); +} + +BT_HIDDEN +void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class) +{ + if (!stream_class || stream_class->frozen) { + return; + } + + BT_LOGD("Freezing stream class: addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + stream_class->frozen = 1; + bt_ctf_field_type_common_freeze(stream_class->event_header_field_type); + bt_ctf_field_type_common_freeze(stream_class->packet_context_field_type); + bt_ctf_field_type_common_freeze(stream_class->event_context_field_type); + bt_ctf_clock_class_freeze(stream_class->clock_class); +} + +BT_HIDDEN +int bt_ctf_stream_class_common_validate_single_clock_class( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_clock_class **expected_clock_class) +{ + int ret; + uint64_t i; + + BT_ASSERT(stream_class); + BT_ASSERT(expected_clock_class); + ret = bt_ctf_field_type_common_validate_single_clock_class( + stream_class->packet_context_field_type, + expected_clock_class); + if (ret) { + BT_LOGW("Stream class's packet context field type " + "is not recursively mapped to the " + "expected clock class: " + "stream-class-addr=%p, " + "stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", " + "ft-addr=%p", + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + stream_class->id, + stream_class->packet_context_field_type); + goto end; + } + + ret = bt_ctf_field_type_common_validate_single_clock_class( + stream_class->event_header_field_type, + expected_clock_class); + if (ret) { + BT_LOGW("Stream class's event header field type " + "is not recursively mapped to the " + "expected clock class: " + "stream-class-addr=%p, " + "stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", " + "ft-addr=%p", + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + stream_class->id, + stream_class->event_header_field_type); + goto end; + } + + ret = bt_ctf_field_type_common_validate_single_clock_class( + stream_class->event_context_field_type, + expected_clock_class); + if (ret) { + BT_LOGW("Stream class's event context field type " + "is not recursively mapped to the " + "expected clock class: " + "stream-class-addr=%p, " + "stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", " + "ft-addr=%p", + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + stream_class->id, + stream_class->event_context_field_type); + goto end; + } + + for (i = 0; i < stream_class->event_classes->len; i++) { + struct bt_ctf_event_class_common *event_class = + g_ptr_array_index(stream_class->event_classes, i); + + BT_ASSERT(event_class); + ret = bt_ctf_event_class_common_validate_single_clock_class( + event_class, expected_clock_class); + if (ret) { + BT_LOGW("Stream class's event class contains a " + "field type which is not recursively mapped to " + "the expected clock class: " + "stream-class-addr=%p, " + "stream-class-name=\"%s\", " + "stream-class-id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + stream_class->id); + goto end; + } + } + +end: + return ret; +} + +static +int init_event_header(struct bt_ctf_stream_class *stream_class) +{ + int ret = 0; + struct bt_ctf_field_type *event_header_type = + bt_ctf_field_type_structure_create(); + struct bt_ctf_field_type *_uint32_t = + get_field_type(FIELD_TYPE_ALIAS_UINT32_T); + struct bt_ctf_field_type *_uint64_t = + get_field_type(FIELD_TYPE_ALIAS_UINT64_T); + + if (!event_header_type) { + BT_LOGE_STR("Cannot create empty structure field type."); + ret = -1; + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(event_header_type, + _uint32_t, "id"); + if (ret) { + BT_LOGE_STR("Cannot add `id` field to event header field type."); + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(event_header_type, + _uint64_t, "timestamp"); + if (ret) { + BT_LOGE_STR("Cannot add `timestamp` field to event header field type."); + goto end; + } + + bt_ctf_object_put_ref(stream_class->common.event_header_field_type); + stream_class->common.event_header_field_type = + (void *) event_header_type; + event_header_type = NULL; + +end: + if (ret) { + bt_ctf_object_put_ref(event_header_type); + } + + bt_ctf_object_put_ref(_uint32_t); + bt_ctf_object_put_ref(_uint64_t); + return ret; +} + +static +int init_packet_context(struct bt_ctf_stream_class *stream_class) +{ + int ret = 0; + struct bt_ctf_field_type *packet_context_type = + bt_ctf_field_type_structure_create(); + struct bt_ctf_field_type *_uint64_t = + get_field_type(FIELD_TYPE_ALIAS_UINT64_T); + struct bt_ctf_field_type *ts_begin_end_uint64_t; + + if (!packet_context_type) { + BT_LOGE_STR("Cannot create empty structure field type."); + ret = -1; + goto end; + } + + ts_begin_end_uint64_t = bt_ctf_field_type_copy(_uint64_t); + if (!ts_begin_end_uint64_t) { + BT_LOGE_STR("Cannot copy integer field type for `timestamp_begin` and `timestamp_end` fields."); + ret = -1; + goto end; + } + + /* + * We create a stream packet context as proposed in the CTF + * specification. + */ + ret = bt_ctf_field_type_structure_add_field(packet_context_type, + ts_begin_end_uint64_t, "timestamp_begin"); + if (ret) { + BT_LOGE_STR("Cannot add `timestamp_begin` field to event header field type."); + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(packet_context_type, + ts_begin_end_uint64_t, "timestamp_end"); + if (ret) { + BT_LOGE_STR("Cannot add `timestamp_end` field to event header field type."); + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(packet_context_type, + _uint64_t, "content_size"); + if (ret) { + BT_LOGE_STR("Cannot add `content_size` field to event header field type."); + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(packet_context_type, + _uint64_t, "packet_size"); + if (ret) { + BT_LOGE_STR("Cannot add `packet_size` field to event header field type."); + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(packet_context_type, + _uint64_t, "events_discarded"); + if (ret) { + BT_LOGE_STR("Cannot add `events_discarded` field to event header field type."); + goto end; + } + + bt_ctf_object_put_ref(stream_class->common.packet_context_field_type); + stream_class->common.packet_context_field_type = + (void *) packet_context_type; + packet_context_type = NULL; + +end: + if (ret) { + bt_ctf_object_put_ref(packet_context_type); + goto end; + } + + bt_ctf_object_put_ref(_uint64_t); + bt_ctf_object_put_ref(ts_begin_end_uint64_t); + return ret; +} + +static +void bt_ctf_stream_class_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_stream_class *stream_class; + + stream_class = (void *) obj; + BT_LOGD("Destroying CTF writer stream class: addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class)); + bt_ctf_stream_class_common_finalize(BT_CTF_TO_COMMON(stream_class)); + bt_ctf_object_put_ref(stream_class->clock); + g_free(stream_class); +} + +struct bt_ctf_stream_class *bt_ctf_stream_class_create(const char *name) +{ + struct bt_ctf_stream_class *stream_class; + int ret; + + BT_LOGD("Creating CTF writer stream class object: name=\"%s\"", name); + stream_class = g_new0(struct bt_ctf_stream_class, 1); + if (!stream_class) { + BT_LOGE_STR("Failed to allocate one CTF writer stream class."); + goto error; + } + + ret = bt_ctf_stream_class_common_initialize(BT_CTF_TO_COMMON(stream_class), + name, bt_ctf_stream_class_destroy); + if (ret) { + /* bt_ctf_stream_class_common_initialize() logs errors */ + goto error; + } + + ret = init_event_header(stream_class); + if (ret) { + BT_LOGE_STR("Cannot initialize stream class's event header field type."); + goto error; + } + + ret = init_packet_context(stream_class); + if (ret) { + BT_LOGE_STR("Cannot initialize stream class's packet context field type."); + goto error; + } + + BT_LOGD("Created CTF writer stream class object: addr=%p, name=\"%s\"", + stream_class, name); + return stream_class; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_class); + return stream_class; +} + +static +int try_map_clock_class(struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *parent_ft, const char *field_name) +{ + struct bt_ctf_clock_class *mapped_clock_class = NULL; + int ret = 0; + struct bt_ctf_field_type *ft = + bt_ctf_field_type_structure_get_field_type_by_name(parent_ft, + field_name); + + BT_ASSERT(stream_class->clock); + + if (!ft) { + /* Field does not exist: not an error */ + goto end; + } + + BT_ASSERT(((struct bt_ctf_field_type_common *) ft)->id == + BT_CTF_FIELD_TYPE_ID_INTEGER); + mapped_clock_class = + bt_ctf_field_type_integer_get_mapped_clock_class(ft); + if (!mapped_clock_class) { + struct bt_ctf_field_type *ft_copy; + + if (!stream_class->clock) { + BT_LOGW("Cannot automatically set field's type mapped clock class: stream class's clock is not set: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", ft-addr=%p", + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class), ft); + ret = -1; + goto end; + } + + ft_copy = bt_ctf_field_type_copy(ft); + if (!ft_copy) { + BT_LOGE("Failed to copy integer field type: ft-addr=%p", + ft); + } + + ret = bt_ctf_field_type_common_integer_set_mapped_clock_class_no_check_frozen( + (void *) ft_copy, stream_class->clock->clock_class); + BT_ASSERT(ret == 0); + + ret = bt_ctf_field_type_common_structure_replace_field( + (void *) parent_ft, field_name, (void *) ft_copy); + bt_ctf_object_put_ref(ft_copy); + BT_LOGV("Automatically mapped field type to stream class's clock class: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", ft-addr=%p, " + "ft-copy-addr=%p", + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class), ft, ft_copy); + } + +end: + bt_ctf_object_put_ref(ft); + bt_ctf_object_put_ref(mapped_clock_class); + return ret; +} + +BT_HIDDEN +int bt_ctf_stream_class_map_clock_class( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *packet_context_type, + struct bt_ctf_field_type *event_header_type) +{ + int ret = 0; + + BT_ASSERT(stream_class); + + if (!stream_class->clock) { + /* No clock class to map to */ + goto end; + } + + if (packet_context_type) { + if (try_map_clock_class(stream_class, packet_context_type, + "timestamp_begin")) { + BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_begin` field's mapped clock class."); + ret = -1; + goto end; + } + + if (try_map_clock_class(stream_class, packet_context_type, + "timestamp_end")) { + BT_LOGE_STR("Cannot automatically set stream class's packet context field type's `timestamp_end` field's mapped clock class."); + ret = -1; + goto end; + } + } + + if (event_header_type) { + if (try_map_clock_class(stream_class, event_header_type, + "timestamp")) { + BT_LOGE_STR("Cannot automatically set stream class's event header field type's `timestamp` field's mapped clock class."); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +struct bt_ctf_clock *bt_ctf_stream_class_get_clock( + struct bt_ctf_stream_class *stream_class) +{ + struct bt_ctf_clock *clock = NULL; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + goto end; + } + + if (!stream_class->clock) { + BT_LOGV("Stream class has no clock: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class)); + goto end; + } + + clock = bt_ctf_object_get_ref(stream_class->clock); + +end: + return clock; +} + +int bt_ctf_stream_class_set_clock( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_clock *clock) +{ + int ret = 0; + + if (!stream_class || !clock) { + BT_LOGW("Invalid parameter: stream class or clock is NULL: " + "stream-class-addr=%p, clock-addr=%p", + stream_class, clock); + ret = -1; + goto end; + } + + if (stream_class->common.frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class)); + ret = -1; + goto end; + } + + /* Replace the current clock of this stream class. */ + bt_ctf_object_put_ref(stream_class->clock); + stream_class->clock = bt_ctf_object_get_ref(clock); + BT_LOGV("Set stream class's clock: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "clock-addr=%p, clock-name=\"%s\"", + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class), + stream_class->clock, + bt_ctf_clock_get_name(stream_class->clock)); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class, + struct metadata_context *context) +{ + int ret = 0; + size_t i; + struct bt_ctf_trace *trace; + struct bt_ctf_field_type *packet_header_type = NULL; + + BT_LOGD("Serializing stream class's metadata: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", metadata-context-addr=%p", + stream_class, + bt_ctf_stream_class_get_name(stream_class), + bt_ctf_stream_class_get_id(stream_class), context); + g_string_assign(context->field_name, ""); + context->current_indentation_level = 1; + if (!stream_class->common.id_set) { + BT_LOGW_STR("Stream class's ID is not set."); + ret = -1; + goto end; + } + + g_string_append(context->string, "stream {\n"); + + /* + * The reference to the trace is only borrowed since the + * serialization of the stream class might have been triggered + * by the trace's destruction. In such a case, the trace's + * reference count would, unexepectedly, go through the sequence + * 1 -> 0 -> 1 -> 0 -> ..., provoking an endless loop of destruction + * and serialization. + */ + trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( + BT_CTF_TO_COMMON(stream_class))); + BT_ASSERT(trace); + packet_header_type = bt_ctf_trace_get_packet_header_field_type(trace); + trace = NULL; + if (packet_header_type) { + struct bt_ctf_field_type *stream_id_type; + + stream_id_type = + bt_ctf_field_type_structure_get_field_type_by_name( + packet_header_type, "stream_id"); + if (stream_id_type) { + /* + * Only set the stream's id if the trace's packet header + * contains a stream_id field. This field is only + * needed if the trace contains only one stream + * class. + */ + g_string_append_printf(context->string, + "\tid = %" PRId64 ";\n", + stream_class->common.id); + } + bt_ctf_object_put_ref(stream_id_type); + } + if (stream_class->common.event_header_field_type) { + BT_LOGD_STR("Serializing stream class's event header field type's metadata."); + g_string_append(context->string, "\tevent.header := "); + ret = bt_ctf_field_type_serialize_recursive( + (void *) stream_class->common.event_header_field_type, + context); + if (ret) { + BT_LOGW("Cannot serialize stream class's event header field type's metadata: " + "ret=%d", ret); + goto end; + } + g_string_append(context->string, ";"); + } + + + if (stream_class->common.packet_context_field_type) { + BT_LOGD_STR("Serializing stream class's packet context field type's metadata."); + g_string_append(context->string, "\n\n\tpacket.context := "); + ret = bt_ctf_field_type_serialize_recursive( + (void *) stream_class->common.packet_context_field_type, + context); + if (ret) { + BT_LOGW("Cannot serialize stream class's packet context field type's metadata: " + "ret=%d", ret); + goto end; + } + g_string_append(context->string, ";"); + } + + if (stream_class->common.event_context_field_type) { + BT_LOGD_STR("Serializing stream class's event context field type's metadata."); + g_string_append(context->string, "\n\n\tevent.context := "); + ret = bt_ctf_field_type_serialize_recursive( + (void *) stream_class->common.event_context_field_type, + context); + if (ret) { + BT_LOGW("Cannot serialize stream class's event context field type's metadata: " + "ret=%d", ret); + goto end; + } + g_string_append(context->string, ";"); + } + + g_string_append(context->string, "\n};\n\n"); + + for (i = 0; i < stream_class->common.event_classes->len; i++) { + struct bt_ctf_event_class *event_class = + stream_class->common.event_classes->pdata[i]; + + ret = bt_ctf_event_class_serialize(event_class, context); + if (ret) { + BT_LOGW("Cannot serialize event class's metadata: " + "event-class-addr=%p, event-class-name=\"%s\", " + "event-class-id=%" PRId64, + event_class, + bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class)); + goto end; + } + } + +end: + bt_ctf_object_put_ref(packet_header_type); + context->current_indentation_level = 0; + return ret; +} + +struct bt_ctf_trace *bt_ctf_stream_class_get_trace( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_object_get_ref(bt_ctf_stream_class_common_borrow_trace( + BT_CTF_TO_COMMON(stream_class))); +} + +const char *bt_ctf_stream_class_get_name( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_stream_class_common_get_name(BT_CTF_TO_COMMON(stream_class)); +} + +int bt_ctf_stream_class_set_name( + struct bt_ctf_stream_class *stream_class, const char *name) +{ + return bt_ctf_stream_class_common_set_name(BT_CTF_TO_COMMON(stream_class), + name); +} + +int64_t bt_ctf_stream_class_get_id( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_stream_class_common_get_id(BT_CTF_TO_COMMON(stream_class)); +} + +int bt_ctf_stream_class_set_id( + struct bt_ctf_stream_class *stream_class, uint64_t id) +{ + return bt_ctf_stream_class_common_set_id(BT_CTF_TO_COMMON(stream_class), id); +} + +struct bt_ctf_field_type *bt_ctf_stream_class_get_packet_context_type( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_object_get_ref( + bt_ctf_stream_class_common_borrow_packet_context_field_type( + BT_CTF_TO_COMMON(stream_class))); +} + +int bt_ctf_stream_class_set_packet_context_type( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *packet_context_type) +{ + return bt_ctf_stream_class_common_set_packet_context_field_type( + BT_CTF_TO_COMMON(stream_class), (void *) packet_context_type); +} + +struct bt_ctf_field_type * +bt_ctf_stream_class_get_event_header_type( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_object_get_ref( + bt_ctf_stream_class_common_borrow_event_header_field_type( + BT_CTF_TO_COMMON(stream_class))); +} + +int bt_ctf_stream_class_set_event_header_type( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *event_header_type) +{ + return bt_ctf_stream_class_common_set_event_header_field_type( + BT_CTF_TO_COMMON(stream_class), (void *) event_header_type); +} + +struct bt_ctf_field_type * +bt_ctf_stream_class_get_event_context_type( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_object_get_ref( + bt_ctf_stream_class_common_borrow_event_context_field_type( + BT_CTF_TO_COMMON(stream_class))); +} + +int bt_ctf_stream_class_set_event_context_type( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *event_context_type) +{ + return bt_ctf_stream_class_common_set_event_context_field_type( + BT_CTF_TO_COMMON(stream_class), (void *) event_context_type); +} + +int64_t bt_ctf_stream_class_get_event_class_count( + struct bt_ctf_stream_class *stream_class) +{ + return bt_ctf_stream_class_common_get_event_class_count( + BT_CTF_TO_COMMON(stream_class)); +} + +struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_index( + struct bt_ctf_stream_class *stream_class, uint64_t index) +{ + return bt_ctf_object_get_ref( + bt_ctf_stream_class_common_borrow_event_class_by_index( + BT_CTF_TO_COMMON(stream_class), index)); +} + +struct bt_ctf_event_class *bt_ctf_stream_class_get_event_class_by_id( + struct bt_ctf_stream_class *stream_class, uint64_t id) +{ + return bt_ctf_object_get_ref( + bt_ctf_stream_class_common_borrow_event_class_by_id( + BT_CTF_TO_COMMON(stream_class), id)); +} + +int bt_ctf_stream_class_add_event_class( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_event_class *event_class) +{ + return bt_ctf_stream_class_common_add_event_class( + BT_CTF_TO_COMMON(stream_class), BT_CTF_TO_COMMON(event_class), + (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy); +} diff --git a/src/ctf-writer/stream-class.h b/src/ctf-writer/stream-class.h new file mode 100644 index 00000000..c008a1fa --- /dev/null +++ b/src/ctf-writer/stream-class.h @@ -0,0 +1,539 @@ +#ifndef BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H +#define BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H + +/* + * Copyright 2014 EfficiOS Inc. + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "common/assert.h" +#include "common/babeltrace.h" +#include "common/common.h" +#include +#include +#include + +#include "clock.h" +#include "field-types.h" +#include "object.h" +#include "stream-class.h" +#include "utils.h" +#include "validation.h" + +struct bt_ctf_stream_class_common { + struct bt_ctf_object base; + GString *name; + + /* Array of pointers to event class addresses */ + GPtrArray *event_classes; + + /* event class id (int64_t) to event class address */ + GHashTable *event_classes_ht; + int id_set; + int64_t id; + int64_t next_event_id; + struct bt_ctf_field_type_common *packet_context_field_type; + struct bt_ctf_field_type_common *event_header_field_type; + struct bt_ctf_field_type_common *event_context_field_type; + int frozen; + int byte_order; + + /* + * This flag indicates if the stream class is valid. A valid + * stream class is _always_ frozen. + */ + int valid; + + /* + * Unique clock class mapped to any field type within this + * stream class, including all the stream class's event class + * field types. This is only set if the stream class is frozen. + * + * If the stream class is frozen and this is still NULL, it is + * still possible that it becomes non-NULL because + * bt_ctf_stream_class_add_event_class() can add an event class + * containing a field type mapped to some clock class. In this + * case, this is the mapped clock class, and at this point, both + * the new event class and the stream class are frozen, so the + * next added event classes are expected to contain field types + * which only map to this specific clock class. + * + * If this is a CTF writer stream class, then this is the + * backing clock class of the `clock` member above. + */ + struct bt_ctf_clock_class *clock_class; +}; + +struct bt_ctf_event_class_common; + +BT_HIDDEN +int bt_ctf_stream_class_common_initialize(struct bt_ctf_stream_class_common *stream_class, + const char *name, bt_ctf_object_release_func release_func); + +BT_HIDDEN +void bt_ctf_stream_class_common_finalize(struct bt_ctf_stream_class_common *stream_class); + +BT_HIDDEN +void bt_ctf_stream_class_common_freeze(struct bt_ctf_stream_class_common *stream_class); + +static inline +const char *bt_ctf_stream_class_common_get_name( + struct bt_ctf_stream_class_common *stream_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->name->len > 0 ? stream_class->name->str : NULL; +} + +static inline +int64_t bt_ctf_stream_class_common_get_id( + struct bt_ctf_stream_class_common *stream_class) +{ + int64_t ret; + + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + + if (!stream_class->id_set) { + BT_LOGV("Stream class's ID is not set: addr=%p, name=\"%s\"", + stream_class, + bt_ctf_stream_class_common_get_name(stream_class)); + ret = (int64_t) -1; + goto end; + } + + ret = stream_class->id; + +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_stream_class_common_set_byte_order( + struct bt_ctf_stream_class_common *stream_class, int byte_order); + +BT_HIDDEN +int bt_ctf_stream_class_common_validate_single_clock_class( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_clock_class **expected_clock_class); + +BT_HIDDEN +int bt_ctf_stream_class_common_add_event_class( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_event_class_common *event_class, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func); + +BT_HIDDEN +int bt_ctf_stream_class_common_visit(struct bt_ctf_stream_class_common *stream_class, + bt_ctf_visitor visitor, void *data); + +BT_HIDDEN +int bt_ctf_stream_class_visit(struct bt_ctf_stream_class *stream_class, + bt_ctf_visitor visitor, void *data); + +static inline +struct bt_ctf_trace_common *bt_ctf_stream_class_common_borrow_trace( + struct bt_ctf_stream_class_common *stream_class) +{ + BT_ASSERT(stream_class); + return (void *) bt_ctf_object_borrow_parent(&stream_class->base); +} + +static inline +int bt_ctf_stream_class_common_set_name(struct bt_ctf_stream_class_common *stream_class, + const char *name) +{ + int ret = 0; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + ret = -1; + goto end; + } + + if (!name) { + g_string_assign(stream_class->name, ""); + } else { + if (strlen(name) == 0) { + BT_LOGW("Invalid parameter: name is empty."); + ret = -1; + goto end; + } + + g_string_assign(stream_class->name, name); + } + + BT_LOGV("Set stream class's name: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); +end: + return ret; +} + +static inline +void _bt_ctf_stream_class_common_set_id( + struct bt_ctf_stream_class_common *stream_class, int64_t id) +{ + BT_ASSERT(stream_class); + stream_class->id = id; + stream_class->id_set = 1; + BT_LOGV("Set stream class's ID (internal): " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); +} + +static inline +int bt_ctf_stream_class_common_set_id_no_check( + struct bt_ctf_stream_class_common *stream_class, int64_t id) +{ + _bt_ctf_stream_class_common_set_id(stream_class, id); + return 0; +} + +static inline +int bt_ctf_stream_class_common_set_id(struct bt_ctf_stream_class_common *stream_class, + uint64_t id_param) +{ + int ret = 0; + int64_t id = (int64_t) id_param; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + ret = -1; + goto end; + } + + if (id < 0) { + BT_LOGW("Invalid parameter: invalid stream class's ID: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64 ", id=%" PRIu64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + id_param); + ret = -1; + goto end; + } + + ret = bt_ctf_stream_class_common_set_id_no_check(stream_class, id); + if (ret == 0) { + BT_LOGV("Set stream class's ID: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + } +end: + return ret; +} + +static inline +int64_t bt_ctf_stream_class_common_get_event_class_count( + struct bt_ctf_stream_class_common *stream_class) +{ + int64_t ret; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = (int64_t) -1; + goto end; + } + + ret = (int64_t) stream_class->event_classes->len; +end: + return ret; +} + +static inline +struct bt_ctf_event_class_common *bt_ctf_stream_class_common_borrow_event_class_by_index( + struct bt_ctf_stream_class_common *stream_class, uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_CTF_ASSERT_PRE(index < stream_class->event_classes->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u", + index, stream_class->event_classes->len); + return g_ptr_array_index(stream_class->event_classes, index); +} + +static inline +struct bt_ctf_event_class_common *bt_ctf_stream_class_common_borrow_event_class_by_id( + struct bt_ctf_stream_class_common *stream_class, uint64_t id) +{ + int64_t id_key = (int64_t) id; + + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_CTF_ASSERT_PRE(id_key >= 0, + "Invalid event class ID: %" PRIu64, id); + return g_hash_table_lookup(stream_class->event_classes_ht, + &id_key); +} + +static inline +struct bt_ctf_field_type_common * +bt_ctf_stream_class_common_borrow_packet_context_field_type( + struct bt_ctf_stream_class_common *stream_class) +{ + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->packet_context_field_type; +} + +static inline +int bt_ctf_stream_class_common_set_packet_context_field_type( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_type) +{ + int ret = 0; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + ret = -1; + goto end; + } + + if (packet_context_type && + bt_ctf_field_type_common_get_type_id(packet_context_type) != + BT_CTF_FIELD_TYPE_ID_STRUCT) { + /* A packet context must be a structure. */ + BT_LOGW("Invalid parameter: stream class's packet context field type must be a structure: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "packet-context-ft-addr=%p, packet-context-ft-id=%s", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + packet_context_type, + bt_ctf_field_type_id_string( + bt_ctf_field_type_common_get_type_id(packet_context_type))); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(stream_class->packet_context_field_type); + stream_class->packet_context_field_type = packet_context_type; + bt_ctf_object_get_ref(stream_class->packet_context_field_type); + BT_LOGV("Set stream class's packet context field type: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "packet-context-ft-addr=%p", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + packet_context_type); + +end: + return ret; +} + +static inline +struct bt_ctf_field_type_common * +bt_ctf_stream_class_common_borrow_event_header_field_type( + struct bt_ctf_stream_class_common *stream_class) +{ + struct bt_ctf_field_type_common *ret = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + + if (!stream_class->event_header_field_type) { + BT_LOGV("Stream class has no event header field type: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + goto end; + } + + ret = stream_class->event_header_field_type; + +end: + return ret; +} + +static inline +int bt_ctf_stream_class_common_set_event_header_field_type( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *event_header_type) +{ + int ret = 0; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + ret = -1; + goto end; + } + + if (event_header_type && + bt_ctf_field_type_common_get_type_id(event_header_type) != + BT_CTF_FIELD_TYPE_ID_STRUCT) { + /* An event header must be a structure. */ + BT_LOGW("Invalid parameter: stream class's event header field type must be a structure: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "event-header-ft-addr=%p, event-header-ft-id=%s", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_header_type, + bt_ctf_field_type_id_string( + bt_ctf_field_type_common_get_type_id(event_header_type))); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(stream_class->event_header_field_type); + stream_class->event_header_field_type = event_header_type; + bt_ctf_object_get_ref(stream_class->event_header_field_type); + BT_LOGV("Set stream class's event header field type: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "event-header-ft-addr=%p", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_header_type); +end: + return ret; +} + +static inline +struct bt_ctf_field_type_common * +bt_ctf_stream_class_common_borrow_event_context_field_type( + struct bt_ctf_stream_class_common *stream_class) +{ + struct bt_ctf_field_type_common *ret = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + + if (!stream_class->event_context_field_type) { + goto end; + } + + ret = stream_class->event_context_field_type; + +end: + return ret; +} + +static inline +int bt_ctf_stream_class_common_set_event_context_field_type( + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *event_context_type) +{ + int ret = 0; + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->frozen) { + BT_LOGW("Invalid parameter: stream class is frozen: " + "addr=%p, name=\"%s\", id=%" PRId64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + ret = -1; + goto end; + } + + if (event_context_type && + bt_ctf_field_type_common_get_type_id(event_context_type) != + BT_CTF_FIELD_TYPE_ID_STRUCT) { + /* A packet context must be a structure. */ + BT_LOGW("Invalid parameter: stream class's event context field type must be a structure: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "event-context-ft-addr=%p, event-context-ft-id=%s", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_context_type, + bt_ctf_field_type_id_string( + bt_ctf_field_type_common_get_type_id(event_context_type))); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(stream_class->event_context_field_type); + stream_class->event_context_field_type = event_context_type; + bt_ctf_object_get_ref(stream_class->event_context_field_type); + BT_LOGV("Set stream class's event context field type: " + "addr=%p, name=\"%s\", id=%" PRId64 ", " + "event-context-ft-addr=%p", + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class), + event_context_type); +end: + return ret; +} + +struct bt_ctf_stream_class { + struct bt_ctf_stream_class_common common; + struct bt_ctf_clock *clock; + int64_t next_stream_id; +}; + +struct metadata_context; + +BT_HIDDEN +int bt_ctf_stream_class_serialize(struct bt_ctf_stream_class *stream_class, + struct metadata_context *context); + +BT_HIDDEN +int bt_ctf_stream_class_map_clock_class( + struct bt_ctf_stream_class *stream_class, + struct bt_ctf_field_type *packet_context_type, + struct bt_ctf_field_type *event_header_type); + +#endif /* BABELTRACE_CTF_WRITER_STREAM_CLASS_INTERNAL_H */ diff --git a/src/ctf-writer/stream.c b/src/ctf-writer/stream.c new file mode 100644 index 00000000..a1025c75 --- /dev/null +++ b/src/ctf-writer/stream.c @@ -0,0 +1,1956 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-STREAM" +#include "logging.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "common/align.h" +#include "common/assert.h" +#include "compat/compiler.h" +#include "ctfser/ctfser.h" + +#include "assert-pre.h" +#include "event-class.h" +#include "event.h" +#include "fields.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" +#include "writer.h" + +BT_HIDDEN +void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream) +{ + BT_LOGD("Finalizing common stream object: addr=%p, name=\"%s\"", + stream, bt_ctf_stream_common_get_name(stream)); + + if (stream->name) { + g_string_free(stream->name, TRUE); + } +} + +BT_HIDDEN +int bt_ctf_stream_common_initialize( + struct bt_ctf_stream_common *stream, + struct bt_ctf_stream_class_common *stream_class, const char *name, + uint64_t id, bt_ctf_object_release_func release_func) +{ + int ret = 0; + struct bt_ctf_trace_common *trace = NULL; + + bt_ctf_object_init_shared_with_parent(&stream->base, release_func); + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + goto error; + } + + BT_LOGD("Initializing common stream object: stream-class-addr=%p, " + "stream-class-name=\"%s\", stream-name=\"%s\", " + "stream-id=%" PRIu64, + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + name, id); + trace = bt_ctf_stream_class_common_borrow_trace(stream_class); + if (!trace) { + BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-name=\"%s\"", + stream_class, + bt_ctf_stream_class_common_get_name(stream_class), name); + goto error; + } + + if (id != -1ULL) { + /* + * Validate that the given ID is unique amongst all the + * existing trace's streams created from the same stream + * class. + */ + size_t i; + + for (i = 0; i < trace->streams->len; i++) { + struct bt_ctf_stream_common *trace_stream = + g_ptr_array_index(trace->streams, i); + + if (trace_stream->stream_class != (void *) stream_class) { + continue; + } + + if (trace_stream->id == id) { + BT_LOGW_STR("Invalid parameter: another stream in the same trace already has this ID."); + goto error; + } + } + } + + /* + * Acquire reference to parent since stream will become publicly + * reachable; it needs its parent to remain valid. + */ + bt_ctf_object_set_parent(&stream->base, &trace->base); + stream->stream_class = stream_class; + stream->id = (int64_t) id; + + if (name) { + stream->name = g_string_new(name); + if (!stream->name) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + } + + BT_LOGD("Set common stream's trace parent: trace-addr=%p", trace); + + /* Add this stream to the trace's streams */ + BT_LOGD("Created common stream object: addr=%p", stream); + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +void bt_ctf_stream_destroy(struct bt_ctf_object *obj); + +static +int try_set_structure_field_integer(struct bt_ctf_field *, char *, uint64_t); + +static +int set_integer_field_value(struct bt_ctf_field* field, uint64_t value) +{ + int ret = 0; + struct bt_ctf_field_type *field_type = NULL; + + if (!field) { + BT_LOGW_STR("Invalid parameter: field is NULL."); + ret = -1; + goto end; + } + + field_type = bt_ctf_field_get_type(field); + BT_ASSERT(field_type); + + if (bt_ctf_field_type_get_type_id(field_type) != + BT_CTF_FIELD_TYPE_ID_INTEGER) { + /* Not an integer and the value is unset, error. */ + BT_LOGW("Invalid parameter: field's type is not an integer field type: " + "field-addr=%p, ft-addr=%p, ft-id=%s", + field, field_type, + bt_ctf_field_type_id_string((int) + bt_ctf_field_type_get_type_id(field_type))); + ret = -1; + goto end; + } + + if (bt_ctf_field_type_integer_is_signed(field_type)) { + ret = bt_ctf_field_integer_signed_set_value(field, (int64_t) value); + if (ret) { + /* Value is out of range, error. */ + BT_LOGW("Cannot set signed integer field's value: " + "addr=%p, value=%" PRId64, + field, (int64_t) value); + goto end; + } + } else { + ret = bt_ctf_field_integer_unsigned_set_value(field, value); + if (ret) { + /* Value is out of range, error. */ + BT_LOGW("Cannot set unsigned integer field's value: " + "addr=%p, value=%" PRIu64, + field, value); + goto end; + } + } +end: + bt_ctf_object_put_ref(field_type); + return ret; +} + +static +int set_packet_header_magic(struct bt_ctf_stream *stream) +{ + int ret = 0; + struct bt_ctf_field *magic_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_header, "magic"); + const uint32_t magic_value = 0xc1fc1fc1; + + BT_ASSERT(stream); + + if (!magic_field) { + /* No magic field found. Not an error, skip. */ + BT_LOGV("No field named `magic` in packet header: skipping: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + ret = bt_ctf_field_integer_unsigned_set_value(magic_field, + (uint64_t) magic_value); + + if (ret) { + BT_LOGW("Cannot set packet header field's `magic` integer field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + magic_field, (uint64_t) magic_value); + } else { + BT_LOGV("Set packet header field's `magic` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + magic_field, (uint64_t) magic_value); + } +end: + bt_ctf_object_put_ref(magic_field); + return ret; +} + +static +int set_packet_header_uuid(struct bt_ctf_stream *stream) +{ + int ret = 0; + int64_t i; + struct bt_ctf_trace *trace = NULL; + struct bt_ctf_field *uuid_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_header, "uuid"); + + BT_ASSERT(stream); + + if (!uuid_field) { + /* No uuid field found. Not an error, skip. */ + BT_LOGV("No field named `uuid` in packet header: skipping: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + trace = (struct bt_ctf_trace *) + bt_ctf_object_get_parent(&stream->common.base); + + for (i = 0; i < 16; i++) { + struct bt_ctf_field *uuid_element = + bt_ctf_field_array_get_field(uuid_field, i); + + ret = bt_ctf_field_integer_unsigned_set_value( + uuid_element, (uint64_t) trace->common.uuid[i]); + bt_ctf_object_put_ref(uuid_element); + if (ret) { + BT_LOGW("Cannot set integer field's value (for `uuid` packet header field): " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, " + "value=%" PRIu64 ", index=%" PRId64, + stream, bt_ctf_stream_get_name(stream), + uuid_element, (uint64_t) trace->common.uuid[i], i); + goto end; + } + } + + BT_LOGV("Set packet header field's `uuid` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p", + stream, bt_ctf_stream_get_name(stream), uuid_field); + +end: + bt_ctf_object_put_ref(uuid_field); + BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); + return ret; +} +static +int set_packet_header_stream_id(struct bt_ctf_stream *stream) +{ + int ret = 0; + uint32_t stream_id; + struct bt_ctf_field *stream_id_field = + bt_ctf_field_structure_get_field_by_name( + stream->packet_header, "stream_id"); + + if (!stream_id_field) { + /* No stream_id field found. Not an error, skip. */ + BT_LOGV("No field named `stream_id` in packet header: skipping: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + stream_id = stream->common.stream_class->id; + ret = bt_ctf_field_integer_unsigned_set_value(stream_id_field, + (uint64_t) stream_id); + if (ret) { + BT_LOGW("Cannot set packet header field's `stream_id` integer field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + stream_id_field, (uint64_t) stream_id); + } else { + BT_LOGV("Set packet header field's `stream_id` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + stream_id_field, (uint64_t) stream_id); + } + +end: + bt_ctf_object_put_ref(stream_id_field); + return ret; +} + +static +int auto_populate_packet_header(struct bt_ctf_stream *stream) +{ + int ret = 0; + + if (!stream->packet_header) { + goto end; + } + + ret = set_packet_header_magic(stream); + if (ret) { + BT_LOGW("Cannot set packet header's magic number field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + ret = set_packet_header_uuid(stream); + if (ret) { + BT_LOGW("Cannot set packet header's UUID field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + ret = set_packet_header_stream_id(stream); + if (ret) { + BT_LOGW("Cannot set packet header's stream class ID field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + BT_LOGV("Automatically populated stream's packet header's known fields: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + +end: + return ret; +} + +static +int set_packet_context_packet_size(struct bt_ctf_stream *stream, + uint64_t packet_size_bits) +{ + int ret = 0; + struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "packet_size"); + + ret = bt_ctf_field_integer_unsigned_set_value(field, packet_size_bits); + if (ret) { + BT_LOGW("Cannot set packet context field's `packet_size` integer field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, packet_size_bits); + } else { + BT_LOGV("Set packet context field's `packet_size` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, packet_size_bits); + } + + bt_ctf_object_put_ref(field); + return ret; +} + +static +int set_packet_context_content_size(struct bt_ctf_stream *stream, + uint64_t content_size_bits) +{ + int ret = 0; + struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "content_size"); + + BT_ASSERT(stream); + + if (!field) { + /* No content size field found. Not an error, skip. */ + BT_LOGV("No field named `content_size` in packet context: skipping: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + ret = bt_ctf_field_integer_unsigned_set_value(field, content_size_bits); + if (ret) { + BT_LOGW("Cannot set packet context field's `content_size` integer field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, content_size_bits); + } else { + BT_LOGV("Set packet context field's `content_size` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, content_size_bits); + } + +end: + bt_ctf_object_put_ref(field); + return ret; +} + +static +int set_packet_context_events_discarded(struct bt_ctf_stream *stream) +{ + int ret = 0; + struct bt_ctf_field *field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "events_discarded"); + + BT_ASSERT(stream); + + if (!field) { + /* No discarded events count field found. Not an error, skip. */ + BT_LOGV("No field named `events_discarded` in packet context: skipping: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + /* + * If the field is set by the user, make sure that the value is + * greater than or equal to the stream's current count of + * discarded events. We do not allow wrapping here. If it's + * valid, update the stream's current count. + */ + if (bt_ctf_field_is_set_recursive(field)) { + uint64_t user_val; + + ret = bt_ctf_field_integer_unsigned_get_value(field, + &user_val); + if (ret) { + BT_LOGW("Cannot get packet context `events_discarded` field's unsigned value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p", + stream, bt_ctf_stream_get_name(stream), field); + goto end; + } + + if (user_val < stream->discarded_events) { + BT_LOGW("Invalid packet context `events_discarded` field's unsigned value: " + "value is lesser than the stream's current discarded events count: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, " + "value=%" PRIu64 ", " + "stream-discarded-events-count=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), field, + user_val, stream->discarded_events); + goto end; + } + + stream->discarded_events = user_val; + } else { + ret = bt_ctf_field_integer_unsigned_set_value(field, + stream->discarded_events); + if (ret) { + BT_LOGW("Cannot set packet context field's `events_discarded` integer field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, stream->discarded_events); + } else { + BT_LOGV("Set packet context field's `events_discarded` field's value: " + "stream-addr=%p, stream-name=\"%s\", field-addr=%p, value=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + field, stream->discarded_events); + } + } + +end: + bt_ctf_object_put_ref(field); + return ret; +} + +static +void update_clock_value(uint64_t *val, uint64_t new_val, + unsigned int new_val_size) +{ + const uint64_t pow2 = 1ULL << new_val_size; + const uint64_t mask = pow2 - 1; + uint64_t val_masked; + +#ifdef BT_LOG_ENABLED_VERBOSE + uint64_t old_val = *val; +#endif + + if (new_val_size == 64) { + *val = new_val; + goto end; + } + + val_masked = *val & mask; + + if (new_val < val_masked) { + /* Wrapped once */ + new_val |= pow2; + } + + *val &= ~mask; + *val |= new_val; + +end: + BT_LOGV("Updated clock value: old-val=%" PRIu64 ", new-val=%" PRIu64, + old_val, *val); + return; +} + +static +int visit_field_update_clock_value(struct bt_ctf_field *field, uint64_t *val) +{ + int ret = 0; + struct bt_ctf_field_common *field_common = (void *) field; + + if (!field) { + goto end; + } + + switch (bt_ctf_field_get_type_id(field)) { + case BT_CTF_FIELD_TYPE_ID_INTEGER: + { + struct bt_ctf_clock_class *cc = + bt_ctf_field_type_integer_get_mapped_clock_class( + (void *) field_common->type); + int val_size; + uint64_t uval; + + if (!cc) { + goto end; + } + + bt_ctf_object_put_ref(cc); + val_size = bt_ctf_field_type_integer_get_size( + (void *) field_common->type); + BT_ASSERT(val_size >= 1); + + if (bt_ctf_field_type_integer_is_signed( + (void *) field_common->type)) { + int64_t ival; + + ret = bt_ctf_field_integer_signed_get_value(field, &ival); + uval = (uint64_t) ival; + } else { + ret = bt_ctf_field_integer_unsigned_get_value(field, &uval); + } + + if (ret) { + /* Not set */ + goto end; + } + + update_clock_value(val, uval, val_size); + break; + } + case BT_CTF_FIELD_TYPE_ID_ENUM: + { + struct bt_ctf_field *int_field = + bt_ctf_field_enumeration_get_container(field); + + BT_ASSERT(int_field); + ret = visit_field_update_clock_value(int_field, val); + bt_ctf_object_put_ref(int_field); + break; + } + case BT_CTF_FIELD_TYPE_ID_ARRAY: + { + uint64_t i; + int64_t len = bt_ctf_field_type_array_get_length( + (void *) field_common->type); + + BT_ASSERT(len >= 0); + + for (i = 0; i < len; i++) { + struct bt_ctf_field *elem_field = + bt_ctf_field_array_get_field(field, i); + + BT_ASSERT(elem_field); + ret = visit_field_update_clock_value(elem_field, val); + bt_ctf_object_put_ref(elem_field); + if (ret) { + goto end; + } + } + break; + } + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + { + uint64_t i; + int64_t len = bt_ctf_field_common_sequence_get_length( + (void *) field); + + if (len < 0) { + ret = -1; + goto end; + } + + for (i = 0; i < len; i++) { + struct bt_ctf_field *elem_field = + bt_ctf_field_sequence_get_field(field, i); + + BT_ASSERT(elem_field); + ret = visit_field_update_clock_value(elem_field, val); + bt_ctf_object_put_ref(elem_field); + if (ret) { + goto end; + } + } + break; + } + case BT_CTF_FIELD_TYPE_ID_STRUCT: + { + uint64_t i; + int64_t len = bt_ctf_field_type_structure_get_field_count( + (void *) field_common->type); + + BT_ASSERT(len >= 0); + + for (i = 0; i < len; i++) { + struct bt_ctf_field *member_field = + bt_ctf_field_structure_get_field_by_index(field, i); + + BT_ASSERT(member_field); + ret = visit_field_update_clock_value(member_field, val); + bt_ctf_object_put_ref(member_field); + if (ret) { + goto end; + } + } + break; + } + case BT_CTF_FIELD_TYPE_ID_VARIANT: + { + struct bt_ctf_field *cur_field = + bt_ctf_field_variant_get_current_field(field); + + if (!cur_field) { + ret = -1; + goto end; + } + + ret = visit_field_update_clock_value(cur_field, val); + bt_ctf_object_put_ref(cur_field); + break; + } + default: + break; + } + +end: + return ret; +} + +int visit_event_update_clock_value(struct bt_ctf_event *event, uint64_t *val) +{ + int ret = 0; + struct bt_ctf_field *field; + + field = bt_ctf_event_get_header(event); + ret = visit_field_update_clock_value(field, val); + bt_ctf_object_put_ref(field); + if (ret) { + BT_LOGW_STR("Cannot automatically update clock value in " + "event's header."); + goto end; + } + + field = bt_ctf_event_get_stream_event_context(event); + ret = visit_field_update_clock_value(field, val); + bt_ctf_object_put_ref(field); + if (ret) { + BT_LOGW_STR("Cannot automatically update clock value in " + "event's stream event context."); + goto end; + } + + field = bt_ctf_event_get_context(event); + ret = visit_field_update_clock_value(field, val); + bt_ctf_object_put_ref(field); + if (ret) { + BT_LOGW_STR("Cannot automatically update clock value in " + "event's context."); + goto end; + } + + field = bt_ctf_event_get_payload_field(event); + ret = visit_field_update_clock_value(field, val); + bt_ctf_object_put_ref(field); + if (ret) { + BT_LOGW_STR("Cannot automatically update clock value in " + "event's payload."); + goto end; + } + +end: + return ret; +} + +static +int set_packet_context_timestamps(struct bt_ctf_stream *stream) +{ + int ret = 0; + uint64_t val; + uint64_t cur_clock_value; + uint64_t init_clock_value = 0; + struct bt_ctf_field *ts_begin_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "timestamp_begin"); + struct bt_ctf_field *ts_end_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "timestamp_end"); + struct bt_ctf_field_common *packet_context = + (void *) stream->packet_context; + uint64_t i; + int64_t len; + + if (ts_begin_field && bt_ctf_field_is_set_recursive(ts_begin_field)) { + /* Use provided `timestamp_begin` value as starting value */ + ret = bt_ctf_field_integer_unsigned_get_value(ts_begin_field, &val); + BT_ASSERT(ret == 0); + init_clock_value = val; + } else if (stream->last_ts_end != -1ULL) { + /* Use last packet's ending timestamp as starting value */ + init_clock_value = stream->last_ts_end; + } + + cur_clock_value = init_clock_value; + + if (stream->last_ts_end != -1ULL && + cur_clock_value < stream->last_ts_end) { + BT_LOGW("Packet's initial timestamp is less than previous " + "packet's final timestamp: " + "stream-addr=%p, stream-name=\"%s\", " + "cur-packet-ts-begin=%" PRIu64 ", " + "prev-packet-ts-end=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + cur_clock_value, stream->last_ts_end); + ret = -1; + goto end; + } + + /* + * Visit all the packet context fields, followed by all the + * fields of all the events, in order, updating our current + * clock value as we visit. + * + * While visiting the packet context fields, do not consider + * `timestamp_begin` and `timestamp_end` because this function's + * purpose is to set them anyway. Also do not consider + * `packet_size`, `content_size`, `events_discarded`, and + * `packet_seq_num` if they are not set because those are + * autopopulating fields. + */ + len = bt_ctf_field_type_structure_get_field_count( + (void *) packet_context->type); + BT_ASSERT(len >= 0); + + for (i = 0; i < len; i++) { + const char *member_name; + struct bt_ctf_field *member_field; + + ret = bt_ctf_field_type_structure_get_field_by_index( + (void *) packet_context->type, &member_name, NULL, i); + BT_ASSERT(ret == 0); + + if (strcmp(member_name, "timestamp_begin") == 0 || + strcmp(member_name, "timestamp_end") == 0) { + continue; + } + + member_field = bt_ctf_field_structure_get_field_by_index( + stream->packet_context, i); + BT_ASSERT(member_field); + + if (strcmp(member_name, "packet_size") == 0 && + !bt_ctf_field_is_set_recursive(member_field)) { + bt_ctf_object_put_ref(member_field); + continue; + } + + if (strcmp(member_name, "content_size") == 0 && + !bt_ctf_field_is_set_recursive(member_field)) { + bt_ctf_object_put_ref(member_field); + continue; + } + + if (strcmp(member_name, "events_discarded") == 0 && + !bt_ctf_field_is_set_recursive(member_field)) { + bt_ctf_object_put_ref(member_field); + continue; + } + + if (strcmp(member_name, "packet_seq_num") == 0 && + !bt_ctf_field_is_set_recursive(member_field)) { + bt_ctf_object_put_ref(member_field); + continue; + } + + ret = visit_field_update_clock_value(member_field, + &cur_clock_value); + bt_ctf_object_put_ref(member_field); + if (ret) { + BT_LOGW("Cannot automatically update clock value " + "in stream's packet context: " + "stream-addr=%p, stream-name=\"%s\", " + "field-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream), + member_name); + goto end; + } + } + + for (i = 0; i < stream->events->len; i++) { + struct bt_ctf_event *event = g_ptr_array_index(stream->events, i); + + BT_ASSERT(event); + ret = visit_event_update_clock_value(event, &cur_clock_value); + if (ret) { + BT_LOGW("Cannot automatically update clock value " + "in stream's packet context: " + "stream-addr=%p, stream-name=\"%s\", " + "index=%" PRIu64 ", event-addr=%p, " + "event-class-id=%" PRId64 ", " + "event-class-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream), + i, event, + bt_ctf_event_class_common_get_id(event->common.class), + bt_ctf_event_class_common_get_name(event->common.class)); + goto end; + } + } + + /* + * Everything is visited, thus the current clock value + * corresponds to the ending timestamp. Validate this value + * against the provided value of `timestamp_end`, if any, + * otherwise set it. + */ + if (ts_end_field && bt_ctf_field_is_set_recursive(ts_end_field)) { + ret = bt_ctf_field_integer_unsigned_get_value(ts_end_field, &val); + BT_ASSERT(ret == 0); + + if (val < cur_clock_value) { + BT_LOGW("Packet's final timestamp is less than " + "computed packet's final timestamp: " + "stream-addr=%p, stream-name=\"%s\", " + "cur-packet-ts-end=%" PRIu64 ", " + "computed-packet-ts-end=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), + val, cur_clock_value); + ret = -1; + goto end; + } + + stream->last_ts_end = val; + } + + if (ts_end_field && !bt_ctf_field_is_set_recursive(ts_end_field)) { + ret = set_integer_field_value(ts_end_field, cur_clock_value); + BT_ASSERT(ret == 0); + stream->last_ts_end = cur_clock_value; + } + + if (!ts_end_field) { + stream->last_ts_end = cur_clock_value; + } + + /* Set `timestamp_begin` field to initial clock value */ + if (ts_begin_field && !bt_ctf_field_is_set_recursive(ts_begin_field)) { + ret = set_integer_field_value(ts_begin_field, init_clock_value); + BT_ASSERT(ret == 0); + } + +end: + bt_ctf_object_put_ref(ts_begin_field); + bt_ctf_object_put_ref(ts_end_field); + return ret; +} + +static +int auto_populate_packet_context(struct bt_ctf_stream *stream, bool set_ts, + uint64_t packet_size_bits, uint64_t content_size_bits) +{ + int ret = 0; + + if (!stream->packet_context) { + goto end; + } + + ret = set_packet_context_packet_size(stream, packet_size_bits); + if (ret) { + BT_LOGW("Cannot set packet context's packet size field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + ret = set_packet_context_content_size(stream, content_size_bits); + if (ret) { + BT_LOGW("Cannot set packet context's content size field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + if (set_ts) { + ret = set_packet_context_timestamps(stream); + if (ret) { + BT_LOGW("Cannot set packet context's timestamp fields: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + } + + ret = set_packet_context_events_discarded(stream); + if (ret) { + BT_LOGW("Cannot set packet context's discarded events count field: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + goto end; + } + + BT_LOGV("Automatically populated stream's packet context's known fields: " + "stream-addr=%p, stream-name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + +end: + return ret; +} + +static +void release_event(struct bt_ctf_event *event) +{ + if (bt_ctf_object_get_ref_count(&event->common.base)) { + /* + * The event is being orphaned, but it must guarantee the + * existence of its event class for the duration of its + * lifetime. + */ + bt_ctf_object_get_ref(event->common.class); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event->common.base.parent); + } else { + bt_ctf_object_try_spec_release(&event->common.base); + } +} + +static +int create_stream_file(struct bt_ctf_writer *writer, + struct bt_ctf_stream *stream) +{ + int ret = 0; + GString *filename = g_string_new(NULL); + int64_t stream_class_id; + char *file_path = NULL; + + BT_LOGD("Creating stream file: writer-addr=%p, stream-addr=%p, " + "stream-name=\"%s\", stream-class-addr=%p, stream-class-name=\"%s\"", + writer, stream, bt_ctf_stream_get_name(stream), + stream->common.stream_class, + stream->common.stream_class->name->str); + + if (stream->common.name && stream->common.name->len > 0) { + /* Use stream name's base name as prefix */ + gchar *basename = g_path_get_basename(stream->common.name->str); + + BT_ASSERT(basename); + + if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) { + g_string_assign(filename, "stream"); + } else { + g_string_assign(filename, basename); + } + + g_free(basename); + goto append_ids; + } + + if (stream->common.stream_class->name && + stream->common.stream_class->name->len > 0) { + /* Use stream class name's base name as prefix */ + gchar *basename = + g_path_get_basename( + stream->common.stream_class->name->str); + + BT_ASSERT(basename); + + if (strcmp(basename, G_DIR_SEPARATOR_S) == 0) { + g_string_assign(filename, "stream"); + } else { + g_string_assign(filename, basename); + } + + g_free(basename); + goto append_ids; + } + + /* Default to using `stream-` as prefix */ + g_string_assign(filename, "stream"); + +append_ids: + stream_class_id = bt_ctf_stream_class_common_get_id(stream->common.stream_class); + BT_ASSERT(stream_class_id >= 0); + BT_ASSERT(stream->common.id >= 0); + g_string_append_printf(filename, "-%" PRId64 "-%" PRId64, + stream_class_id, stream->common.id); + + file_path = g_build_filename(writer->path->str, filename->str, NULL); + if (file_path == NULL) { + ret = -1; + goto end; + } + + ret = bt_ctfser_init(&stream->ctfser, file_path); + g_free(file_path); + if (ret) { + /* bt_ctfser_init() logs errors */ + goto end; + } + + BT_LOGD("Created stream file for writing: " + "stream-addr=%p, stream-name=\"%s\", " + "filename=\"%s\"", stream, bt_ctf_stream_get_name(stream), + filename->str); + +end: + g_string_free(filename, TRUE); + return ret; +} + +BT_HIDDEN +struct bt_ctf_stream *bt_ctf_stream_create_with_id( + struct bt_ctf_stream_class *stream_class, + const char *name, uint64_t id) +{ + int ret; + int fd; + struct bt_ctf_stream *stream = NULL; + struct bt_ctf_trace *trace = NULL; + struct bt_ctf_writer *writer = NULL; + + BT_LOGD("Creating CTF writer stream object: stream-class-addr=%p, " + "stream-class-name=\"%s\", stream-name=\"%s\", " + "stream-id=%" PRIu64, + stream_class, bt_ctf_stream_class_get_name(stream_class), + name, id); + stream = g_new0(struct bt_ctf_stream, 1); + if (!stream) { + BT_LOGE_STR("Failed to allocate one stream."); + goto error; + } + + if (id == -1ULL) { + id = stream_class->next_stream_id; + } + + ret = bt_ctf_stream_common_initialize(BT_CTF_TO_COMMON(stream), + BT_CTF_TO_COMMON(stream_class), name, id, bt_ctf_stream_destroy); + if (ret) { + /* bt_ctf_stream_common_initialize() logs errors */ + goto error; + } + + trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( + BT_CTF_TO_COMMON(stream_class))); + if (!trace) { + BT_LOGW("Invalid parameter: cannot create stream from a stream class which is not part of trace: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-name=\"%s\"", + stream_class, bt_ctf_stream_class_get_name(stream_class), + name); + goto error; + } + + writer = (struct bt_ctf_writer *) + bt_ctf_object_get_parent(&trace->common.base); + stream->last_ts_end = -1ULL; + BT_LOGD("CTF writer stream object belongs writer's trace: " + "writer-addr=%p", writer); + BT_ASSERT(writer); + + if (stream_class->common.packet_context_field_type) { + BT_LOGD("Creating stream's packet context field: " + "ft-addr=%p", + stream_class->common.packet_context_field_type); + stream->packet_context = bt_ctf_field_create( + (void *) stream_class->common.packet_context_field_type); + if (!stream->packet_context) { + BT_LOGW_STR("Cannot create stream's packet context field."); + goto error; + } + + /* Initialize events_discarded */ + ret = try_set_structure_field_integer( + stream->packet_context, "events_discarded", 0); + if (ret < 0) { + BT_LOGW("Cannot set `events_discarded` field in packet context: " + "ret=%d, packet-context-field-addr=%p", + ret, stream->packet_context); + goto error; + } + } + + stream->events = g_ptr_array_new_with_free_func( + (GDestroyNotify) release_event); + if (!stream->events) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + if (trace->common.packet_header_field_type) { + BT_LOGD("Creating stream's packet header field: " + "ft-addr=%p", trace->common.packet_header_field_type); + stream->packet_header = + bt_ctf_field_create( + (void *) trace->common.packet_header_field_type); + if (!stream->packet_header) { + BT_LOGW_STR("Cannot create stream's packet header field."); + goto error; + } + } + + /* + * Attempt to populate the default trace packet header fields + * (magic, uuid and stream_id). This will _not_ fail shall the + * fields not be found or be of an incompatible type; they will + * simply not be populated automatically. The user will have to + * make sure to set the trace packet header fields himself + * before flushing. + */ + ret = auto_populate_packet_header(stream); + if (ret) { + BT_LOGW_STR("Cannot automatically populate the stream's packet header."); + goto error; + } + + /* Create file associated with this stream */ + fd = create_stream_file(writer, stream); + if (fd < 0) { + BT_LOGW_STR("Cannot create stream file."); + goto error; + } + + /* Freeze the writer */ + BT_LOGD_STR("Freezing stream's CTF writer."); + bt_ctf_writer_freeze(writer); + + /* Add this stream to the trace's streams */ + g_ptr_array_add(trace->common.streams, stream); + stream_class->next_stream_id++; + BT_LOGD("Created stream object: addr=%p", stream); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(stream); + +end: + bt_ctf_object_put_ref(writer); + return stream; +} + +struct bt_ctf_stream *bt_ctf_stream_create( + struct bt_ctf_stream_class *stream_class, + const char *name, uint64_t id_param) +{ + return bt_ctf_stream_create_with_id(stream_class, + name, id_param); +} + +int bt_ctf_stream_get_discarded_events_count( + struct bt_ctf_stream *stream, uint64_t *count) +{ + int ret = 0; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + ret = -1; + goto end; + } + + if (!count) { + BT_LOGW_STR("Invalid parameter: count is NULL."); + ret = -1; + goto end; + } + + *count = (uint64_t) stream->discarded_events; + +end: + return ret; +} + +static +int set_packet_context_events_discarded_field(struct bt_ctf_stream *stream, + uint64_t count) +{ + int ret = 0; + struct bt_ctf_field *events_discarded_field = NULL; + + if (!stream->packet_context) { + goto end; + } + + events_discarded_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "events_discarded"); + if (!events_discarded_field) { + goto end; + } + + ret = bt_ctf_field_integer_unsigned_set_value( + events_discarded_field, count); + if (ret) { + BT_LOGW("Cannot set packet context's `events_discarded` field: " + "field-addr=%p, value=%" PRIu64, + events_discarded_field, count); + goto end; + } + +end: + bt_ctf_object_put_ref(events_discarded_field); + return ret; +} + +void bt_ctf_stream_append_discarded_events(struct bt_ctf_stream *stream, + uint64_t event_count) +{ + int ret; + uint64_t new_count; + struct bt_ctf_field *events_discarded_field = NULL; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + goto end; + } + + BT_LOGV("Appending discarded events to stream: " + "stream-addr=%p, stream-name=\"%s\", append-count=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), event_count); + + if (!stream->packet_context) { + BT_LOGW_STR("Invalid parameter: stream has no packet context field."); + goto end; + } + + events_discarded_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "events_discarded"); + if (!events_discarded_field) { + BT_LOGW_STR("No field named `events_discarded` in stream's packet context."); + goto end; + } + + new_count = stream->discarded_events + event_count; + if (new_count < stream->discarded_events) { + BT_LOGW("New discarded events count is less than the stream's current discarded events count: " + "cur-count=%" PRIu64 ", new-count=%" PRIu64, + stream->discarded_events, new_count); + goto end; + } + + ret = set_packet_context_events_discarded_field(stream, new_count); + if (ret) { + /* set_packet_context_events_discarded_field() logs errors */ + goto end; + } + + stream->discarded_events = new_count; + BT_LOGV("Appended discarded events to stream: " + "stream-addr=%p, stream-name=\"%s\", append-count=%" PRIu64, + stream, bt_ctf_stream_get_name(stream), event_count); + +end: + bt_ctf_object_put_ref(events_discarded_field); +} + +static int auto_populate_event_header(struct bt_ctf_stream *stream, + struct bt_ctf_event *event) +{ + int ret = 0; + struct bt_ctf_field *id_field = NULL, *timestamp_field = NULL; + struct bt_ctf_clock_class *mapped_clock_class = NULL; + struct bt_ctf_stream_class *stream_class = + BT_CTF_FROM_COMMON(bt_ctf_stream_common_borrow_class( + BT_CTF_TO_COMMON(stream))); + int64_t event_class_id; + + BT_ASSERT(event); + + if (!event->common.header_field) { + goto end; + } + + if (event->common.frozen) { + BT_LOGW_STR("Cannot populate event header field: event is frozen."); + ret = -1; + goto end; + } + + BT_LOGV("Automatically populating event's header field: " + "stream-addr=%p, stream-name=\"%s\", event-addr=%p", + stream, bt_ctf_stream_get_name(stream), event); + + id_field = bt_ctf_field_structure_get_field_by_name( + (void *) event->common.header_field->field, "id"); + event_class_id = bt_ctf_event_class_common_get_id(event->common.class); + BT_ASSERT(event_class_id >= 0); + + if (id_field && bt_ctf_field_get_type_id(id_field) == BT_CTF_FIELD_TYPE_ID_INTEGER) { + ret = set_integer_field_value(id_field, event_class_id); + if (ret) { + BT_LOGW("Cannot set event header's `id` field's value: " + "addr=%p, value=%" PRIu64, id_field, + event_class_id); + goto end; + } + } + + /* + * The conditions to automatically set the timestamp are: + * + * 1. The event header field "timestamp" exists and is an + * integer field. + * 2. This stream's class has a registered clock (set with + * bt_ctf_stream_class_set_clock()). + * 3. The "timestamp" field is not set. + */ + timestamp_field = bt_ctf_field_structure_get_field_by_name( + (void *) event->common.header_field->field, "timestamp"); + if (timestamp_field && stream_class->clock && + bt_ctf_field_get_type_id(id_field) == BT_CTF_FIELD_TYPE_ID_INTEGER && + !bt_ctf_field_is_set_recursive(timestamp_field)) { + mapped_clock_class = + bt_ctf_field_type_integer_get_mapped_clock_class( + (void *) ((struct bt_ctf_field_common *) timestamp_field)->type); + if (mapped_clock_class) { + uint64_t timestamp; + + BT_ASSERT(mapped_clock_class == + stream_class->clock->clock_class); + ret = bt_ctf_clock_get_value( + stream_class->clock, + ×tamp); + BT_ASSERT(ret == 0); + ret = set_integer_field_value(timestamp_field, + timestamp); + if (ret) { + BT_LOGW("Cannot set event header's `timestamp` field's value: " + "addr=%p, value=%" PRIu64, + timestamp_field, timestamp); + goto end; + } + } + } + + BT_LOGV("Automatically populated event's header field: " + "stream-addr=%p, stream-name=\"%s\", event-addr=%p", + stream, bt_ctf_stream_get_name(stream), event); + +end: + bt_ctf_object_put_ref(id_field); + bt_ctf_object_put_ref(timestamp_field); + bt_ctf_object_put_ref(mapped_clock_class); + return ret; +} + +int bt_ctf_stream_append_event(struct bt_ctf_stream *stream, + struct bt_ctf_event *event) +{ + int ret = 0; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + ret = -1; + goto end; + } + + if (!event) { + BT_LOGW_STR("Invalid parameter: event is NULL."); + ret = -1; + goto end; + } + + BT_LOGV("Appending event to stream: " + "stream-addr=%p, stream-name=\"%s\", event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + stream, bt_ctf_stream_get_name(stream), event, + bt_ctf_event_class_common_get_name( + bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))), + bt_ctf_event_class_common_get_id( + bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event)))); + + /* + * The event is not supposed to have a parent stream at this + * point. The only other way an event can have a parent stream + * is if it was assigned when setting a packet to the event, + * in which case the packet's stream is not a writer stream, + * and thus the user is trying to append an event which belongs + * to another stream. + */ + if (event->common.base.parent) { + ret = -1; + goto end; + } + + bt_ctf_object_set_parent(&event->common.base, &stream->common.base); + BT_LOGV_STR("Automatically populating the header of the event to append."); + ret = auto_populate_event_header(stream, event); + if (ret) { + /* auto_populate_event_header() reports errors */ + goto error; + } + + /* Make sure the various scopes of the event are set */ + BT_LOGV_STR("Validating event to append."); + BT_CTF_ASSERT_PRE(bt_ctf_event_common_validate(BT_CTF_TO_COMMON(event)) == 0, + "Invalid event: event-addr=%p", event); + + /* Save the new event and freeze it */ + BT_LOGV_STR("Freezing the event to append."); + bt_ctf_event_common_set_is_frozen(BT_CTF_TO_COMMON(event), true); + g_ptr_array_add(stream->events, event); + + /* + * Event had to hold a reference to its event class as long as it wasn't + * part of the same trace hierarchy. From now on, the event and its + * class share the same lifetime guarantees and the reference is no + * longer needed. + */ + BT_LOGV_STR("Putting the event's class."); + bt_ctf_object_put_ref(event->common.class); + BT_LOGV("Appended event to stream: " + "stream-addr=%p, stream-name=\"%s\", event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64, + stream, bt_ctf_stream_get_name(stream), event, + bt_ctf_event_class_common_get_name( + bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event))), + bt_ctf_event_class_common_get_id( + bt_ctf_event_common_borrow_class(BT_CTF_TO_COMMON(event)))); + +end: + return ret; + +error: + /* + * Orphan the event; we were not successful in associating it to + * a stream. + */ + bt_ctf_object_set_parent(&event->common.base, NULL); + return ret; +} + +struct bt_ctf_field *bt_ctf_stream_get_packet_context(struct bt_ctf_stream *stream) +{ + struct bt_ctf_field *packet_context = NULL; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + goto end; + } + + packet_context = stream->packet_context; + if (packet_context) { + bt_ctf_object_get_ref(packet_context); + } +end: + return packet_context; +} + +int bt_ctf_stream_set_packet_context(struct bt_ctf_stream *stream, + struct bt_ctf_field *field) +{ + int ret = 0; + struct bt_ctf_field_type *field_type; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + ret = -1; + goto end; + } + + field_type = bt_ctf_field_get_type(field); + if (bt_ctf_field_type_common_compare((void *) field_type, + stream->common.stream_class->packet_context_field_type)) { + BT_LOGW("Invalid parameter: packet context's field type is different from the stream's packet context field type: " + "stream-addr=%p, stream-name=\"%s\", " + "packet-context-field-addr=%p, " + "packet-context-ft-addr=%p", + stream, bt_ctf_stream_get_name(stream), + field, field_type); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(field_type); + bt_ctf_object_put_ref(stream->packet_context); + stream->packet_context = bt_ctf_object_get_ref(field); + BT_LOGV("Set stream's packet context field: " + "stream-addr=%p, stream-name=\"%s\", " + "packet-context-field-addr=%p", + stream, bt_ctf_stream_get_name(stream), field); +end: + return ret; +} + +struct bt_ctf_field *bt_ctf_stream_get_packet_header(struct bt_ctf_stream *stream) +{ + struct bt_ctf_field *packet_header = NULL; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + goto end; + } + + packet_header = stream->packet_header; + if (packet_header) { + bt_ctf_object_get_ref(packet_header); + } +end: + return packet_header; +} + +int bt_ctf_stream_set_packet_header(struct bt_ctf_stream *stream, + struct bt_ctf_field *field) +{ + int ret = 0; + struct bt_ctf_trace *trace = NULL; + struct bt_ctf_field_type *field_type = NULL; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + ret = -1; + goto end; + } + + trace = (struct bt_ctf_trace *) + bt_ctf_object_get_parent(&stream->common.base); + + if (!field) { + if (trace->common.packet_header_field_type) { + BT_LOGW("Invalid parameter: setting no packet header but packet header field type is not NULL: " + "stream-addr=%p, stream-name=\"%s\", " + "packet-header-field-addr=%p, " + "expected-ft-addr=%p", + stream, bt_ctf_stream_get_name(stream), + field, trace->common.packet_header_field_type); + ret = -1; + goto end; + } + + goto skip_validation; + } + + field_type = bt_ctf_field_get_type(field); + BT_ASSERT(field_type); + + if (bt_ctf_field_type_common_compare((void *) field_type, + trace->common.packet_header_field_type)) { + BT_LOGW("Invalid parameter: packet header's field type is different from the stream's packet header field type: " + "stream-addr=%p, stream-name=\"%s\", " + "packet-header-field-addr=%p, " + "packet-header-ft-addr=%p", + stream, bt_ctf_stream_get_name(stream), + field, field_type); + ret = -1; + goto end; + } + +skip_validation: + bt_ctf_object_put_ref(stream->packet_header); + stream->packet_header = bt_ctf_object_get_ref(field); + BT_LOGV("Set stream's packet header field: " + "stream-addr=%p, stream-name=\"%s\", " + "packet-header-field-addr=%p", + stream, bt_ctf_stream_get_name(stream), field); +end: + BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); + bt_ctf_object_put_ref(field_type); + return ret; +} + +static +void reset_structure_field(struct bt_ctf_field *structure, const char *name) +{ + struct bt_ctf_field *member; + + member = bt_ctf_field_structure_get_field_by_name(structure, name); + if (member) { + bt_ctf_field_common_reset_recursive((void *) member); + bt_ctf_object_put_ref(member); + } +} + +int bt_ctf_stream_flush(struct bt_ctf_stream *stream) +{ + int ret = 0; + size_t i; + uint64_t packet_context_offset_bits = 0; + struct bt_ctf_trace *trace; + enum bt_ctf_byte_order native_byte_order; + bool has_packet_size = false; + uint64_t packet_size_bits = 0; + uint64_t content_size_bits = 0; + + if (!stream) { + BT_LOGW_STR("Invalid parameter: stream is NULL."); + ret = -1; + goto end_no_stream; + } + + if (stream->packet_context) { + struct bt_ctf_field *packet_size_field; + + packet_size_field = bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "packet_size"); + has_packet_size = (packet_size_field != NULL); + bt_ctf_object_put_ref(packet_size_field); + } + + if (stream->flushed_packet_count == 1) { + if (!stream->packet_context) { + BT_LOGW_STR("Cannot flush a stream which has no packet context field more than once."); + ret = -1; + goto end; + } + + if (!has_packet_size) { + BT_LOGW_STR("Cannot flush a stream which has no packet context's `packet_size` field more than once."); + ret = -1; + goto end; + } + } + + BT_LOGV("Flushing stream's current packet: stream-addr=%p, " + "stream-name=\"%s\", packet-index=%u", stream, + bt_ctf_stream_get_name(stream), stream->flushed_packet_count); + trace = BT_CTF_FROM_COMMON(bt_ctf_stream_class_common_borrow_trace( + stream->common.stream_class)); + BT_ASSERT(trace); + native_byte_order = bt_ctf_trace_get_native_byte_order(trace); + + ret = auto_populate_packet_header(stream); + if (ret) { + BT_LOGW_STR("Cannot automatically populate the stream's packet header field."); + ret = -1; + goto end; + } + + /* Initialize packet/content sizes to `0`; we will overwrite later */ + ret = auto_populate_packet_context(stream, true, 0, 0); + if (ret) { + BT_LOGW_STR("Cannot automatically populate the stream's packet context field."); + ret = -1; + goto end; + } + + ret = bt_ctfser_open_packet(&stream->ctfser); + if (ret) { + /* bt_ctfser_open_packet() logs errors */ + ret = -1; + goto end; + } + + if (stream->packet_header) { + BT_LOGV_STR("Serializing packet header field (initial)."); + ret = bt_ctf_field_serialize_recursive(stream->packet_header, + &stream->ctfser, native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize stream's packet header field: " + "field-addr=%p", stream->packet_header); + goto end; + } + } + + if (stream->packet_context) { + /* Save packet context's position to overwrite it later */ + packet_context_offset_bits = + bt_ctfser_get_offset_in_current_packet_bits( + &stream->ctfser); + + /* Write packet context */ + BT_LOGV_STR("Serializing packet context field (initial)."); + ret = bt_ctf_field_serialize_recursive(stream->packet_context, + &stream->ctfser, native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize stream's packet context field: " + "field-addr=%p", stream->packet_context); + goto end; + } + } + + BT_LOGV("Serializing events: count=%u", stream->events->len); + + for (i = 0; i < stream->events->len; i++) { + struct bt_ctf_event *event = g_ptr_array_index( + stream->events, i); + struct bt_ctf_event_class *event_class = + BT_CTF_FROM_COMMON(bt_ctf_event_common_borrow_class( + BT_CTF_TO_COMMON(event))); + + BT_LOGV("Serializing event: index=%zu, event-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "ser-offset=%" PRIu64, + i, event, bt_ctf_event_class_get_name(event_class), + bt_ctf_event_class_get_id(event_class), + bt_ctfser_get_offset_in_current_packet_bits( + &stream->ctfser)); + + /* Write event header */ + if (event->common.header_field) { + BT_LOGV_STR("Serializing event's header field."); + ret = bt_ctf_field_serialize_recursive( + (void *) event->common.header_field->field, + &stream->ctfser, native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize event's header field: " + "field-addr=%p", + event->common.header_field->field); + goto end; + } + } + + /* Write stream event context */ + if (event->common.stream_event_context_field) { + BT_LOGV_STR("Serializing event's stream event context field."); + ret = bt_ctf_field_serialize_recursive( + (void *) event->common.stream_event_context_field, + &stream->ctfser, native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize event's stream event context field: " + "field-addr=%p", + event->common.stream_event_context_field); + goto end; + } + } + + /* Write event content */ + ret = bt_ctf_event_serialize(event, &stream->ctfser, + native_byte_order); + if (ret) { + /* bt_ctf_event_serialize() logs errors */ + goto end; + } + } + + content_size_bits = bt_ctfser_get_offset_in_current_packet_bits( + &stream->ctfser); + + if (!has_packet_size && content_size_bits % 8 != 0) { + BT_LOGW("Stream's packet context field type has no `packet_size` field, " + "but current content size is not a multiple of 8 bits: " + "content-size=%" PRIu64 ", " + "packet-size=%" PRIu64, + content_size_bits, + packet_size_bits); + ret = -1; + goto end; + } + + /* Set packet size; make it a multiple of 8 */ + packet_size_bits = (content_size_bits + 7) & ~UINT64_C(7); + + if (stream->packet_context) { + /* + * The whole packet is serialized at this point. Make + * sure that, if `packet_size` is missing, the current + * content size is equal to the current packet size. + */ + struct bt_ctf_field *field = + bt_ctf_field_structure_get_field_by_name( + stream->packet_context, "content_size"); + + bt_ctf_object_put_ref(field); + if (!field) { + if (content_size_bits != packet_size_bits) { + BT_LOGW("Stream's packet context's `content_size` field is missing, " + "but current packet's content size is not equal to its packet size: " + "content-size=%" PRIu64 ", " + "packet-size=%" PRIu64, + bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser), + packet_size_bits); + ret = -1; + goto end; + } + } + + /* + * Overwrite the packet context now that the stream + * position's packet and content sizes have the correct + * values. + */ + bt_ctfser_set_offset_in_current_packet_bits(&stream->ctfser, + packet_context_offset_bits); + ret = auto_populate_packet_context(stream, false, + packet_size_bits, content_size_bits); + if (ret) { + BT_LOGW_STR("Cannot automatically populate the stream's packet context field."); + ret = -1; + goto end; + } + + BT_LOGV("Rewriting (serializing) packet context field."); + ret = bt_ctf_field_serialize_recursive(stream->packet_context, + &stream->ctfser, native_byte_order); + if (ret) { + BT_LOGW("Cannot serialize stream's packet context field: " + "field-addr=%p", stream->packet_context); + goto end; + } + } + + g_ptr_array_set_size(stream->events, 0); + stream->flushed_packet_count++; + bt_ctfser_close_current_packet(&stream->ctfser, packet_size_bits / 8); + +end: + /* Reset automatically-set fields. */ + if (stream->packet_context) { + reset_structure_field(stream->packet_context, "timestamp_begin"); + reset_structure_field(stream->packet_context, "timestamp_end"); + reset_structure_field(stream->packet_context, "packet_size"); + reset_structure_field(stream->packet_context, "content_size"); + reset_structure_field(stream->packet_context, "events_discarded"); + } + + if (ret == 0) { + BT_LOGV("Flushed stream's current packet: " + "content-size=%" PRIu64 ", packet-size=%" PRIu64, + content_size_bits, packet_size_bits); + } + +end_no_stream: + return ret; +} + +static +void bt_ctf_stream_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_stream *stream = (void *) obj; + + BT_LOGD("Destroying CTF writer stream object: addr=%p, name=\"%s\"", + stream, bt_ctf_stream_get_name(stream)); + + bt_ctf_stream_common_finalize(BT_CTF_TO_COMMON(stream)); + bt_ctfser_fini(&stream->ctfser); + + if (stream->events) { + BT_LOGD_STR("Putting events."); + g_ptr_array_free(stream->events, TRUE); + } + + BT_LOGD_STR("Putting packet header field."); + bt_ctf_object_put_ref(stream->packet_header); + BT_LOGD_STR("Putting packet context field."); + bt_ctf_object_put_ref(stream->packet_context); + g_free(stream); +} + +static +int _set_structure_field_integer(struct bt_ctf_field *structure, char *name, + uint64_t value, bt_bool force) +{ + int ret = 0; + struct bt_ctf_field_type *field_type = NULL; + struct bt_ctf_field *integer; + + BT_ASSERT(structure); + BT_ASSERT(name); + + integer = bt_ctf_field_structure_get_field_by_name(structure, name); + if (!integer) { + /* Field not found, not an error. */ + BT_LOGV("Field not found: struct-field-addr=%p, " + "name=\"%s\", force=%d", structure, name, force); + goto end; + } + + /* Make sure the payload has not already been set. */ + if (!force && bt_ctf_field_is_set_recursive(integer)) { + /* Payload already set, not an error */ + BT_LOGV("Field's payload is already set: struct-field-addr=%p, " + "name=\"%s\", force=%d", structure, name, force); + goto end; + } + + field_type = bt_ctf_field_get_type(integer); + BT_ASSERT(field_type); + if (bt_ctf_field_type_get_type_id(field_type) != BT_CTF_FIELD_TYPE_ID_INTEGER) { + /* + * The user most likely meant for us to populate this field + * automatically. However, we can only do this if the field + * is an integer. Return an error. + */ + BT_LOGW("Invalid parameter: field's type is not an integer field type: " + "field-addr=%p, ft-addr=%p, ft-id=%s", + integer, field_type, + bt_ctf_field_type_id_string((int) + bt_ctf_field_type_get_type_id(field_type))); + ret = -1; + goto end; + } + + if (bt_ctf_field_type_integer_is_signed(field_type)) { + ret = bt_ctf_field_integer_signed_set_value(integer, + (int64_t) value); + } else { + ret = bt_ctf_field_integer_unsigned_set_value(integer, value); + } + ret = !ret ? 1 : ret; +end: + bt_ctf_object_put_ref(integer); + bt_ctf_object_put_ref(field_type); + return ret; +} + +/* + * Returns the following codes: + * 1 if the field was found and set, + * 0 if nothing was done (field not found, or was already set), + * <0 if an error was encoutered + */ +static +int try_set_structure_field_integer(struct bt_ctf_field *structure, char *name, + uint64_t value) +{ + return _set_structure_field_integer(structure, name, value, BT_FALSE); +} + +struct bt_ctf_stream_class *bt_ctf_stream_get_class( + struct bt_ctf_stream *stream) +{ + return bt_ctf_object_get_ref(bt_ctf_stream_common_borrow_class(BT_CTF_TO_COMMON(stream))); +} + +const char *bt_ctf_stream_get_name(struct bt_ctf_stream *stream) +{ + return bt_ctf_stream_common_get_name(BT_CTF_TO_COMMON(stream)); +} + +int64_t bt_ctf_stream_get_id(struct bt_ctf_stream *stream) +{ + return bt_ctf_stream_common_get_id(BT_CTF_TO_COMMON(stream)); +} diff --git a/src/ctf-writer/stream.h b/src/ctf-writer/stream.h new file mode 100644 index 00000000..8c3d7d7c --- /dev/null +++ b/src/ctf-writer/stream.h @@ -0,0 +1,108 @@ +#ifndef BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H +#define BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "common/assert.h" +#include "common/babeltrace.h" +#include +#include "ctfser/ctfser.h" +#include + +#include "assert-pre.h" +#include "object.h" +#include "stream.h" +#include "utils.h" + +struct bt_ctf_stream_common; + +struct bt_ctf_stream_common { + struct bt_ctf_object base; + int64_t id; + struct bt_ctf_stream_class_common *stream_class; + GString *name; +}; + +BT_HIDDEN +int bt_ctf_stream_common_initialize( + struct bt_ctf_stream_common *stream, + struct bt_ctf_stream_class_common *stream_class, const char *name, + uint64_t id, bt_ctf_object_release_func release_func); + +BT_HIDDEN +void bt_ctf_stream_common_finalize(struct bt_ctf_stream_common *stream); + +static inline +struct bt_ctf_stream_class_common *bt_ctf_stream_common_borrow_class( + struct bt_ctf_stream_common *stream) +{ + BT_ASSERT(stream); + return stream->stream_class; +} + +static inline +const char *bt_ctf_stream_common_get_name(struct bt_ctf_stream_common *stream) +{ + BT_CTF_ASSERT_PRE_NON_NULL(stream, "Stream"); + return stream->name ? stream->name->str : NULL; +} + +static inline +int64_t bt_ctf_stream_common_get_id(struct bt_ctf_stream_common *stream) +{ + int64_t ret; + + BT_CTF_ASSERT_PRE_NON_NULL(stream, "Stream"); + ret = stream->id; + if (ret < 0) { + BT_LOGV("Stream's ID is not set: addr=%p, name=\"%s\"", + stream, bt_ctf_stream_common_get_name(stream)); + } + + return ret; +} + +struct bt_ctf_stream { + struct bt_ctf_stream_common common; + struct bt_ctf_field *packet_header; + struct bt_ctf_field *packet_context; + + /* Array of pointers to bt_ctf_event for the current packet */ + GPtrArray *events; + struct bt_ctfser ctfser; + unsigned int flushed_packet_count; + uint64_t discarded_events; + uint64_t last_ts_end; +}; + +BT_HIDDEN +struct bt_ctf_stream *bt_ctf_stream_create_with_id( + struct bt_ctf_stream_class *stream_class, + const char *name, uint64_t id); + +#endif /* BABELTRACE_CTF_WRITER_STREAM_INTERNAL_H */ diff --git a/src/ctf-writer/trace.c b/src/ctf-writer/trace.c new file mode 100644 index 00000000..f2ce7141 --- /dev/null +++ b/src/ctf-writer/trace.c @@ -0,0 +1,1886 @@ +/* + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-TRACE" +#include "logging.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" + +#include "attributes.h" +#include "clock-class.h" +#include "clock.h" +#include "event-class.h" +#include "event.h" +#include "field-types.h" +#include "field-wrapper.h" +#include "functor.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" +#include "utils.h" +#include "validation.h" +#include "values.h" +#include "visitor.h" +#include "writer.h" + +#define DEFAULT_IDENTIFIER_SIZE 128 +#define DEFAULT_METADATA_STRING_SIZE 4096 + +BT_HIDDEN +int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace, + bt_ctf_object_release_func release_func) +{ + int ret = 0; + + BT_LOGD_STR("Initializing common trace object."); + trace->native_byte_order = BT_CTF_BYTE_ORDER_UNSPECIFIED; + bt_ctf_object_init_shared_with_parent(&trace->base, release_func); + trace->clock_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_ctf_object_put_ref); + if (!trace->clock_classes) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + trace->streams = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_ctf_object_try_spec_release); + if (!trace->streams) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + trace->stream_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_ctf_object_try_spec_release); + if (!trace->stream_classes) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + /* Create the environment array object */ + trace->environment = bt_ctf_attributes_create(); + if (!trace->environment) { + BT_LOGE_STR("Cannot create empty attributes object."); + goto error; + } + + BT_LOGD("Initialized common trace object: addr=%p", trace); + goto end; + +error: + ret = -1; + +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace) +{ + BT_LOGD("Finalizing common trace object: addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + + if (trace->environment) { + BT_LOGD_STR("Destroying environment attributes."); + bt_ctf_attributes_destroy(trace->environment); + } + + if (trace->name) { + g_string_free(trace->name, TRUE); + } + + if (trace->clock_classes) { + BT_LOGD_STR("Putting clock classes."); + g_ptr_array_free(trace->clock_classes, TRUE); + } + + if (trace->streams) { + BT_LOGD_STR("Destroying streams."); + g_ptr_array_free(trace->streams, TRUE); + } + + if (trace->stream_classes) { + BT_LOGD_STR("Destroying stream classes."); + g_ptr_array_free(trace->stream_classes, TRUE); + } + + BT_LOGD_STR("Putting packet header field type."); + bt_ctf_object_put_ref(trace->packet_header_field_type); +} + +BT_HIDDEN +int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!name) { + BT_LOGW_STR("Invalid parameter: name is NULL."); + ret = -1; + goto end; + } + + if (trace->frozen) { + BT_LOGW("Invalid parameter: trace is frozen: " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + ret = -1; + goto end; + } + + trace->name = trace->name ? g_string_assign(trace->name, name) : + g_string_new(name); + if (!trace->name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + BT_LOGV("Set trace's name: addr=%p, name=\"%s\"", trace, name); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace, + const unsigned char *uuid) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!uuid) { + BT_LOGW_STR("Invalid parameter: UUID is NULL."); + ret = -1; + goto end; + } + + if (trace->frozen) { + BT_LOGW("Invalid parameter: trace is frozen: " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + ret = -1; + goto end; + } + + memcpy(trace->uuid, uuid, BABELTRACE_UUID_LEN); + trace->uuid_set = BT_TRUE; + BT_LOGV("Set trace's UUID: addr=%p, name=\"%s\", " + "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + trace, bt_ctf_trace_common_get_name(trace), + (unsigned int) uuid[0], + (unsigned int) uuid[1], + (unsigned int) uuid[2], + (unsigned int) uuid[3], + (unsigned int) uuid[4], + (unsigned int) uuid[5], + (unsigned int) uuid[6], + (unsigned int) uuid[7], + (unsigned int) uuid[8], + (unsigned int) uuid[9], + (unsigned int) uuid[10], + (unsigned int) uuid[11], + (unsigned int) uuid[12], + (unsigned int) uuid[13], + (unsigned int) uuid[14], + (unsigned int) uuid[15]); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace, + const char *name, struct bt_ctf_private_value *value) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!name) { + BT_LOGW_STR("Invalid parameter: name is NULL."); + ret = -1; + goto end; + } + + if (!value) { + BT_LOGW_STR("Invalid parameter: value is NULL."); + ret = -1; + goto end; + } + + if (!bt_ctf_identifier_is_valid(name)) { + BT_LOGW("Invalid parameter: environment field's name is not a valid CTF identifier: " + "trace-addr=%p, trace-name=\"%s\", " + "env-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), name); + ret = -1; + goto end; + } + + if (!bt_ctf_value_is_integer(bt_ctf_private_value_as_value(value)) && + !bt_ctf_value_is_string(bt_ctf_private_value_as_value(value))) { + BT_LOGW("Invalid parameter: environment field's value is not an integer or string value: " + "trace-addr=%p, trace-name=\"%s\", " + "env-name=\"%s\", env-value-type=%s", + trace, bt_ctf_trace_common_get_name(trace), name, + bt_ctf_value_type_string( + bt_ctf_value_get_type( + bt_ctf_private_value_as_value(value)))); + ret = -1; + goto end; + } + + if (trace->frozen) { + /* + * New environment fields may be added to a frozen trace, + * but existing fields may not be changed. + * + * The object passed is frozen like all other attributes. + */ + struct bt_ctf_private_value *attribute = + bt_ctf_attributes_borrow_field_value_by_name( + trace->environment, name); + + if (attribute) { + BT_LOGW("Invalid parameter: trace is frozen and environment field already exists with this name: " + "trace-addr=%p, trace-name=\"%s\", " + "env-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), name); + ret = -1; + goto end; + } + + bt_ctf_value_freeze(bt_ctf_private_value_as_value(value)); + } + + ret = bt_ctf_attributes_set_field_value(trace->environment, name, + value); + if (ret) { + BT_LOGE("Cannot set environment field's value: " + "trace-addr=%p, trace-name=\"%s\", " + "env-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), name); + } else { + BT_LOGV("Set environment field's value: " + "trace-addr=%p, trace-name=\"%s\", " + "env-name=\"%s\", value-addr=%p", + trace, bt_ctf_trace_common_get_name(trace), name, value); + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace, + const char *name, const char *value) +{ + int ret = 0; + struct bt_ctf_private_value *env_value_string_obj = NULL; + + if (!value) { + BT_LOGW_STR("Invalid parameter: value is NULL."); + ret = -1; + goto end; + } + + env_value_string_obj = bt_ctf_private_value_string_create_init(value); + if (!env_value_string_obj) { + BT_LOGE_STR("Cannot create string value object."); + ret = -1; + goto end; + } + + /* bt_ctf_trace_common_set_environment_field() logs errors */ + ret = bt_ctf_trace_common_set_environment_field(trace, name, + env_value_string_obj); + +end: + bt_ctf_object_put_ref(env_value_string_obj); + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field_integer( + struct bt_ctf_trace_common *trace, const char *name, int64_t value) +{ + int ret = 0; + struct bt_ctf_private_value *env_value_integer_obj = NULL; + + env_value_integer_obj = bt_ctf_private_value_integer_create_init(value); + if (!env_value_integer_obj) { + BT_LOGE_STR("Cannot create integer value object."); + ret = -1; + goto end; + } + + /* bt_ctf_trace_common_set_environment_field() logs errors */ + ret = bt_ctf_trace_common_set_environment_field(trace, name, + env_value_integer_obj); + +end: + bt_ctf_object_put_ref(env_value_integer_obj); + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_clock_class *clock_class) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!bt_ctf_clock_class_is_valid(clock_class)) { + BT_LOGW("Invalid parameter: clock class is invalid: " + "trace-addr=%p, trace-name=\"%s\", " + "clock-class-addr=%p, clock-class-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + /* Check for duplicate clock classes */ + if (bt_ctf_trace_common_has_clock_class(trace, clock_class)) { + BT_LOGW("Invalid parameter: clock class already exists in trace: " + "trace-addr=%p, trace-name=\"%s\", " + "clock-class-addr=%p, clock-class-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), + clock_class, bt_ctf_clock_class_get_name(clock_class)); + ret = -1; + goto end; + } + + bt_ctf_object_get_ref(clock_class); + g_ptr_array_add(trace->clock_classes, clock_class); + + if (trace->frozen) { + BT_LOGV_STR("Freezing added clock class because trace is frozen."); + bt_ctf_clock_class_freeze(clock_class); + } + + BT_LOGV("Added clock class to trace: " + "trace-addr=%p, trace-name=\"%s\", " + "clock-class-addr=%p, clock-class-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), + clock_class, bt_ctf_clock_class_get_name(clock_class)); + +end: + return ret; +} + +static +bool packet_header_field_type_is_valid(struct bt_ctf_trace_common *trace, + struct bt_ctf_field_type_common *packet_header_type) +{ + int ret; + bool is_valid = true; + struct bt_ctf_field_type_common *field_type = NULL; + + if (!packet_header_type) { + /* + * No packet header field type: trace must have only + * one stream. At this point the stream class being + * added is not part of the trace yet, so we validate + * that the trace contains no stream classes yet. + */ + if (trace->stream_classes->len >= 1) { + BT_LOGW_STR("Invalid packet header field type: " + "packet header field type does not exist but there's more than one stream class in the trace."); + goto invalid; + } + + /* No packet header field type: valid at this point */ + goto end; + } + + /* Packet header field type, if it exists, must be a structure */ + if (packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid packet header field type: must be a structure field type if it exists: " + "ft-addr=%p, ft-id=%s", + packet_header_type, + bt_ctf_field_type_id_string(packet_header_type->id)); + goto invalid; + } + + /* + * If there's a `magic` field, it must be a 32-bit unsigned + * integer field type. Also it must be the first field of the + * packet header field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_header_type, "magic"); + if (field_type) { + const char *field_name; + + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet header field type: `magic` field must be an integer field type: " + "magic-ft-addr=%p, magic-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet header field type: `magic` field must be an unsigned integer field type: " + "magic-ft-addr=%p", field_type); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_get_size(field_type) != 32) { + BT_LOGW("Invalid packet header field type: `magic` field must be a 32-bit unsigned integer field type: " + "magic-ft-addr=%p, magic-ft-size=%u", + field_type, + bt_ctf_field_type_common_integer_get_size(field_type)); + goto invalid; + } + + ret = bt_ctf_field_type_common_structure_borrow_field_by_index( + packet_header_type, &field_name, NULL, 0); + BT_ASSERT(ret == 0); + + if (strcmp(field_name, "magic") != 0) { + BT_LOGW("Invalid packet header field type: `magic` field must be the first field: " + "magic-ft-addr=%p, first-field-name=\"%s\"", + field_type, field_name); + goto invalid; + } + } + + /* + * If there's a `uuid` field, it must be an array field type of + * length 16 with an 8-bit unsigned integer element field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_header_type, "uuid"); + if (field_type) { + struct bt_ctf_field_type_common *elem_ft; + + if (field_type->id != BT_CTF_FIELD_TYPE_ID_ARRAY) { + BT_LOGW("Invalid packet header field type: `uuid` field must be an array field type: " + "uuid-ft-addr=%p, uuid-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_array_get_length(field_type) != 16) { + BT_LOGW("Invalid packet header field type: `uuid` array field type's length must be 16: " + "uuid-ft-addr=%p, uuid-ft-length=%" PRId64, + field_type, + bt_ctf_field_type_common_array_get_length(field_type)); + goto invalid; + } + + elem_ft = bt_ctf_field_type_common_array_borrow_element_field_type(field_type); + BT_ASSERT(elem_ft); + + if (elem_ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an integer field type: " + "elem-ft-addr=%p, elem-ft-id=%s", + elem_ft, + bt_ctf_field_type_id_string(elem_ft->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(elem_ft)) { + BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an unsigned integer field type: " + "elem-ft-addr=%p", elem_ft); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_get_size(elem_ft) != 8) { + BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an 8-bit unsigned integer field type: " + "elem-ft-addr=%p, elem-ft-size=%u", + elem_ft, + bt_ctf_field_type_common_integer_get_size(elem_ft)); + goto invalid; + } + } + + /* + * The `stream_id` field must exist if there's more than one + * stream classes in the trace. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_header_type, "stream_id"); + + if (!field_type && trace->stream_classes->len >= 1) { + BT_LOGW_STR("Invalid packet header field type: " + "`stream_id` field does not exist but there's more than one stream class in the trace."); + goto invalid; + } + + /* + * If there's a `stream_id` field, it must be an unsigned + * integer field type. + */ + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet header field type: `stream_id` field must be an integer field type: " + "stream-id-ft-addr=%p, stream-id-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet header field type: `stream_id` field must be an unsigned integer field type: " + "stream-id-ft-addr=%p", field_type); + goto invalid; + } + } + + /* + * If there's a `packet_seq_num` field, it must be an unsigned + * integer field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_header_type, "packet_seq_num"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an integer field type: " + "stream-id-ft-addr=%p, packet-seq-num-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an unsigned integer field type: " + "packet-seq-num-ft-addr=%p", field_type); + goto invalid; + } + } + + goto end; + +invalid: + is_valid = false; + +end: + return is_valid; +} + +static +bool packet_context_field_type_is_valid(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_type, + bool check_ts_begin_end_mapped) +{ + bool is_valid = true; + struct bt_ctf_field_type_common *field_type = NULL; + + if (!packet_context_type) { + /* No packet context field type: valid at this point */ + goto end; + } + + /* Packet context field type, if it exists, must be a structure */ + if (packet_context_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid packet context field type: must be a structure field type if it exists: " + "ft-addr=%p, ft-id=%s", + packet_context_type, + bt_ctf_field_type_id_string(packet_context_type->id)); + goto invalid; + } + + /* + * If there's a `packet_size` field, it must be an unsigned + * integer field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_context_type, "packet_size"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet context field type: `packet_size` field must be an integer field type: " + "packet-size-ft-addr=%p, packet-size-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet context field type: `packet_size` field must be an unsigned integer field type: " + "packet-size-ft-addr=%p", field_type); + goto invalid; + } + } + + /* + * If there's a `content_size` field, it must be an unsigned + * integer field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_context_type, "content_size"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet context field type: `content_size` field must be an integer field type: " + "content-size-ft-addr=%p, content-size-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet context field type: `content_size` field must be an unsigned integer field type: " + "content-size-ft-addr=%p", field_type); + goto invalid; + } + } + + /* + * If there's a `events_discarded` field, it must be an unsigned + * integer field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_context_type, "events_discarded"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet context field type: `events_discarded` field must be an integer field type: " + "events-discarded-ft-addr=%p, events-discarded-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet context field type: `events_discarded` field must be an unsigned integer field type: " + "events-discarded-ft-addr=%p", field_type); + goto invalid; + } + } + + /* + * If there's a `timestamp_begin` field, it must be an unsigned + * integer field type. Also, if the trace is not a CTF writer's + * trace, then we cannot automatically set the mapped clock + * class of this field, so it must have a mapped clock class. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_context_type, "timestamp_begin"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an integer field type: " + "timestamp-begin-ft-addr=%p, timestamp-begin-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an unsigned integer field type: " + "timestamp-begin-ft-addr=%p", field_type); + goto invalid; + } + + if (check_ts_begin_end_mapped) { + struct bt_ctf_clock_class *clock_class = + bt_ctf_field_type_common_integer_borrow_mapped_clock_class( + field_type); + + if (!clock_class) { + BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be mapped to a clock class: " + "timestamp-begin-ft-addr=%p", field_type); + goto invalid; + } + } + } + + /* + * If there's a `timestamp_end` field, it must be an unsigned + * integer field type. Also, if the trace is not a CTF writer's + * trace, then we cannot automatically set the mapped clock + * class of this field, so it must have a mapped clock class. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + packet_context_type, "timestamp_end"); + if (field_type) { + if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) { + BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an integer field type: " + "timestamp-end-ft-addr=%p, timestamp-end-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + if (bt_ctf_field_type_common_integer_is_signed(field_type)) { + BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an unsigned integer field type: " + "timestamp-end-ft-addr=%p", field_type); + goto invalid; + } + + if (check_ts_begin_end_mapped) { + struct bt_ctf_clock_class *clock_class = + bt_ctf_field_type_common_integer_borrow_mapped_clock_class( + field_type); + + if (!clock_class) { + BT_LOGW("Invalid packet context field type: `timestamp_end` field must be mapped to a clock class: " + "timestamp-end-ft-addr=%p", field_type); + goto invalid; + } + } + } + + goto end; + +invalid: + is_valid = false; + +end: + return is_valid; +} + +static +bool event_header_field_type_is_valid(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *event_header_type) +{ + bool is_valid = true; + struct bt_ctf_field_type_common *field_type = NULL; + + /* + * We do not validate that the `timestamp` field exists here + * because CTF does not require this exact name to be mapped to + * a clock class. + */ + + if (!event_header_type) { + /* + * No event header field type: stream class must have + * only one event class. + */ + if (bt_ctf_stream_class_common_get_event_class_count(stream_class) > 1) { + BT_LOGW_STR("Invalid event header field type: " + "event header field type does not exist but there's more than one event class in the stream class."); + goto invalid; + } + + /* No event header field type: valid at this point */ + goto end; + } + + /* Event header field type, if it exists, must be a structure */ + if (event_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid event header field type: must be a structure field type if it exists: " + "ft-addr=%p, ft-id=%s", + event_header_type, + bt_ctf_field_type_id_string(event_header_type->id)); + goto invalid; + } + + /* + * If there's an `id` field, it must be an unsigned integer + * field type or an enumeration field type with an unsigned + * integer container field type. + */ + field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name( + event_header_type, "id"); + if (field_type) { + struct bt_ctf_field_type_common *int_ft; + + if (field_type->id == BT_CTF_FIELD_TYPE_ID_INTEGER) { + int_ft = field_type; + } else if (field_type->id == BT_CTF_FIELD_TYPE_ID_ENUM) { + int_ft = bt_ctf_field_type_common_enumeration_borrow_container_field_type( + field_type); + } else { + BT_LOGW("Invalid event header field type: `id` field must be an integer or enumeration field type: " + "id-ft-addr=%p, id-ft-id=%s", + field_type, + bt_ctf_field_type_id_string(field_type->id)); + goto invalid; + } + + BT_ASSERT(int_ft); + if (bt_ctf_field_type_common_integer_is_signed(int_ft)) { + BT_LOGW("Invalid event header field type: `id` field must be an unsigned integer or enumeration field type: " + "id-ft-addr=%p", int_ft); + goto invalid; + } + } + + goto end; + +invalid: + is_valid = false; + +end: + return is_valid; +} + +static +int check_packet_header_type_has_no_clock_class(struct bt_ctf_trace_common *trace) +{ + int ret = 0; + + if (trace->packet_header_field_type) { + struct bt_ctf_clock_class *clock_class = NULL; + + ret = bt_ctf_field_type_common_validate_single_clock_class( + trace->packet_header_field_type, + &clock_class); + bt_ctf_object_put_ref(clock_class); + if (ret || clock_class) { + BT_LOGW("Trace's packet header field type cannot " + "contain a field type which is mapped to " + "a clock class: " + "trace-addr=%p, trace-name=\"%s\", " + "clock-class-name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace), + clock_class ? + bt_ctf_clock_class_get_name(clock_class) : + NULL); + ret = -1; + } + } + + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func, + struct bt_ctf_clock_class *init_expected_clock_class, + int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_field_type, + struct bt_ctf_field_type_common *event_header_field_type), + bool check_ts_begin_end_mapped) +{ + int ret; + int64_t i; + int64_t stream_id; + struct bt_ctf_validation_output trace_sc_validation_output = { 0 }; + struct bt_ctf_validation_output *ec_validation_outputs = NULL; + const enum bt_ctf_validation_flag trace_sc_validation_flags = + BT_CTF_VALIDATION_FLAG_TRACE | + BT_CTF_VALIDATION_FLAG_STREAM; + const enum bt_ctf_validation_flag ec_validation_flags = + BT_CTF_VALIDATION_FLAG_EVENT; + struct bt_ctf_field_type_common *packet_header_type = NULL; + struct bt_ctf_field_type_common *packet_context_type = NULL; + struct bt_ctf_field_type_common *event_header_type = NULL; + struct bt_ctf_field_type_common *stream_event_ctx_type = NULL; + int64_t event_class_count; + struct bt_ctf_trace_common *current_parent_trace = NULL; + struct bt_ctf_clock_class *expected_clock_class = + bt_ctf_object_get_ref(init_expected_clock_class); + + BT_ASSERT(copy_field_type_func); + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + BT_LOGD("Adding stream class to trace: " + "trace-addr=%p, trace-name=\"%s\", " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64, + trace, bt_ctf_trace_common_get_name(trace), + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + + current_parent_trace = bt_ctf_stream_class_common_borrow_trace(stream_class); + if (current_parent_trace) { + /* Stream class is already associated to a trace, abort. */ + BT_LOGW("Invalid parameter: stream class is already part of a trace: " + "stream-class-trace-addr=%p, " + "stream-class-trace-name=\"%s\"", + current_parent_trace, + bt_ctf_trace_common_get_name(current_parent_trace)); + ret = -1; + goto end; + } + + event_class_count = + bt_ctf_stream_class_common_get_event_class_count(stream_class); + BT_ASSERT(event_class_count >= 0); + + if (!stream_class->frozen) { + /* + * Stream class is not frozen yet. Validate that the + * stream class contains at most a single clock class + * because the previous + * bt_ctf_stream_class_common_add_event_class() calls did + * not make this validation since the stream class's + * direct field types (packet context, event header, + * event context) could change afterwards. This stream + * class is about to be frozen and those field types + * won't be changed if this function succeeds. + * + * At this point we're also sure that the stream class's + * clock, if any, has the same class as the stream + * class's expected clock class, if any. This is why, if + * bt_ctf_stream_class_common_validate_single_clock_class() + * succeeds below, the call to + * bt_ctf_stream_class_map_clock_class() at the end of this + * function is safe because it maps to the same, single + * clock class. + */ + ret = bt_ctf_stream_class_common_validate_single_clock_class( + stream_class, &expected_clock_class); + if (ret) { + BT_LOGW("Invalid parameter: stream class or one of its " + "event classes contains a field type which is " + "not recursively mapped to the expected " + "clock class: " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "stream-class-name=\"%s\", " + "expected-clock-class-addr=%p, " + "expected-clock-class-name=\"%s\"", + stream_class, bt_ctf_stream_class_common_get_id(stream_class), + bt_ctf_stream_class_common_get_name(stream_class), + expected_clock_class, + expected_clock_class ? + bt_ctf_clock_class_get_name(expected_clock_class) : + NULL); + goto end; + } + } + + ret = check_packet_header_type_has_no_clock_class(trace); + if (ret) { + /* check_packet_header_type_has_no_clock_class() logs errors */ + goto end; + } + + /* + * We're about to freeze both the trace and the stream class. + * Also, each event class contained in this stream class are + * already frozen. + * + * This trace, this stream class, and all its event classes + * should be valid at this point. + * + * Validate trace and stream class first, then each event + * class of this stream class can be validated individually. + */ + packet_header_type = + bt_ctf_trace_common_borrow_packet_header_field_type(trace); + packet_context_type = + bt_ctf_stream_class_common_borrow_packet_context_field_type(stream_class); + event_header_type = + bt_ctf_stream_class_common_borrow_event_header_field_type(stream_class); + stream_event_ctx_type = + bt_ctf_stream_class_common_borrow_event_context_field_type(stream_class); + + BT_LOGD("Validating trace and stream class field types."); + ret = bt_ctf_validate_class_types(trace->environment, + packet_header_type, packet_context_type, event_header_type, + stream_event_ctx_type, NULL, NULL, trace->valid, + stream_class->valid, 1, &trace_sc_validation_output, + trace_sc_validation_flags, copy_field_type_func); + + if (ret) { + /* + * This means something went wrong during the validation + * process, not that the objects are invalid. + */ + BT_LOGE("Failed to validate trace and stream class field types: " + "ret=%d", ret); + goto end; + } + + if ((trace_sc_validation_output.valid_flags & + trace_sc_validation_flags) != + trace_sc_validation_flags) { + /* Invalid trace/stream class */ + BT_LOGW("Invalid trace or stream class field types: " + "valid-flags=0x%x", + trace_sc_validation_output.valid_flags); + ret = -1; + goto end; + } + + if (event_class_count > 0) { + ec_validation_outputs = g_new0(struct bt_ctf_validation_output, + event_class_count); + if (!ec_validation_outputs) { + BT_LOGE_STR("Failed to allocate one validation output structure."); + ret = -1; + goto end; + } + } + + /* Validate each event class individually */ + for (i = 0; i < event_class_count; i++) { + struct bt_ctf_event_class_common *event_class = + bt_ctf_stream_class_common_borrow_event_class_by_index( + stream_class, i); + struct bt_ctf_field_type_common *event_context_type = NULL; + struct bt_ctf_field_type_common *event_payload_type = NULL; + + event_context_type = + bt_ctf_event_class_common_borrow_context_field_type( + event_class); + event_payload_type = + bt_ctf_event_class_common_borrow_payload_field_type( + event_class); + + /* + * It is important to use the field types returned by + * the previous trace and stream class validation here + * because copies could have been made. + */ + BT_LOGD("Validating event class's field types: " + "addr=%p, name=\"%s\", id=%" PRId64, + event_class, bt_ctf_event_class_common_get_name(event_class), + bt_ctf_event_class_common_get_id(event_class)); + ret = bt_ctf_validate_class_types(trace->environment, + trace_sc_validation_output.packet_header_type, + trace_sc_validation_output.packet_context_type, + trace_sc_validation_output.event_header_type, + trace_sc_validation_output.stream_event_ctx_type, + event_context_type, event_payload_type, + 1, 1, event_class->valid, &ec_validation_outputs[i], + ec_validation_flags, copy_field_type_func); + + if (ret) { + BT_LOGE("Failed to validate event class field types: " + "ret=%d", ret); + goto end; + } + + if ((ec_validation_outputs[i].valid_flags & + ec_validation_flags) != ec_validation_flags) { + /* Invalid event class */ + BT_LOGW("Invalid event class field types: " + "valid-flags=0x%x", + ec_validation_outputs[i].valid_flags); + ret = -1; + goto end; + } + } + + stream_id = bt_ctf_stream_class_common_get_id(stream_class); + if (stream_id < 0) { + stream_id = trace->next_stream_id++; + if (stream_id < 0) { + BT_LOGE_STR("No more stream class IDs available."); + ret = -1; + goto end; + } + + /* Try to assign a new stream id */ + for (i = 0; i < trace->stream_classes->len; i++) { + if (stream_id == bt_ctf_stream_class_common_get_id( + trace->stream_classes->pdata[i])) { + /* Duplicate stream id found */ + BT_LOGW("Duplicate stream class ID: " + "id=%" PRId64, (int64_t) stream_id); + ret = -1; + goto end; + } + } + + if (bt_ctf_stream_class_common_set_id_no_check(stream_class, + stream_id)) { + /* TODO Should retry with a different stream id */ + BT_LOGE("Cannot set stream class's ID: " + "id=%" PRId64, (int64_t) stream_id); + ret = -1; + goto end; + } + } + + /* + * At this point all the field types in the validation output + * are valid. Validate the semantics of some scopes according to + * the CTF specification. + */ + if (!packet_header_field_type_is_valid(trace, + trace_sc_validation_output.packet_header_type)) { + BT_LOGW_STR("Invalid trace's packet header field type."); + ret = -1; + goto end; + } + + if (!packet_context_field_type_is_valid(trace, + stream_class, + trace_sc_validation_output.packet_context_type, + check_ts_begin_end_mapped)) { + BT_LOGW_STR("Invalid stream class's packet context field type."); + ret = -1; + goto end; + } + + if (!event_header_field_type_is_valid(trace, + stream_class, + trace_sc_validation_output.event_header_type)) { + BT_LOGW_STR("Invalid steam class's event header field type."); + ret = -1; + goto end; + } + + /* + * Now is the time to automatically map specific field types of + * the stream class's packet context and event header field + * types to the stream class's clock's class if they are not + * mapped to a clock class yet. We do it here because we know + * that after this point, everything is frozen so it won't be + * possible for the user to modify the stream class's clock, or + * to map those field types to other clock classes. + */ + if (map_clock_classes_func) { + if (map_clock_classes_func(stream_class, + trace_sc_validation_output.packet_context_type, + trace_sc_validation_output.event_header_type)) { + /* map_clock_classes_func() logs errors */ + ret = -1; + goto end; + } + } + + bt_ctf_object_set_parent(&stream_class->base, &trace->base); + g_ptr_array_add(trace->stream_classes, stream_class); + + /* + * At this point we know that the function will be successful. + * Therefore we can replace the trace and stream class field + * types with what's in their validation output structure and + * mark them as valid. We can also replace the field types of + * all the event classes of the stream class and mark them as + * valid. + */ + bt_ctf_validation_replace_types(trace, stream_class, NULL, + &trace_sc_validation_output, trace_sc_validation_flags); + trace->valid = 1; + stream_class->valid = 1; + + /* + * Put what was not moved in bt_ctf_validation_replace_types(). + */ + bt_ctf_validation_output_put_types(&trace_sc_validation_output); + + for (i = 0; i < event_class_count; i++) { + struct bt_ctf_event_class_common *event_class = + bt_ctf_stream_class_common_borrow_event_class_by_index( + stream_class, i); + + bt_ctf_validation_replace_types(NULL, NULL, event_class, + &ec_validation_outputs[i], ec_validation_flags); + event_class->valid = 1; + + /* + * Put what was not moved in + * bt_ctf_validation_replace_types(). + */ + bt_ctf_validation_output_put_types(&ec_validation_outputs[i]); + } + + /* + * Freeze the trace and the stream class. + */ + bt_ctf_stream_class_common_freeze(stream_class); + bt_ctf_trace_common_freeze(trace); + + /* + * It is safe to set the stream class's unique clock class + * now because the stream class is frozen. + */ + if (expected_clock_class) { + BT_CTF_OBJECT_MOVE_REF(stream_class->clock_class, expected_clock_class); + } + + BT_LOGD("Added stream class to trace: " + "trace-addr=%p, trace-name=\"%s\", " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRId64, + trace, bt_ctf_trace_common_get_name(trace), + stream_class, bt_ctf_stream_class_common_get_name(stream_class), + bt_ctf_stream_class_common_get_id(stream_class)); + +end: + if (ret) { + bt_ctf_object_set_parent(&stream_class->base, NULL); + + if (ec_validation_outputs) { + for (i = 0; i < event_class_count; i++) { + bt_ctf_validation_output_put_types( + &ec_validation_outputs[i]); + } + } + } + + g_free(ec_validation_outputs); + bt_ctf_validation_output_put_types(&trace_sc_validation_output); + bt_ctf_object_put_ref(expected_clock_class); + return ret; +} + +BT_HIDDEN +bt_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_clock_class *clock_class) +{ + struct bt_ctf_search_query query = { .value = clock_class, .found = 0 }; + + BT_ASSERT(trace); + BT_ASSERT(clock_class); + + g_ptr_array_foreach(trace->clock_classes, value_exists, &query); + return query.found; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace, + enum bt_ctf_byte_order byte_order, bool allow_unspecified) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (trace->frozen) { + BT_LOGW("Invalid parameter: trace is frozen: " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + ret = -1; + goto end; + } + + if (byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED && !allow_unspecified) { + BT_LOGW("Invalid parameter: BT_CTF_BYTE_ORDER_UNSPECIFIED byte order is not allowed: " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + ret = -1; + goto end; + } + + if (byte_order != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN && + byte_order != BT_CTF_BYTE_ORDER_BIG_ENDIAN && + byte_order != BT_CTF_BYTE_ORDER_NETWORK) { + BT_LOGW("Invalid parameter: invalid byte order: " + "addr=%p, name=\"%s\", bo=%s", + trace, bt_ctf_trace_common_get_name(trace), + bt_ctf_byte_order_string(byte_order)); + ret = -1; + goto end; + } + + trace->native_byte_order = byte_order; + BT_LOGV("Set trace's native byte order: " + "addr=%p, name=\"%s\", bo=%s", + trace, bt_ctf_trace_common_get_name(trace), + bt_ctf_byte_order_string(byte_order)); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace, + struct bt_ctf_field_type_common *packet_header_type) +{ + int ret = 0; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (trace->frozen) { + BT_LOGW("Invalid parameter: trace is frozen: " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + ret = -1; + goto end; + } + + /* packet_header_type must be a structure. */ + if (packet_header_type && + packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) { + BT_LOGW("Invalid parameter: packet header field type must be a structure field type if it exists: " + "addr=%p, name=\"%s\", ft-addr=%p, ft-id=%s", + trace, bt_ctf_trace_common_get_name(trace), + packet_header_type, + bt_ctf_field_type_id_string(packet_header_type->id)); + ret = -1; + goto end; + } + + bt_ctf_object_put_ref(trace->packet_header_field_type); + trace->packet_header_field_type = bt_ctf_object_get_ref(packet_header_type); + BT_LOGV("Set trace's packet header field type: " + "addr=%p, name=\"%s\", packet-context-ft-addr=%p", + trace, bt_ctf_trace_common_get_name(trace), packet_header_type); +end: + return ret; +} + +static +int64_t get_stream_class_count(void *element) +{ + return bt_ctf_trace_get_stream_class_count( + (struct bt_ctf_trace *) element); +} + +static +void *get_stream_class(void *element, int i) +{ + return bt_ctf_trace_get_stream_class_by_index( + (struct bt_ctf_trace *) element, i); +} + +static +int visit_stream_class(void *object, bt_ctf_visitor visitor,void *data) +{ + return bt_ctf_stream_class_visit(object, visitor, data); +} + +int bt_ctf_trace_visit(struct bt_ctf_trace *trace, + bt_ctf_visitor visitor, void *data) +{ + int ret; + struct bt_ctf_visitor_object obj = { + .object = trace, + .type = BT_CTF_VISITOR_OBJECT_TYPE_TRACE + }; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!visitor) { + BT_LOGW_STR("Invalid parameter: visitor is NULL."); + ret = -1; + goto end; + } + + BT_LOGV("Visiting trace: addr=%p, name=\"%s\"", + trace, bt_ctf_trace_get_name(trace)); + ret = bt_ctf_visitor_helper(&obj, get_stream_class_count, + get_stream_class, visit_stream_class, visitor, data); +end: + return ret; +} + +static +void bt_ctf_trace_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_trace *trace = (void *) obj; + + BT_LOGD("Destroying CTF writer trace object: addr=%p, name=\"%s\"", + trace, bt_ctf_trace_get_name(trace)); + bt_ctf_trace_common_finalize(BT_CTF_TO_COMMON(trace)); + g_free(trace); +} + +BT_HIDDEN +struct bt_ctf_trace *bt_ctf_trace_create(void) +{ + struct bt_ctf_trace *trace = NULL; + int ret; + + BT_LOGD_STR("Creating CTF writer trace object."); + trace = g_new0(struct bt_ctf_trace, 1); + if (!trace) { + BT_LOGE_STR("Failed to allocate one CTF writer trace."); + goto error; + } + + ret = bt_ctf_trace_common_initialize(BT_CTF_TO_COMMON(trace), + bt_ctf_trace_destroy); + if (ret) { + /* bt_ctf_trace_common_initialize() logs errors */ + goto error; + } + + BT_LOGD("Created CTF writer trace object: addr=%p", trace); + return trace; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(trace); + return trace; +} + +const unsigned char *bt_ctf_trace_get_uuid(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_uuid(BT_CTF_TO_COMMON(trace)); +} + +int bt_ctf_trace_set_uuid(struct bt_ctf_trace *trace, + const unsigned char *uuid) +{ + return bt_ctf_trace_common_set_uuid(BT_CTF_TO_COMMON(trace), uuid); +} + +int bt_ctf_trace_set_environment_field_string(struct bt_ctf_trace *trace, + const char *name, const char *value) +{ + return bt_ctf_trace_common_set_environment_field_string(BT_CTF_TO_COMMON(trace), + name, value); +} + +int bt_ctf_trace_set_environment_field_integer( + struct bt_ctf_trace *trace, const char *name, int64_t value) +{ + return bt_ctf_trace_common_set_environment_field_integer( + BT_CTF_TO_COMMON(trace), name, value); +} + +int64_t bt_ctf_trace_get_environment_field_count(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_environment_field_count(BT_CTF_TO_COMMON(trace)); +} + +const char * +bt_ctf_trace_get_environment_field_name_by_index(struct bt_ctf_trace *trace, + uint64_t index) +{ + return bt_ctf_trace_common_get_environment_field_name_by_index( + BT_CTF_TO_COMMON(trace), index); +} + +struct bt_ctf_value *bt_ctf_trace_get_environment_field_value_by_index( + struct bt_ctf_trace *trace, uint64_t index) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_environment_field_value_by_index( + BT_CTF_TO_COMMON(trace), index)); +} + +struct bt_ctf_value *bt_ctf_trace_get_environment_field_value_by_name( + struct bt_ctf_trace *trace, const char *name) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_environment_field_value_by_name( + BT_CTF_TO_COMMON(trace), name)); +} + +BT_HIDDEN +int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace, + struct bt_ctf_clock_class *clock_class) +{ + return bt_ctf_trace_common_add_clock_class(BT_CTF_TO_COMMON(trace), + (void *) clock_class); +} + +BT_HIDDEN +int64_t bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_clock_class_count(BT_CTF_TO_COMMON(trace)); +} + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index( + struct bt_ctf_trace *trace, uint64_t index) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_clock_class_by_index( + BT_CTF_TO_COMMON(trace), index)); +} + +static +int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type) +{ + int ret = bt_ctf_stream_class_map_clock_class( + BT_CTF_FROM_COMMON(stream_class), + BT_CTF_FROM_COMMON(packet_context_type), + BT_CTF_FROM_COMMON(event_header_type)); + + if (ret) { + BT_LOGW_STR("Cannot automatically map selected stream class's field types to stream class's clock's class."); + } + + return ret; +} + +int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace, + struct bt_ctf_stream_class *stream_class) +{ + int ret = 0; + struct bt_ctf_clock_class *expected_clock_class = NULL; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + ret = -1; + goto end; + } + + if (!stream_class) { + BT_LOGW_STR("Invalid parameter: stream class is NULL."); + ret = -1; + goto end; + } + + if (stream_class->clock) { + struct bt_ctf_clock_class *stream_clock_class = + stream_class->clock->clock_class; + + /* + * Make sure this clock was also added to the + * trace (potentially through its CTF writer + * owner). + */ + size_t i; + + for (i = 0; i < trace->common.clock_classes->len; i++) { + if (trace->common.clock_classes->pdata[i] == + stream_clock_class) { + /* Found! */ + break; + } + } + + if (i == trace->common.clock_classes->len) { + /* Not found */ + BT_LOGW("Stream class's clock's class is not part of the trace: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + stream_clock_class, + bt_ctf_clock_class_get_name(stream_clock_class)); + ret = -1; + goto end; + } + + if (stream_class->common.clock_class && + stream_class->common.clock_class != + stream_class->clock->clock_class) { + /* + * Stream class already has an expected clock + * class, but it does not match its clock's + * class. + */ + BT_LOGW("Invalid parameter: stream class's clock's " + "class does not match stream class's " + "expected clock class: " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "stream-class-name=\"%s\", " + "expected-clock-class-addr=%p, " + "expected-clock-class-name=\"%s\"", + stream_class, + bt_ctf_stream_class_get_id(stream_class), + bt_ctf_stream_class_get_name(stream_class), + stream_class->common.clock_class, + bt_ctf_clock_class_get_name(stream_class->common.clock_class)); + } else if (!stream_class->common.clock_class) { + /* + * Set expected clock class to stream class's + * clock's class. + */ + expected_clock_class = stream_class->clock->clock_class; + } + } + + + ret = bt_ctf_trace_common_add_stream_class(BT_CTF_TO_COMMON(trace), + BT_CTF_TO_COMMON(stream_class), + (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy, + expected_clock_class, map_clock_classes_func, + false); + +end: + return ret; +} + +int64_t bt_ctf_trace_get_stream_count(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_stream_count(BT_CTF_TO_COMMON(trace)); +} + +struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index( + struct bt_ctf_trace *trace, uint64_t index) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_by_index( + BT_CTF_TO_COMMON(trace), index)); +} + +int64_t bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_stream_class_count(BT_CTF_TO_COMMON(trace)); +} + +struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index( + struct bt_ctf_trace *trace, uint64_t index) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_class_by_index( + BT_CTF_TO_COMMON(trace), index)); +} + +struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id( + struct bt_ctf_trace *trace, uint64_t id) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_stream_class_by_id( + BT_CTF_TO_COMMON(trace), id)); +} + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name( + struct bt_ctf_trace *trace, const char *name) +{ + return bt_ctf_object_get_ref( + bt_ctf_trace_common_borrow_clock_class_by_name(BT_CTF_TO_COMMON(trace), + name)); +} + +static +int append_trace_metadata(struct bt_ctf_trace *trace, + struct metadata_context *context) +{ + unsigned char *uuid = trace->common.uuid; + int ret = 0; + + if (trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NATIVE || + trace->common.native_byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED) { + BT_LOGW("Invalid parameter: trace's byte order cannot be BT_CTF_BYTE_ORDER_NATIVE or BT_CTF_BYTE_ORDER_UNSPECIFIED at this point; " + "set it with bt_ctf_trace_set_native_byte_order(): " + "addr=%p, name=\"%s\"", + trace, bt_ctf_trace_get_name(trace)); + ret = -1; + goto end; + } + + g_string_append(context->string, "trace {\n"); + g_string_append(context->string, "\tmajor = 1;\n"); + g_string_append(context->string, "\tminor = 8;\n"); + BT_ASSERT(trace->common.native_byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN || + trace->common.native_byte_order == BT_CTF_BYTE_ORDER_BIG_ENDIAN || + trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NETWORK); + + if (trace->common.uuid_set) { + g_string_append_printf(context->string, + "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + + g_string_append_printf(context->string, "\tbyte_order = %s;\n", + bt_ctf_get_byte_order_string(trace->common.native_byte_order)); + + if (trace->common.packet_header_field_type) { + g_string_append(context->string, "\tpacket.header := "); + context->current_indentation_level++; + g_string_assign(context->field_name, ""); + BT_LOGD_STR("Serializing trace's packet header field type's metadata."); + ret = bt_ctf_field_type_serialize_recursive( + (void *) trace->common.packet_header_field_type, + context); + if (ret) { + goto end; + } + context->current_indentation_level--; + } + + g_string_append(context->string, ";\n};\n\n"); +end: + return ret; +} + +static +void append_env_metadata(struct bt_ctf_trace *trace, + struct metadata_context *context) +{ + int64_t i; + int64_t env_size; + + env_size = bt_ctf_attributes_get_count(trace->common.environment); + if (env_size <= 0) { + return; + } + + g_string_append(context->string, "env {\n"); + + for (i = 0; i < env_size; i++) { + struct bt_ctf_private_value *env_field_value_obj = NULL; + const char *entry_name; + + entry_name = bt_ctf_attributes_get_field_name( + trace->common.environment, i); + env_field_value_obj = bt_ctf_attributes_borrow_field_value( + trace->common.environment, i); + + BT_ASSERT(entry_name); + BT_ASSERT(env_field_value_obj); + + switch (bt_ctf_value_get_type( + bt_ctf_private_value_as_value(env_field_value_obj))) { + case BT_CTF_VALUE_TYPE_INTEGER: + { + int64_t int_value; + + int_value = bt_ctf_value_integer_get( + bt_ctf_private_value_as_value( + env_field_value_obj)); + g_string_append_printf(context->string, + "\t%s = %" PRId64 ";\n", entry_name, + int_value); + break; + } + case BT_CTF_VALUE_TYPE_STRING: + { + const char *str_value; + char *escaped_str = NULL; + + str_value = bt_ctf_value_string_get( + bt_ctf_private_value_as_value( + env_field_value_obj)); + escaped_str = g_strescape(str_value, NULL); + if (!escaped_str) { + BT_LOGE("Cannot escape string: string=\"%s\"", + str_value); + continue; + } + + g_string_append_printf(context->string, + "\t%s = \"%s\";\n", entry_name, escaped_str); + free(escaped_str); + break; + } + default: + continue; + } + } + + g_string_append(context->string, "};\n\n"); +} + +char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace) +{ + char *metadata = NULL; + struct metadata_context *context = NULL; + int err = 0; + size_t i; + + if (!trace) { + BT_LOGW_STR("Invalid parameter: trace is NULL."); + goto end; + } + + context = g_new0(struct metadata_context, 1); + if (!context) { + BT_LOGE_STR("Failed to allocate one metadata context."); + goto end; + } + + context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE); + context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE); + g_string_append(context->string, "/* CTF 1.8 */\n\n"); + if (append_trace_metadata(trace, context)) { + /* append_trace_metadata() logs errors */ + goto error; + } + append_env_metadata(trace, context); + g_ptr_array_foreach(trace->common.clock_classes, + (GFunc) bt_ctf_clock_class_serialize, context); + + for (i = 0; i < trace->common.stream_classes->len; i++) { + /* bt_ctf_stream_class_serialize() logs details */ + err = bt_ctf_stream_class_serialize( + trace->common.stream_classes->pdata[i], context); + if (err) { + /* bt_ctf_stream_class_serialize() logs errors */ + goto error; + } + } + + metadata = context->string->str; + +error: + g_string_free(context->string, err ? TRUE : FALSE); + g_string_free(context->field_name, TRUE); + g_free(context); + +end: + return metadata; +} + +enum bt_ctf_byte_order bt_ctf_trace_get_native_byte_order( + struct bt_ctf_trace *trace) +{ + return (int) bt_ctf_trace_common_get_native_byte_order(BT_CTF_TO_COMMON(trace)); +} + +int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace, + enum bt_ctf_byte_order byte_order) +{ + return bt_ctf_trace_common_set_native_byte_order(BT_CTF_TO_COMMON(trace), + (int) byte_order, false); +} + +struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_field_type( + struct bt_ctf_trace *trace) +{ + return bt_ctf_object_get_ref(bt_ctf_trace_common_borrow_packet_header_field_type( + BT_CTF_TO_COMMON(trace))); +} + +int bt_ctf_trace_set_packet_header_field_type(struct bt_ctf_trace *trace, + struct bt_ctf_field_type *packet_header_type) +{ + return bt_ctf_trace_common_set_packet_header_field_type(BT_CTF_TO_COMMON(trace), + (void *) packet_header_type); +} + +const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace) +{ + return bt_ctf_trace_common_get_name(BT_CTF_TO_COMMON(trace)); +} diff --git a/src/ctf-writer/trace.h b/src/ctf-writer/trace.h new file mode 100644 index 00000000..ae5f1ec3 --- /dev/null +++ b/src/ctf-writer/trace.h @@ -0,0 +1,391 @@ +#ifndef BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H +#define BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H + +/* + * Copyright 2014 EfficiOS Inc. + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "common/babeltrace.h" +#include "compat/uuid.h" +#include +#include +#include +#include +#include +#include + +#include "assert-pre.h" +#include "attributes.h" +#include "clock-class.h" +#include "object.h" +#include "stream-class.h" +#include "validation.h" +#include "values.h" + +struct bt_ctf_trace_common { + struct bt_ctf_object base; + GString *name; + int frozen; + unsigned char uuid[BABELTRACE_UUID_LEN]; + bt_bool uuid_set; + enum bt_ctf_byte_order native_byte_order; + struct bt_ctf_private_value *environment; + GPtrArray *clock_classes; /* Array of pointers to bt_ctf_clock_class */ + GPtrArray *stream_classes; /* Array of ptrs to bt_ctf_stream_class_common */ + GPtrArray *streams; /* Array of ptrs to bt_ctf_stream_common */ + struct bt_ctf_field_type_common *packet_header_field_type; + int64_t next_stream_id; + + /* + * This flag indicates if the trace is valid. A valid + * trace is _always_ frozen. + */ + int valid; +}; + +BT_HIDDEN +bt_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace, + bt_ctf_object_release_func release_func); + +BT_HIDDEN +void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace); + +static inline +const char *bt_ctf_trace_common_get_name(struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->name ? trace->name->str : NULL; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name); + +static inline +const unsigned char *bt_ctf_trace_common_get_uuid(struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->uuid_set ? trace->uuid : NULL; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace, const unsigned char *uuid); + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace, + const char *name, struct bt_ctf_private_value *value); + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace, + const char *name, const char *value); + +BT_HIDDEN +int bt_ctf_trace_common_set_environment_field_integer(struct bt_ctf_trace_common *trace, + const char *name, int64_t value); + +static inline +int64_t bt_ctf_trace_common_get_environment_field_count( + struct bt_ctf_trace_common *trace) +{ + int64_t ret; + + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + ret = bt_ctf_attributes_get_count(trace->environment); + BT_ASSERT(ret >= 0); + return ret; +} + +static inline +const char * +bt_ctf_trace_common_get_environment_field_name_by_index( + struct bt_ctf_trace_common *trace, uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return bt_ctf_attributes_get_field_name(trace->environment, index); +} + +static inline +struct bt_ctf_private_value * +bt_ctf_trace_common_borrow_environment_field_value_by_index( + struct bt_ctf_trace_common *trace, uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return bt_ctf_attributes_borrow_field_value(trace->environment, index); +} + +static inline +struct bt_ctf_private_value * +bt_ctf_trace_common_borrow_environment_field_value_by_name( + struct bt_ctf_trace_common *trace, const char *name) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + return bt_ctf_attributes_borrow_field_value_by_name(trace->environment, + name); +} + +BT_HIDDEN +int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_clock_class *clock_class); + +static inline +int64_t bt_ctf_trace_common_get_clock_class_count(struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->clock_classes->len; +} + +static inline +struct bt_ctf_clock_class *bt_ctf_trace_common_borrow_clock_class_by_index( + struct bt_ctf_trace_common *trace, uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE(index < trace->clock_classes->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u", + index, trace->clock_classes->len); + return g_ptr_array_index(trace->clock_classes, index); +} + +static inline +int64_t bt_ctf_trace_common_get_stream_count(struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return (int64_t) trace->streams->len; +} + +static inline +struct bt_ctf_stream_common *bt_ctf_trace_common_borrow_stream_by_index( + struct bt_ctf_trace_common *trace, + uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE(index < trace->streams->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u", + index, trace->streams->len); + return g_ptr_array_index(trace->streams, index); +} + +static inline +int64_t bt_ctf_trace_common_get_stream_class_count(struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return (int64_t) trace->stream_classes->len; +} + +static inline +struct bt_ctf_stream_class_common *bt_ctf_trace_common_borrow_stream_class_by_index( + struct bt_ctf_trace_common *trace, uint64_t index) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE(index < trace->stream_classes->len, + "Index is out of bounds: index=%" PRIu64 ", " + "count=%u", + index, trace->stream_classes->len); + return g_ptr_array_index(trace->stream_classes, index); +} + +static inline +struct bt_ctf_stream_class_common *bt_ctf_trace_common_borrow_stream_class_by_id( + struct bt_ctf_trace_common *trace, uint64_t id_param) +{ + int i; + struct bt_ctf_stream_class_common *stream_class = NULL; + int64_t id = (int64_t) id_param; + + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE(id >= 0, + "Invalid stream class ID: %" PRIu64, id_param); + + for (i = 0; i < trace->stream_classes->len; i++) { + struct bt_ctf_stream_class_common *stream_class_candidate; + + stream_class_candidate = + g_ptr_array_index(trace->stream_classes, i); + + if (bt_ctf_stream_class_common_get_id(stream_class_candidate) == + (int64_t) id) { + stream_class = stream_class_candidate; + goto end; + } + } + +end: + return stream_class; +} + +static inline +struct bt_ctf_clock_class *bt_ctf_trace_common_borrow_clock_class_by_name( + struct bt_ctf_trace_common *trace, const char *name) +{ + size_t i; + struct bt_ctf_clock_class *clock_class = NULL; + + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_CTF_ASSERT_PRE_NON_NULL(name, "Name"); + + for (i = 0; i < trace->clock_classes->len; i++) { + struct bt_ctf_clock_class *cur_clk = + g_ptr_array_index(trace->clock_classes, i); + const char *cur_clk_name = bt_ctf_clock_class_get_name(cur_clk); + + if (!cur_clk_name) { + goto end; + } + + if (!strcmp(cur_clk_name, name)) { + clock_class = cur_clk; + goto end; + } + } + +end: + return clock_class; +} + +static inline +enum bt_ctf_byte_order bt_ctf_trace_common_get_native_byte_order( + struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->native_byte_order; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace, + enum bt_ctf_byte_order byte_order, bool allow_unspecified); + +static inline +struct bt_ctf_field_type_common *bt_ctf_trace_common_borrow_packet_header_field_type( + struct bt_ctf_trace_common *trace) +{ + BT_CTF_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->packet_header_field_type; +} + +BT_HIDDEN +int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace, + struct bt_ctf_field_type_common *packet_header_field_type); + +static inline +void bt_ctf_trace_common_freeze(struct bt_ctf_trace_common *trace) +{ + int i; + + if (trace->frozen) { + return; + } + + BT_LOGD("Freezing trace: addr=%p, name=\"%s\"", + trace, bt_ctf_trace_common_get_name(trace)); + BT_LOGD_STR("Freezing packet header field type."); + bt_ctf_field_type_common_freeze(trace->packet_header_field_type); + BT_LOGD_STR("Freezing environment attributes."); + bt_ctf_attributes_freeze(trace->environment); + + if (trace->clock_classes->len > 0) { + BT_LOGD_STR("Freezing clock classes."); + } + + for (i = 0; i < trace->clock_classes->len; i++) { + struct bt_ctf_clock_class *clock_class = + g_ptr_array_index(trace->clock_classes, i); + + bt_ctf_clock_class_freeze(clock_class); + } + + trace->frozen = 1; +} + +BT_HIDDEN +int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func, + struct bt_ctf_clock_class *init_expected_clock_class, + int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_field_type_common *packet_context_field_type, + struct bt_ctf_field_type_common *event_header_field_type), + bool check_ts_begin_end_mapped); + +struct bt_ctf_trace { + struct bt_ctf_trace_common common; +}; + +/* + * bt_ctf_trace_get_metadata_string: get metadata string. + * + * Get the trace's TSDL metadata. The caller assumes the ownership of the + * returned string. + * + * @param trace Trace instance. + * + * Returns the metadata string on success, NULL on error. + */ +BT_HIDDEN +char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace); + +BT_HIDDEN +struct bt_ctf_trace *bt_ctf_trace_create(void); + +BT_HIDDEN +int64_t bt_ctf_trace_get_clock_class_count( + struct bt_ctf_trace *trace); + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index( + struct bt_ctf_trace *trace, uint64_t index); + +BT_HIDDEN +struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name( + struct bt_ctf_trace *trace, const char *name); + +BT_HIDDEN +int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace, + struct bt_ctf_clock_class *clock_class); + +BT_HIDDEN +int64_t bt_ctf_trace_get_environment_field_count( + struct bt_ctf_trace *trace); + +BT_HIDDEN +const char *bt_ctf_trace_get_environment_field_name_by_index( + struct bt_ctf_trace *trace, uint64_t index); + +BT_HIDDEN +struct bt_ctf_value * +bt_ctf_trace_get_environment_field_value_by_index(struct bt_ctf_trace *trace, + uint64_t index); + +BT_HIDDEN +struct bt_ctf_value * +bt_ctf_trace_get_environment_field_value_by_name( + struct bt_ctf_trace *trace, const char *name); + +#endif /* BABELTRACE_CTF_WRITER_TRACE_INTERNAL_H */ diff --git a/src/ctf-writer/utils.c b/src/ctf-writer/utils.c new file mode 100644 index 00000000..b63883e4 --- /dev/null +++ b/src/ctf-writer/utils.c @@ -0,0 +1,125 @@ +/* + * utils.c + * + * Babeltrace CTF writer - Utilities + * + * Copyright 2015 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-UTILS" +#include "logging.h" + +#include +#include + +#include +#include + +#include "common/assert.h" + +#include "clock-class.h" +#include "field-types.h" + +static +const char * const reserved_keywords_str[] = {"align", "callsite", + "const", "char", "clock", "double", "enum", "env", "event", + "floating_point", "float", "integer", "int", "long", "short", "signed", + "stream", "string", "struct", "trace", "typealias", "typedef", + "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"}; + +static GHashTable *reserved_keywords_set; +static int init_done; + +static +void try_init_reserved_keywords(void) +{ + size_t i; + const size_t reserved_keywords_count = + sizeof(reserved_keywords_str) / sizeof(char *); + + if (reserved_keywords_set) { + return; + } + + reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal); + BT_ASSERT(reserved_keywords_set); + + for (i = 0; i < reserved_keywords_count; i++) { + gpointer quark = GINT_TO_POINTER(g_quark_from_string( + reserved_keywords_str[i])); + + g_hash_table_insert(reserved_keywords_set, quark, quark); + } + + init_done = 1; +} + +static __attribute__((destructor)) +void trace_finalize(void) +{ + if (reserved_keywords_set) { + g_hash_table_destroy(reserved_keywords_set); + } +} + +bt_bool bt_ctf_identifier_is_valid(const char *identifier) +{ + bt_bool is_valid = BT_TRUE; + char *string = NULL; + char *save_ptr, *token; + + if (!identifier) { + BT_LOGV_STR("Invalid parameter: input string is NULL."); + is_valid = BT_FALSE; + goto end; + } + + try_init_reserved_keywords(); + + if (identifier[0] == '\0') { + is_valid = BT_FALSE; + goto end; + } + + string = strdup(identifier); + if (!string) { + BT_LOGE("strdup() failed."); + is_valid = BT_FALSE; + goto end; + } + + token = strtok_r(string, " ", &save_ptr); + while (token) { + if (g_hash_table_lookup_extended(reserved_keywords_set, + GINT_TO_POINTER(g_quark_from_string(token)), + NULL, NULL)) { + is_valid = BT_FALSE; + goto end; + } + + token = strtok_r(NULL, " ", &save_ptr); + } +end: + free(string); + return is_valid; +} diff --git a/src/ctf-writer/utils.h b/src/ctf-writer/utils.h new file mode 100644 index 00000000..488251ce --- /dev/null +++ b/src/ctf-writer/utils.h @@ -0,0 +1,224 @@ +#ifndef BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H +#define BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H + +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include +#include + +#include "field-path.h" + +#define BT_CTF_TO_COMMON(_obj) (&(_obj)->common) +#define BT_CTF_FROM_COMMON(_obj) ((void *) _obj) + +struct bt_ctf_search_query { + gpointer value; + int found; +}; + +BT_HIDDEN +const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order); + +static inline +const char *bt_ctf_field_type_id_string(enum bt_ctf_field_type_id type_id) +{ + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_UNKNOWN: + return "BT_CTF_FIELD_TYPE_ID_UNKNOWN"; + case BT_CTF_FIELD_TYPE_ID_INTEGER: + return "BT_CTF_FIELD_TYPE_ID_INTEGER"; + case BT_CTF_FIELD_TYPE_ID_FLOAT: + return "BT_CTF_FIELD_TYPE_ID_FLOAT"; + case BT_CTF_FIELD_TYPE_ID_ENUM: + return "BT_CTF_FIELD_TYPE_ID_ENUM"; + case BT_CTF_FIELD_TYPE_ID_STRING: + return "BT_CTF_FIELD_TYPE_ID_STRING"; + case BT_CTF_FIELD_TYPE_ID_STRUCT: + return "BT_CTF_FIELD_TYPE_ID_STRUCT"; + case BT_CTF_FIELD_TYPE_ID_ARRAY: + return "BT_CTF_FIELD_TYPE_ID_ARRAY"; + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + return "BT_CTF_FIELD_TYPE_ID_SEQUENCE"; + case BT_CTF_FIELD_TYPE_ID_VARIANT: + return "BT_CTF_FIELD_TYPE_ID_VARIANT"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_ctf_byte_order_string(enum bt_ctf_byte_order bo) +{ + switch (bo) { + case BT_CTF_BYTE_ORDER_UNKNOWN: + return "BT_CTF_BYTE_ORDER_UNKNOWN"; + case BT_CTF_BYTE_ORDER_UNSPECIFIED: + return "BT_CTF_BYTE_ORDER_UNSPECIFIED"; + case BT_CTF_BYTE_ORDER_NATIVE: + return "BT_CTF_BYTE_ORDER_NATIVE"; + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + return "BT_CTF_BYTE_ORDER_LITTLE_ENDIAN"; + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + return "BT_CTF_BYTE_ORDER_BIG_ENDIAN"; + case BT_CTF_BYTE_ORDER_NETWORK: + return "BT_CTF_BYTE_ORDER_NETWORK"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_ctf_string_encoding_string(enum bt_ctf_string_encoding encoding) +{ + switch (encoding) { + case BT_CTF_STRING_ENCODING_UNKNOWN: + return "BT_CTF_STRING_ENCODING_UNKNOWN"; + case BT_CTF_STRING_ENCODING_NONE: + return "BT_CTF_STRING_ENCODING_NONE"; + case BT_CTF_STRING_ENCODING_UTF8: + return "BT_CTF_STRING_ENCODING_UTF8"; + case BT_CTF_STRING_ENCODING_ASCII: + return "BT_CTF_STRING_ENCODING_ASCII"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_ctf_integer_base_string(enum bt_ctf_integer_base base) +{ + switch (base) { + case BT_CTF_INTEGER_BASE_UNKNOWN: + return "BT_CTF_INTEGER_BASE_UNKNOWN"; + case BT_CTF_INTEGER_BASE_UNSPECIFIED: + return "BT_CTF_INTEGER_BASE_UNSPECIFIED"; + case BT_CTF_INTEGER_BASE_BINARY: + return "BT_CTF_INTEGER_BASE_BINARY"; + case BT_CTF_INTEGER_BASE_OCTAL: + return "BT_CTF_INTEGER_BASE_OCTAL"; + case BT_CTF_INTEGER_BASE_DECIMAL: + return "BT_CTF_INTEGER_BASE_DECIMAL"; + case BT_CTF_INTEGER_BASE_HEXADECIMAL: + return "BT_CTF_INTEGER_BASE_HEXADECIMAL"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_ctf_scope_string(enum bt_ctf_scope scope) +{ + switch (scope) { + case BT_CTF_SCOPE_UNKNOWN: + return "BT_CTF_SCOPE_UNKNOWN"; + case BT_CTF_SCOPE_TRACE_PACKET_HEADER: + return "BT_CTF_SCOPE_TRACE_PACKET_HEADER"; + case BT_CTF_SCOPE_STREAM_PACKET_CONTEXT: + return "BT_CTF_SCOPE_STREAM_PACKET_CONTEXT"; + case BT_CTF_SCOPE_STREAM_EVENT_HEADER: + return "BT_CTF_SCOPE_STREAM_EVENT_HEADER"; + case BT_CTF_SCOPE_STREAM_EVENT_CONTEXT: + return "BT_CTF_SCOPE_STREAM_EVENT_CONTEXT"; + case BT_CTF_SCOPE_EVENT_CONTEXT: + return "BT_CTF_SCOPE_EVENT_CONTEXT"; + case BT_CTF_SCOPE_EVENT_PAYLOAD: + return "BT_CTF_SCOPE_EVENT_PAYLOAD"; + case BT_CTF_SCOPE_ENV: + return "BT_CTF_SCOPE_ENV"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_ctf_event_class_log_level_string( + enum bt_ctf_event_class_log_level level) +{ + switch (level) { + case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_UNKNOWN"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_UNSPECIFIED"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_EMERGENCY"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_ALERT"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_CRITICAL"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_ERROR"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_WARNING"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_NOTICE"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_INFO"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE"; + case BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG: + return "BT_CTF_EVENT_CLASS_LOG_LEVEL_DEBUG"; + default: + return "(unknown)"; + } +}; + +static inline +GString *bt_ctf_field_path_string(struct bt_ctf_field_path *path) +{ + GString *str = g_string_new(NULL); + size_t i; + + BT_ASSERT(path); + + if (!str) { + goto end; + } + + g_string_append_printf(str, "[%s", bt_common_scope_string( + bt_ctf_field_path_get_root_scope(path))); + + for (i = 0; i < bt_ctf_field_path_get_index_count(path); i++) { + int index = bt_ctf_field_path_get_index(path, i); + + g_string_append_printf(str, ", %d", index); + } + + g_string_append(str, "]"); + +end: + return str; +} + +#endif /* BABELTRACE_CTF_WRITER_UTILS_INTERNAL_H */ diff --git a/src/ctf-writer/validation.c b/src/ctf-writer/validation.c new file mode 100644 index 00000000..1d50d4d6 --- /dev/null +++ b/src/ctf-writer/validation.c @@ -0,0 +1,654 @@ +/* + * validation.c + * + * Babeltrace - CTF writer: Validation of trace, stream class, and event class + * + * Copyright 2016-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-VALIDATION" +#include "logging.h" + +#include + +#include "common/babeltrace.h" + +#include "assert-pre.h" +#include "event-class.h" +#include "field-types.h" +#include "field-types.h" +#include "resolve.h" +#include "stream-class.h" +#include "trace.h" +#include "validation.h" +#include "values.h" + +/* + * This function resolves and validates the field types of an event + * class. Only `event_context_type` and `event_payload_type` are + * resolved and validated; the other field types are used as eventual + * resolving targets. + * + * All parameters are owned by the caller. + */ +static +int validate_event_class_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type, + struct bt_ctf_field_type_common *event_context_type, + struct bt_ctf_field_type_common *event_payload_type) +{ + int ret = 0; + + BT_LOGV("Validating event class field types: " + "packet-header-ft-addr=%p, " + "packet-context-ft-addr=%p, " + "event-header-ft-addr=%p, " + "stream-event-context-ft-addr=%p, " + "event-context-ft-addr=%p, " + "event-payload-ft-addr=%p", + packet_header_type, packet_context_type, event_header_type, + stream_event_ctx_type, event_context_type, event_payload_type); + + /* Resolve sequence type lengths and variant type tags first */ + ret = bt_ctf_resolve_types(environment, packet_header_type, + packet_context_type, event_header_type, stream_event_ctx_type, + event_context_type, event_payload_type, + BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT | + BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD); + if (ret) { + BT_LOGW("Cannot resolve event class field types: ret=%d", + ret); + goto end; + } + + /* Validate field types individually */ + if (event_context_type) { + ret = bt_ctf_field_type_common_validate(event_context_type); + if (ret) { + BT_LOGW("Invalid event class's context field type: " + "ret=%d", ret); + goto end; + } + } + + if (event_payload_type) { + ret = bt_ctf_field_type_common_validate(event_payload_type); + if (ret) { + BT_LOGW("Invalid event class's payload field type: " + "ret=%d", ret); + goto end; + } + } + +end: + return ret; +} + +/* + * This function resolves and validates the field types of a stream + * class. Only `packet_context_type`, `event_header_type`, and + * `stream_event_ctx_type` are resolved and validated; the other field + * type is used as an eventual resolving target. + * + * All parameters are owned by the caller. + */ +static +int validate_stream_class_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type) +{ + int ret = 0; + + BT_LOGV("Validating stream class field types: " + "packet-header-ft-addr=%p, " + "packet-context-ft-addr=%p, " + "event-header-ft-addr=%p, " + "stream-event-context-ft-addr=%p", + packet_header_type, packet_context_type, event_header_type, + stream_event_ctx_type); + + /* Resolve sequence type lengths and variant type tags first */ + ret = bt_ctf_resolve_types(environment, packet_header_type, + packet_context_type, event_header_type, stream_event_ctx_type, + NULL, NULL, + BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT | + BT_CTF_RESOLVE_FLAG_EVENT_HEADER | + BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX); + if (ret) { + BT_LOGW("Cannot resolve stream class field types: ret=%d", + ret); + goto end; + } + + /* Validate field types individually */ + if (packet_context_type) { + ret = bt_ctf_field_type_common_validate(packet_context_type); + if (ret) { + BT_LOGW("Invalid stream class's packet context field type: " + "ret=%d", ret); + goto end; + } + } + + if (event_header_type) { + ret = bt_ctf_field_type_common_validate(event_header_type); + if (ret) { + BT_LOGW("Invalid stream class's event header field type: " + "ret=%d", ret); + goto end; + } + } + + if (stream_event_ctx_type) { + ret = bt_ctf_field_type_common_validate( + stream_event_ctx_type); + if (ret) { + BT_LOGW("Invalid stream class's event context field type: " + "ret=%d", ret); + goto end; + } + } + +end: + return ret; +} + +/* + * This function resolves and validates the field types of a trace. + * + * All parameters are owned by the caller. + */ +static +int validate_trace_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type) +{ + int ret = 0; + + BT_LOGV("Validating event class field types: " + "packet-header-ft-addr=%p", packet_header_type); + + /* Resolve sequence type lengths and variant type tags first */ + ret = bt_ctf_resolve_types(environment, packet_header_type, + NULL, NULL, NULL, NULL, NULL, + BT_CTF_RESOLVE_FLAG_PACKET_HEADER); + if (ret) { + BT_LOGW("Cannot resolve trace field types: ret=%d", + ret); + goto end; + } + + /* Validate field types individually */ + if (packet_header_type) { + ret = bt_ctf_field_type_common_validate(packet_header_type); + if (ret) { + BT_LOGW("Invalid trace's packet header field type: " + "ret=%d", ret); + goto end; + } + } + +end: + return ret; +} + +/* + * Checks whether or not `field_type` contains a variant or a sequence + * field type, recursively. Returns 1 if it's the case. + * + * `field_type` is owned by the caller. + */ +static +int field_type_contains_sequence_or_variant_ft(struct bt_ctf_field_type_common *type) +{ + int ret = 0; + enum bt_ctf_field_type_id type_id = bt_ctf_field_type_common_get_type_id(type); + + switch (type_id) { + case BT_CTF_FIELD_TYPE_ID_SEQUENCE: + case BT_CTF_FIELD_TYPE_ID_VARIANT: + ret = 1; + goto end; + case BT_CTF_FIELD_TYPE_ID_ARRAY: + case BT_CTF_FIELD_TYPE_ID_STRUCT: + { + int i; + int field_count = bt_ctf_field_type_common_get_field_count(type); + + if (field_count < 0) { + ret = -1; + goto end; + } + + for (i = 0; i < field_count; ++i) { + struct bt_ctf_field_type_common *child_type = + bt_ctf_field_type_common_borrow_field_at_index( + type, i); + + ret = field_type_contains_sequence_or_variant_ft( + child_type); + if (ret != 0) { + goto end; + } + } + break; + } + default: + break; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type, + struct bt_ctf_field_type_common *event_context_type, + struct bt_ctf_field_type_common *event_payload_type, + int trace_valid, int stream_class_valid, int event_class_valid, + struct bt_ctf_validation_output *output, + enum bt_ctf_validation_flag validate_flags, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func) +{ + int ret = 0; + int contains_seq_var; + int valid_ret; + + BT_LOGV("Validating field types: " + "packet-header-ft-addr=%p, " + "packet-context-ft-addr=%p, " + "event-header-ft-addr=%p, " + "stream-event-context-ft-addr=%p, " + "event-context-ft-addr=%p, " + "event-payload-ft-addr=%p, " + "trace-is-valid=%d, stream-class-is-valid=%d, " + "event-class-is-valid=%d, validation-flags=%x", + packet_header_type, packet_context_type, event_header_type, + stream_event_ctx_type, event_context_type, event_payload_type, + trace_valid, stream_class_valid, event_class_valid, + (unsigned int) validate_flags); + + /* Clean output values */ + memset(output, 0, sizeof(*output)); + + /* Set initial valid flags according to valid parameters */ + if (trace_valid) { + output->valid_flags |= BT_CTF_VALIDATION_FLAG_TRACE; + } + + if (stream_class_valid) { + output->valid_flags |= BT_CTF_VALIDATION_FLAG_STREAM; + } + + if (event_class_valid) { + output->valid_flags |= BT_CTF_VALIDATION_FLAG_EVENT; + } + + /* Own the type parameters */ + bt_ctf_object_get_ref(packet_header_type); + bt_ctf_object_get_ref(packet_context_type); + bt_ctf_object_get_ref(event_header_type); + bt_ctf_object_get_ref(stream_event_ctx_type); + bt_ctf_object_get_ref(event_context_type); + bt_ctf_object_get_ref(event_payload_type); + + /* Validate trace */ + if ((validate_flags & BT_CTF_VALIDATION_FLAG_TRACE) && !trace_valid) { + struct bt_ctf_field_type_common *packet_header_type_copy = NULL; + + /* Create field type copies */ + if (packet_header_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + packet_header_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + packet_header_type_copy = packet_header_type; + bt_ctf_object_get_ref(packet_header_type_copy); + goto skip_packet_header_type_copy; + } + + BT_LOGV_STR("Copying packet header field type because it contains at least one sequence or variant field type."); + packet_header_type_copy = + copy_field_type_func(packet_header_type); + if (!packet_header_type_copy) { + ret = -1; + BT_LOGE_STR("Cannot copy packet header field type."); + goto error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(packet_header_type_copy); + } + +skip_packet_header_type_copy: + /* Put original reference and move copy */ + BT_CTF_OBJECT_MOVE_REF(packet_header_type, packet_header_type_copy); + + /* Validate trace field types */ + valid_ret = validate_trace_types(environment, + packet_header_type); + if (valid_ret == 0) { + /* Trace is valid */ + output->valid_flags |= BT_CTF_VALIDATION_FLAG_TRACE; + } + } + + /* Validate stream class */ + if ((validate_flags & BT_CTF_VALIDATION_FLAG_STREAM) && + !stream_class_valid) { + struct bt_ctf_field_type_common *packet_context_type_copy = NULL; + struct bt_ctf_field_type_common *event_header_type_copy = NULL; + struct bt_ctf_field_type_common *stream_event_ctx_type_copy = NULL; + + if (packet_context_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + packet_context_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + packet_context_type_copy = packet_context_type; + bt_ctf_object_get_ref(packet_context_type_copy); + goto skip_packet_context_type_copy; + } + + BT_LOGV_STR("Copying packet context field type because it contains at least one sequence or variant field type."); + packet_context_type_copy = + copy_field_type_func(packet_context_type); + if (!packet_context_type_copy) { + BT_LOGE_STR("Cannot copy packet context field type."); + goto sc_validation_error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(packet_context_type_copy); + } + +skip_packet_context_type_copy: + if (event_header_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + event_header_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + event_header_type_copy = event_header_type; + bt_ctf_object_get_ref(event_header_type_copy); + goto skip_event_header_type_copy; + } + + BT_LOGV_STR("Copying event header field type because it contains at least one sequence or variant field type."); + event_header_type_copy = + copy_field_type_func(event_header_type); + if (!event_header_type_copy) { + BT_LOGE_STR("Cannot copy event header field type."); + goto sc_validation_error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(event_header_type_copy); + } + +skip_event_header_type_copy: + if (stream_event_ctx_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + stream_event_ctx_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + stream_event_ctx_type_copy = + stream_event_ctx_type; + bt_ctf_object_get_ref(stream_event_ctx_type_copy); + goto skip_stream_event_ctx_type_copy; + } + + BT_LOGV_STR("Copying stream event context field type because it contains at least one sequence or variant field type."); + stream_event_ctx_type_copy = + copy_field_type_func(stream_event_ctx_type); + if (!stream_event_ctx_type_copy) { + BT_LOGE_STR("Cannot copy stream event context field type."); + goto sc_validation_error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(stream_event_ctx_type_copy); + } + +skip_stream_event_ctx_type_copy: + /* Put original references and move copies */ + BT_CTF_OBJECT_MOVE_REF(packet_context_type, packet_context_type_copy); + BT_CTF_OBJECT_MOVE_REF(event_header_type, event_header_type_copy); + BT_CTF_OBJECT_MOVE_REF(stream_event_ctx_type, stream_event_ctx_type_copy); + + /* Validate stream class field types */ + valid_ret = validate_stream_class_types(environment, + packet_header_type, packet_context_type, + event_header_type, stream_event_ctx_type); + if (valid_ret == 0) { + /* Stream class is valid */ + output->valid_flags |= BT_CTF_VALIDATION_FLAG_STREAM; + } + + goto sc_validation_done; + +sc_validation_error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_header_type_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_type_copy); + ret = -1; + goto error; + } + +sc_validation_done: + /* Validate event class */ + if ((validate_flags & BT_CTF_VALIDATION_FLAG_EVENT) && + !event_class_valid) { + struct bt_ctf_field_type_common *event_context_type_copy = NULL; + struct bt_ctf_field_type_common *event_payload_type_copy = NULL; + + if (event_context_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + event_context_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + event_context_type_copy = event_context_type; + bt_ctf_object_get_ref(event_context_type_copy); + goto skip_event_context_type_copy; + } + + BT_LOGV_STR("Copying event context field type because it contains at least one sequence or variant field type."); + event_context_type_copy = + copy_field_type_func(event_context_type); + if (!event_context_type_copy) { + BT_LOGE_STR("Cannot copy event context field type."); + goto ec_validation_error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(event_context_type_copy); + } + +skip_event_context_type_copy: + if (event_payload_type) { + contains_seq_var = + field_type_contains_sequence_or_variant_ft( + event_payload_type); + if (contains_seq_var < 0) { + ret = contains_seq_var; + goto error; + } else if (!contains_seq_var) { + /* No copy is needed */ + event_payload_type_copy = event_payload_type; + bt_ctf_object_get_ref(event_payload_type_copy); + goto skip_event_payload_type_copy; + } + + BT_LOGV_STR("Copying event payload field type because it contains at least one sequence or variant field type."); + event_payload_type_copy = + copy_field_type_func(event_payload_type); + if (!event_payload_type_copy) { + BT_LOGE_STR("Cannot copy event payload field type."); + goto ec_validation_error; + } + + /* + * Freeze this copy: if it's returned to the + * caller, it cannot be modified any way since + * it will be resolved. + */ + bt_ctf_field_type_common_freeze(event_payload_type_copy); + } + +skip_event_payload_type_copy: + /* Put original references and move copies */ + BT_CTF_OBJECT_MOVE_REF(event_context_type, event_context_type_copy); + BT_CTF_OBJECT_MOVE_REF(event_payload_type, event_payload_type_copy); + + /* Validate event class field types */ + valid_ret = validate_event_class_types(environment, + packet_header_type, packet_context_type, + event_header_type, stream_event_ctx_type, + event_context_type, event_payload_type); + if (valid_ret == 0) { + /* Event class is valid */ + output->valid_flags |= BT_CTF_VALIDATION_FLAG_EVENT; + } + + goto ec_validation_done; + +ec_validation_error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type_copy); + ret = -1; + goto error; + } + +ec_validation_done: + /* + * Validation is complete. Move the field types that were used + * to validate (and that were possibly altered by the validation + * process) to the output values. + */ + BT_CTF_OBJECT_MOVE_REF(output->packet_header_type, packet_header_type); + BT_CTF_OBJECT_MOVE_REF(output->packet_context_type, packet_context_type); + BT_CTF_OBJECT_MOVE_REF(output->event_header_type, event_header_type); + BT_CTF_OBJECT_MOVE_REF(output->stream_event_ctx_type, stream_event_ctx_type); + BT_CTF_OBJECT_MOVE_REF(output->event_context_type, event_context_type); + BT_CTF_OBJECT_MOVE_REF(output->event_payload_type, event_payload_type); + return ret; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_header_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(packet_context_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_header_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(stream_event_ctx_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_context_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(event_payload_type); + return ret; +} + +BT_HIDDEN +void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_validation_output *output, + enum bt_ctf_validation_flag replace_flags) +{ + if ((replace_flags & BT_CTF_VALIDATION_FLAG_TRACE) && trace) { + bt_ctf_field_type_common_freeze(trace->packet_header_field_type); + BT_CTF_OBJECT_MOVE_REF(trace->packet_header_field_type, + output->packet_header_type); + } + + if ((replace_flags & BT_CTF_VALIDATION_FLAG_STREAM) && stream_class) { + bt_ctf_field_type_common_freeze(stream_class->packet_context_field_type); + bt_ctf_field_type_common_freeze(stream_class->event_header_field_type); + bt_ctf_field_type_common_freeze(stream_class->event_context_field_type); + BT_CTF_OBJECT_MOVE_REF(stream_class->packet_context_field_type, + output->packet_context_type); + BT_CTF_OBJECT_MOVE_REF(stream_class->event_header_field_type, + output->event_header_type); + BT_CTF_OBJECT_MOVE_REF(stream_class->event_context_field_type, + output->stream_event_ctx_type); + } + + if ((replace_flags & BT_CTF_VALIDATION_FLAG_EVENT) && event_class) { + bt_ctf_field_type_common_freeze(event_class->context_field_type); + bt_ctf_field_type_common_freeze(event_class->payload_field_type); + BT_CTF_OBJECT_MOVE_REF(event_class->context_field_type, output->event_context_type); + BT_CTF_OBJECT_MOVE_REF(event_class->payload_field_type, output->event_payload_type); + } +} + +BT_HIDDEN +void bt_ctf_validation_output_put_types( + struct bt_ctf_validation_output *output) +{ + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->packet_header_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->packet_context_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_header_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->stream_event_ctx_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_context_type); + BT_CTF_OBJECT_PUT_REF_AND_RESET(output->event_payload_type); +} diff --git a/src/ctf-writer/validation.h b/src/ctf-writer/validation.h new file mode 100644 index 00000000..18e6ff7e --- /dev/null +++ b/src/ctf-writer/validation.h @@ -0,0 +1,128 @@ +#ifndef BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H +#define BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H + +/* + * Copyright 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" + +#include "values.h" + +struct bt_ctf_trace_common; +struct bt_ctf_stream_class_common; +struct bt_ctf_event_class_common; +struct bt_ctf_field_type_common; + +typedef struct bt_ctf_field_type_common *(*bt_ctf_validation_flag_copy_field_type_func)( + struct bt_ctf_field_type_common *); + +enum bt_ctf_validation_flag { + BT_CTF_VALIDATION_FLAG_TRACE = 1, + BT_CTF_VALIDATION_FLAG_STREAM = 2, + BT_CTF_VALIDATION_FLAG_EVENT = 4, +}; + +/* + * Validation output structure. + * + * This is where the results of the validation function go. The field + * types are the validated ones which should replace the original field + * types of a trace, a stream class, and an event class. + * + * `valid_flags` contains the results of the validation. + */ +struct bt_ctf_validation_output { + struct bt_ctf_field_type_common *packet_header_type; + struct bt_ctf_field_type_common *packet_context_type; + struct bt_ctf_field_type_common *event_header_type; + struct bt_ctf_field_type_common *stream_event_ctx_type; + struct bt_ctf_field_type_common *event_context_type; + struct bt_ctf_field_type_common *event_payload_type; + enum bt_ctf_validation_flag valid_flags; +}; + +/* + * This function resolves and validates the field types of an event + * class, a stream class, and a trace. Copies are created if needed + * and the resulting field types to use are placed in the `output` + * validation structure, which also contains the results of the + * validation. Copies can replace the original field types of a trace, + * a stream class, and an event class using + * bt_ctf_validation_replace_types(). + * + * The current known validity of the field types of the trace, + * stream class, and event class must be indicated with the + * `trace_valid`, `stream_class_valid`, and `event_class_valid` + * parameters. If a class is valid, its field types are not copied, + * validated, or resolved during this call. + * + * The validation flags `validate_flags` indicate which classes should + * have their field types validated. + * + * All parameters are owned by the caller. + */ +BT_HIDDEN +int bt_ctf_validate_class_types(struct bt_ctf_private_value *environment, + struct bt_ctf_field_type_common *packet_header_type, + struct bt_ctf_field_type_common *packet_context_type, + struct bt_ctf_field_type_common *event_header_type, + struct bt_ctf_field_type_common *stream_event_ctx_type, + struct bt_ctf_field_type_common *event_context_type, + struct bt_ctf_field_type_common *event_payload_type, + int trace_valid, int stream_class_valid, int event_class_valid, + struct bt_ctf_validation_output *output, + enum bt_ctf_validation_flag validate_flags, + bt_ctf_validation_flag_copy_field_type_func copy_field_type_func); + +/* + * This function replaces the actual field types of a trace, a stream + * class, and an event class with the appropriate field types contained + * in a validation output structure. + * + * The replace flags `replace_flags` indicate which classes should have + * their field types replaced. + * + * Note that the field types that are not used in the validation output + * structure are still owned by it at the end of this call. + * bt_ctf_validation_output_put_types() should be called to clean the + * structure. + * + * All parameters are owned by the caller. + */ +BT_HIDDEN +void bt_ctf_validation_replace_types(struct bt_ctf_trace_common *trace, + struct bt_ctf_stream_class_common *stream_class, + struct bt_ctf_event_class_common *event_class, + struct bt_ctf_validation_output *output, + enum bt_ctf_validation_flag replace_flags); + +/* + * This function puts all the field types contained in a given + * validation output structure. + * + * `output` is owned by the caller and is not freed here. + */ +BT_HIDDEN +void bt_ctf_validation_output_put_types( + struct bt_ctf_validation_output *output); + +#endif /* BABELTRACE_CTF_WRITER_VALIDATION_INTERNAL_H */ diff --git a/src/ctf-writer/values.c b/src/ctf-writer/values.c new file mode 100644 index 00000000..fb5ffa67 --- /dev/null +++ b/src/ctf-writer/values.c @@ -0,0 +1,1345 @@ +/* + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER-VALUES" +#include "logging.h" + +#include +#include +#include + +#include +#include + +#include "common/assert.h" +#include "common/common.h" +#include "compat/compiler.h" +#include "compat/glib.h" + +#include "assert-pre.h" +#include "object.h" +#include "values.h" + +#define BT_CTF_VALUE_FROM_CONCRETE(_concrete) ((struct bt_ctf_value *) (_concrete)) +#define BT_CTF_VALUE_TO_BOOL(_base) ((struct bt_ctf_value_bool *) (_base)) +#define BT_CTF_VALUE_TO_INTEGER(_base) ((struct bt_ctf_value_integer *) (_base)) +#define BT_CTF_VALUE_TO_REAL(_base) ((struct bt_ctf_value_real *) (_base)) +#define BT_CTF_VALUE_TO_STRING(_base) ((struct bt_ctf_value_string *) (_base)) +#define BT_CTF_VALUE_TO_ARRAY(_base) ((struct bt_ctf_value_array *) (_base)) +#define BT_CTF_VALUE_TO_MAP(_base) ((struct bt_ctf_value_map *) (_base)) + +#define BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(_value, _type) \ + BT_CTF_ASSERT_PRE(((struct bt_ctf_value *) (_value))->type == (_type), \ + "Value has the wrong type ID: expected-type=%d", (_type)) + +#define BT_CTF_ASSERT_PRE_VALUE_HOT(_value, _name) \ + BT_CTF_ASSERT_PRE_HOT(((struct bt_ctf_value *) (_value)), (_name), "") + +#define BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(_index, _count) \ + BT_CTF_ASSERT_PRE((_index) < (_count), \ + "Index is out of bound: " \ + "index=%" PRIu64 ", count=%u", (_index), (_count)); + +struct bt_ctf_value { + struct bt_ctf_object base; + enum bt_ctf_value_type type; + bt_bool frozen; +}; + +static +void bt_ctf_value_null_instance_release_func(struct bt_ctf_object *obj) +{ + BT_LOGW("Releasing the null value singleton: addr=%p", obj); +} + +static +struct bt_ctf_value bt_ctf_value_null_instance = { + .base = { + .is_shared = true, + .ref_count = 1, + .release_func = bt_ctf_value_null_instance_release_func, + .spec_release_func = NULL, + .parent_is_owner_listener_func = NULL, + .parent = NULL, + }, + .type = BT_CTF_VALUE_TYPE_NULL, + .frozen = BT_TRUE, +}; + +struct bt_ctf_value *const bt_ctf_value_null = &bt_ctf_value_null_instance; +struct bt_ctf_private_value *const bt_ctf_private_value_null = + (void *) &bt_ctf_value_null_instance; + +struct bt_ctf_value_bool { + struct bt_ctf_value base; + bt_bool value; +}; + +struct bt_ctf_value_integer { + struct bt_ctf_value base; + int64_t value; +}; + +struct bt_ctf_value_real { + struct bt_ctf_value base; + double value; +}; + +struct bt_ctf_value_string { + struct bt_ctf_value base; + GString *gstr; +}; + +struct bt_ctf_value_array { + struct bt_ctf_value base; + GPtrArray *garray; +}; + +struct bt_ctf_value_map { + struct bt_ctf_value base; + GHashTable *ght; +}; + +static +void bt_ctf_value_destroy(struct bt_ctf_object *obj); + +static +void bt_ctf_value_string_destroy(struct bt_ctf_value *object) +{ + g_string_free(BT_CTF_VALUE_TO_STRING(object)->gstr, TRUE); + BT_CTF_VALUE_TO_STRING(object)->gstr = NULL; +} + +static +void bt_ctf_value_array_destroy(struct bt_ctf_value *object) +{ + /* + * Pointer array's registered value destructor will take care + * of putting each contained object. + */ + g_ptr_array_free(BT_CTF_VALUE_TO_ARRAY(object)->garray, TRUE); + BT_CTF_VALUE_TO_ARRAY(object)->garray = NULL; +} + +static +void bt_ctf_value_map_destroy(struct bt_ctf_value *object) +{ + /* + * Hash table's registered value destructor will take care of + * putting each contained object. Keys are GQuarks and cannot + * be destroyed anyway. + */ + g_hash_table_destroy(BT_CTF_VALUE_TO_MAP(object)->ght); + BT_CTF_VALUE_TO_MAP(object)->ght = NULL; +} + +static +void (* const destroy_funcs[])(struct bt_ctf_value *) = { + [BT_CTF_VALUE_TYPE_NULL] = NULL, + [BT_CTF_VALUE_TYPE_BOOL] = NULL, + [BT_CTF_VALUE_TYPE_INTEGER] = NULL, + [BT_CTF_VALUE_TYPE_REAL] = NULL, + [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_destroy, + [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_destroy, + [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_destroy, +}; + +static +struct bt_ctf_private_value *bt_ctf_value_null_copy(const struct bt_ctf_value *null_obj) +{ + return (void *) bt_ctf_value_null; +} + +static +struct bt_ctf_private_value *bt_ctf_value_bool_copy(const struct bt_ctf_value *bool_obj) +{ + return bt_ctf_private_value_bool_create_init( + BT_CTF_VALUE_TO_BOOL(bool_obj)->value); +} + +static +struct bt_ctf_private_value *bt_ctf_value_integer_copy( + const struct bt_ctf_value *integer_obj) +{ + return bt_ctf_private_value_integer_create_init( + BT_CTF_VALUE_TO_INTEGER(integer_obj)->value); +} + +static +struct bt_ctf_private_value *bt_ctf_value_real_copy(const struct bt_ctf_value *real_obj) +{ + return bt_ctf_private_value_real_create_init( + BT_CTF_VALUE_TO_REAL(real_obj)->value); +} + +static +struct bt_ctf_private_value *bt_ctf_value_string_copy(const struct bt_ctf_value *string_obj) +{ + return bt_ctf_private_value_string_create_init( + BT_CTF_VALUE_TO_STRING(string_obj)->gstr->str); +} + +static +struct bt_ctf_private_value *bt_ctf_value_array_copy(const struct bt_ctf_value *array_obj) +{ + int i; + int ret; + struct bt_ctf_private_value *copy_obj; + struct bt_ctf_value_array *typed_array_obj; + + BT_LOGD("Copying array value: addr=%p", array_obj); + typed_array_obj = BT_CTF_VALUE_TO_ARRAY(array_obj); + copy_obj = bt_ctf_private_value_array_create(); + if (!copy_obj) { + BT_LOGE_STR("Cannot create empty array value."); + goto end; + } + + for (i = 0; i < typed_array_obj->garray->len; ++i) { + struct bt_ctf_private_value *element_obj_copy = NULL; + struct bt_ctf_value *element_obj = + bt_ctf_value_array_borrow_element_by_index( + array_obj, i); + + BT_ASSERT(element_obj); + BT_LOGD("Copying array value's element: element-addr=%p, " + "index=%d", element_obj, i); + ret = bt_ctf_value_copy(&element_obj_copy, element_obj); + if (ret) { + BT_LOGE("Cannot copy array value's element: " + "array-addr=%p, index=%d", + array_obj, i); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + + BT_ASSERT(element_obj_copy); + ret = bt_ctf_private_value_array_append_element(copy_obj, + (void *) element_obj_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(element_obj_copy); + if (ret) { + BT_LOGE("Cannot append to array value: addr=%p", + array_obj); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + } + + BT_LOGD("Copied array value: original-addr=%p, copy-addr=%p", + array_obj, copy_obj); + +end: + return copy_obj; +} + +static +struct bt_ctf_private_value *bt_ctf_value_map_copy(const struct bt_ctf_value *map_obj) +{ + int ret; + GHashTableIter iter; + gpointer key, element_obj; + struct bt_ctf_private_value *copy_obj; + struct bt_ctf_private_value *element_obj_copy = NULL; + struct bt_ctf_value_map *typed_map_obj; + + BT_LOGD("Copying map value: addr=%p", map_obj); + typed_map_obj = BT_CTF_VALUE_TO_MAP(map_obj); + copy_obj = bt_ctf_private_value_map_create(); + if (!copy_obj) { + goto end; + } + + g_hash_table_iter_init(&iter, typed_map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + BT_ASSERT(key_str); + BT_LOGD("Copying map value's element: element-addr=%p, " + "key=\"%s\"", element_obj, key_str); + ret = bt_ctf_value_copy(&element_obj_copy, element_obj); + if (ret) { + BT_LOGE("Cannot copy map value's element: " + "map-addr=%p, key=\"%s\"", + map_obj, key_str); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + + BT_ASSERT(element_obj_copy); + ret = bt_ctf_private_value_map_insert_entry(copy_obj, key_str, + (void *) element_obj_copy); + BT_CTF_OBJECT_PUT_REF_AND_RESET(element_obj_copy); + if (ret) { + BT_LOGE("Cannot insert into map value: addr=%p, key=\"%s\"", + map_obj, key_str); + BT_CTF_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + } + + BT_LOGD("Copied map value: addr=%p", map_obj); + +end: + return copy_obj; +} + +static +struct bt_ctf_private_value *(* const copy_funcs[])(const struct bt_ctf_value *) = { + [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_copy, + [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_bool_copy, + [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_integer_copy, + [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_real_copy, + [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_copy, + [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_copy, + [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_copy, +}; + +static +bt_bool bt_ctf_value_null_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + /* + * Always BT_TRUE since bt_ctf_value_compare() already checks if both + * object_a and object_b have the same type, and in the case of + * null value objects, they're always the same if it is so. + */ + return BT_TRUE; +} + +static +bt_bool bt_ctf_value_bool_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + if (BT_CTF_VALUE_TO_BOOL(object_a)->value != + BT_CTF_VALUE_TO_BOOL(object_b)->value) { + BT_LOGV("Boolean value objects are different: " + "bool-a-val=%d, bool-b-val=%d", + BT_CTF_VALUE_TO_BOOL(object_a)->value, + BT_CTF_VALUE_TO_BOOL(object_b)->value); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_ctf_value_integer_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + if (BT_CTF_VALUE_TO_INTEGER(object_a)->value != + BT_CTF_VALUE_TO_INTEGER(object_b)->value) { + BT_LOGV("Integer value objects are different: " + "int-a-val=%" PRId64 ", int-b-val=%" PRId64, + BT_CTF_VALUE_TO_INTEGER(object_a)->value, + BT_CTF_VALUE_TO_INTEGER(object_b)->value); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_ctf_value_real_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + if (BT_CTF_VALUE_TO_REAL(object_a)->value != + BT_CTF_VALUE_TO_REAL(object_b)->value) { + BT_LOGV("Real number value objects are different: " + "real-a-val=%f, real-b-val=%f", + BT_CTF_VALUE_TO_REAL(object_a)->value, + BT_CTF_VALUE_TO_REAL(object_b)->value); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_ctf_value_string_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + if (strcmp(BT_CTF_VALUE_TO_STRING(object_a)->gstr->str, + BT_CTF_VALUE_TO_STRING(object_b)->gstr->str) != 0) { + BT_LOGV("String value objects are different: " + "string-a-val=\"%s\", string-b-val=\"%s\"", + BT_CTF_VALUE_TO_STRING(object_a)->gstr->str, + BT_CTF_VALUE_TO_STRING(object_b)->gstr->str); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_ctf_value_array_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + int i; + bt_bool ret = BT_TRUE; + const struct bt_ctf_value_array *array_obj_a = + BT_CTF_VALUE_TO_ARRAY(object_a); + + if (bt_ctf_value_array_get_size(object_a) != + bt_ctf_value_array_get_size(object_b)) { + BT_LOGV("Array values are different: size mismatch " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-size=%" PRId64 ", value-b-size=%" PRId64, + object_a, object_b, + bt_ctf_value_array_get_size(object_a), + bt_ctf_value_array_get_size(object_b)); + ret = BT_FALSE; + goto end; + } + + for (i = 0; i < array_obj_a->garray->len; ++i) { + struct bt_ctf_value *element_obj_a; + struct bt_ctf_value *element_obj_b; + + element_obj_a = bt_ctf_value_array_borrow_element_by_index( + object_a, i); + element_obj_b = bt_ctf_value_array_borrow_element_by_index( + object_b, i); + + if (!bt_ctf_value_compare(element_obj_a, element_obj_b)) { + BT_LOGV("Array values's elements are different: " + "value-a-addr=%p, value-b-addr=%p, index=%d", + element_obj_a, element_obj_b, i); + ret = BT_FALSE; + goto end; + } + } + +end: + return ret; +} + +static +bt_bool bt_ctf_value_map_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + bt_bool ret = BT_TRUE; + GHashTableIter iter; + gpointer key, element_obj_a; + const struct bt_ctf_value_map *map_obj_a = BT_CTF_VALUE_TO_MAP(object_a); + + if (bt_ctf_value_map_get_size(object_a) != + bt_ctf_value_map_get_size(object_b)) { + BT_LOGV("Map values are different: size mismatch " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-size=%" PRId64 ", value-b-size=%" PRId64, + object_a, object_b, + bt_ctf_value_map_get_size(object_a), + bt_ctf_value_map_get_size(object_b)); + ret = BT_FALSE; + goto end; + } + + g_hash_table_iter_init(&iter, map_obj_a->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj_a)) { + struct bt_ctf_value *element_obj_b; + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + element_obj_b = bt_ctf_value_map_borrow_entry_value(object_b, + key_str); + + if (!bt_ctf_value_compare(element_obj_a, element_obj_b)) { + BT_LOGV("Map values's elements are different: " + "value-a-addr=%p, value-b-addr=%p, key=\"%s\"", + element_obj_a, element_obj_b, key_str); + ret = BT_FALSE; + goto end; + } + } + +end: + return ret; +} + +static +bt_bool (* const compare_funcs[])(const struct bt_ctf_value *, + const struct bt_ctf_value *) = { + [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_compare, + [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_bool_compare, + [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_integer_compare, + [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_real_compare, + [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_string_compare, + [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_compare, + [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_compare, +}; + +static +void bt_ctf_value_null_freeze(struct bt_ctf_value *object) +{ +} + +static +void bt_ctf_value_generic_freeze(struct bt_ctf_value *object) +{ + object->frozen = BT_TRUE; +} + +static +void bt_ctf_value_array_freeze(struct bt_ctf_value *object) +{ + int i; + struct bt_ctf_value_array *typed_array_obj = + BT_CTF_VALUE_TO_ARRAY(object); + + for (i = 0; i < typed_array_obj->garray->len; ++i) { + bt_ctf_value_freeze(g_ptr_array_index(typed_array_obj->garray, i)); + } + + bt_ctf_value_generic_freeze(object); +} + +static +void bt_ctf_value_map_freeze(struct bt_ctf_value *object) +{ + GHashTableIter iter; + gpointer key, element_obj; + const struct bt_ctf_value_map *map_obj = BT_CTF_VALUE_TO_MAP(object); + + g_hash_table_iter_init(&iter, map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + bt_ctf_value_freeze(element_obj); + } + + bt_ctf_value_generic_freeze(object); +} + +static +void (* const freeze_funcs[])(struct bt_ctf_value *) = { + [BT_CTF_VALUE_TYPE_NULL] = bt_ctf_value_null_freeze, + [BT_CTF_VALUE_TYPE_BOOL] = bt_ctf_value_generic_freeze, + [BT_CTF_VALUE_TYPE_INTEGER] = bt_ctf_value_generic_freeze, + [BT_CTF_VALUE_TYPE_REAL] = bt_ctf_value_generic_freeze, + [BT_CTF_VALUE_TYPE_STRING] = bt_ctf_value_generic_freeze, + [BT_CTF_VALUE_TYPE_ARRAY] = bt_ctf_value_array_freeze, + [BT_CTF_VALUE_TYPE_MAP] = bt_ctf_value_map_freeze, +}; + +static +void bt_ctf_value_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_value *value; + + value = container_of(obj, struct bt_ctf_value, base); + BT_LOGD("Destroying value: addr=%p", value); + + if (bt_ctf_value_is_null(value)) { + BT_LOGD_STR("Not destroying the null value singleton."); + return; + } + + if (destroy_funcs[value->type]) { + destroy_funcs[value->type](value); + } + + g_free(value); +} + +BT_HIDDEN +enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object) +{ + enum bt_ctf_value_status ret = BT_CTF_VALUE_STATUS_OK; + + BT_ASSERT(object); + + if (object->frozen) { + goto end; + } + + BT_LOGD("Freezing value: addr=%p", object); + freeze_funcs[object->type](object); + +end: + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object) +{ + BT_CTF_ASSERT_PRE_NON_NULL(object, "Value object"); + return object->type; +} + +static +struct bt_ctf_value bt_ctf_value_create_base(enum bt_ctf_value_type type) +{ + struct bt_ctf_value value; + + value.type = type; + value.frozen = BT_FALSE; + bt_ctf_object_init_shared(&value.base, bt_ctf_value_destroy); + return value; +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_bool val) +{ + struct bt_ctf_value_bool *bool_obj; + + BT_LOGD("Creating boolean value object: val=%d", val); + bool_obj = g_new0(struct bt_ctf_value_bool, 1); + if (!bool_obj) { + BT_LOGE_STR("Failed to allocate one boolean value object."); + goto end; + } + + bool_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_BOOL); + bool_obj->value = val; + BT_LOGD("Created boolean value object: addr=%p", bool_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(bool_obj); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void) +{ + return bt_ctf_private_value_bool_create_init(BT_FALSE); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init(int64_t val) +{ + struct bt_ctf_value_integer *integer_obj; + + BT_LOGD("Creating integer value object: val=%" PRId64, val); + integer_obj = g_new0(struct bt_ctf_value_integer, 1); + if (!integer_obj) { + BT_LOGE_STR("Failed to allocate one integer value object."); + goto end; + } + + integer_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_INTEGER); + integer_obj->value = val; + BT_LOGD("Created integer value object: addr=%p", + integer_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(integer_obj); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void) +{ + return bt_ctf_private_value_integer_create_init(0); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val) +{ + struct bt_ctf_value_real *real_obj; + + BT_LOGD("Creating real number value object: val=%f", val); + real_obj = g_new0(struct bt_ctf_value_real, 1); + if (!real_obj) { + BT_LOGE_STR("Failed to allocate one real number value object."); + goto end; + } + + real_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_REAL); + real_obj->value = val; + BT_LOGD("Created real number value object: addr=%p", + real_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(real_obj); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_real_create(void) +{ + return bt_ctf_private_value_real_create_init(0.); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_string_create_init(const char *val) +{ + struct bt_ctf_value_string *string_obj = NULL; + + if (!val) { + BT_LOGW_STR("Invalid parameter: value is NULL."); + goto end; + } + + BT_LOGD("Creating string value object: val-len=%zu", strlen(val)); + string_obj = g_new0(struct bt_ctf_value_string, 1); + if (!string_obj) { + BT_LOGE_STR("Failed to allocate one string object."); + goto end; + } + + string_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_STRING); + string_obj->gstr = g_string_new(val); + if (!string_obj->gstr) { + BT_LOGE_STR("Failed to allocate a GString."); + g_free(string_obj); + string_obj = NULL; + goto end; + } + + BT_LOGD("Created string value object: addr=%p", + string_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(string_obj); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_string_create(void) +{ + return bt_ctf_private_value_string_create_init(""); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_array_create(void) +{ + struct bt_ctf_value_array *array_obj; + + BT_LOGD_STR("Creating empty array value object."); + array_obj = g_new0(struct bt_ctf_value_array, 1); + if (!array_obj) { + BT_LOGE_STR("Failed to allocate one array object."); + goto end; + } + + array_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_ARRAY); + array_obj->garray = bt_g_ptr_array_new_full(0, + (GDestroyNotify) bt_ctf_object_put_ref); + if (!array_obj->garray) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + g_free(array_obj); + array_obj = NULL; + goto end; + } + + BT_LOGD("Created array value object: addr=%p", + array_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(array_obj); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_map_create(void) +{ + struct bt_ctf_value_map *map_obj; + + BT_LOGD_STR("Creating empty map value object."); + map_obj = g_new0(struct bt_ctf_value_map, 1); + if (!map_obj) { + BT_LOGE_STR("Failed to allocate one map object."); + goto end; + } + + map_obj->base = bt_ctf_value_create_base(BT_CTF_VALUE_TYPE_MAP); + map_obj->ght = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) bt_ctf_object_put_ref); + if (!map_obj->ght) { + BT_LOGE_STR("Failed to allocate a GHashTable."); + g_free(map_obj); + map_obj = NULL; + goto end; + } + + BT_LOGD("Created map value object: addr=%p", + map_obj); + +end: + return (void *) BT_CTF_VALUE_FROM_CONCRETE(map_obj); +} + +BT_HIDDEN +bt_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_CTF_VALUE_TYPE_BOOL); + return BT_CTF_VALUE_TO_BOOL(bool_obj)->value; +} + +BT_HIDDEN +void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, bt_bool val) +{ + BT_CTF_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_CTF_VALUE_TYPE_BOOL); + BT_CTF_ASSERT_PRE_VALUE_HOT(bool_obj, "Value object"); + BT_CTF_VALUE_TO_BOOL(bool_obj)->value = val; + BT_LOGV("Set boolean value's raw value: value-addr=%p, value=%d", + bool_obj, val); +} + +BT_HIDDEN +int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, BT_CTF_VALUE_TYPE_INTEGER); + return BT_CTF_VALUE_TO_INTEGER(integer_obj)->value; +} + +BT_HIDDEN +void bt_ctf_private_value_integer_set(struct bt_ctf_private_value *integer_obj, + int64_t val) +{ + BT_CTF_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, BT_CTF_VALUE_TYPE_INTEGER); + BT_CTF_ASSERT_PRE_VALUE_HOT(integer_obj, "Value object"); + BT_CTF_VALUE_TO_INTEGER(integer_obj)->value = val; + BT_LOGV("Set integer value's raw value: value-addr=%p, value=%" PRId64, + integer_obj, val); +} + +BT_HIDDEN +double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_CTF_VALUE_TYPE_REAL); + return BT_CTF_VALUE_TO_REAL(real_obj)->value; +} + +BT_HIDDEN +void bt_ctf_private_value_real_set(struct bt_ctf_private_value *real_obj, double val) +{ + BT_CTF_ASSERT_PRE_NON_NULL(real_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_CTF_VALUE_TYPE_REAL); + BT_CTF_ASSERT_PRE_VALUE_HOT(real_obj, "Value object"); + BT_CTF_VALUE_TO_REAL(real_obj)->value = val; + BT_LOGV("Set real number value's raw value: value-addr=%p, value=%f", + real_obj, val); +} + +BT_HIDDEN +const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(string_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_CTF_VALUE_TYPE_STRING); + return BT_CTF_VALUE_TO_STRING(string_obj)->gstr->str; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_string_set( + struct bt_ctf_private_value *string_obj, const char *val) +{ + BT_CTF_ASSERT_PRE_NON_NULL(string_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_CTF_VALUE_TYPE_STRING); + BT_CTF_ASSERT_PRE_VALUE_HOT(string_obj, "Value object"); + g_string_assign(BT_CTF_VALUE_TO_STRING(string_obj)->gstr, val); + BT_LOGV("Set string value's raw value: value-addr=%p, raw-value-addr=%p", + string_obj, val); + return BT_CTF_VALUE_STATUS_OK; +} + +BT_HIDDEN +uint64_t bt_ctf_value_array_get_size(const struct bt_ctf_value *array_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); + return (uint64_t) BT_CTF_VALUE_TO_ARRAY(array_obj)->garray->len; +} + +BT_HIDDEN +struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index( + const struct bt_ctf_value *array_obj, + uint64_t index) +{ + struct bt_ctf_value_array *typed_array_obj = + BT_CTF_VALUE_TO_ARRAY(array_obj); + + BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); + BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, + typed_array_obj->garray->len); + return g_ptr_array_index(typed_array_obj->garray, index); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index( + const struct bt_ctf_private_value *array_obj, + uint64_t index) +{ + return (void *) bt_ctf_value_array_borrow_element_by_index( + (void *) array_obj, index); +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_element( + struct bt_ctf_private_value *array_obj, + struct bt_ctf_value *element_obj) +{ + struct bt_ctf_value_array *typed_array_obj = + BT_CTF_VALUE_TO_ARRAY(array_obj); + + BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); + BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); + BT_CTF_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); + g_ptr_array_add(typed_array_obj->garray, element_obj); + bt_ctf_object_get_ref(element_obj); + BT_LOGV("Appended element to array value: array-value-addr=%p, " + "element-value-addr=%p, new-size=%u", + array_obj, element_obj, typed_array_obj->garray->len); + return BT_CTF_VALUE_STATUS_OK; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element( + struct bt_ctf_private_value *array_obj, bt_bool val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *bool_obj = NULL; + + bool_obj = bt_ctf_private_value_bool_create_init(val); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) bool_obj); + bt_ctf_object_put_ref(bool_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element( + struct bt_ctf_private_value *array_obj, int64_t val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *integer_obj = NULL; + + integer_obj = bt_ctf_private_value_integer_create_init(val); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) integer_obj); + bt_ctf_object_put_ref(integer_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element( + struct bt_ctf_private_value *array_obj, double val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *real_obj = NULL; + + real_obj = bt_ctf_private_value_real_create_init(val); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) real_obj); + bt_ctf_object_put_ref(real_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element( + struct bt_ctf_private_value *array_obj, const char *val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *string_obj = NULL; + + string_obj = bt_ctf_private_value_string_create_init(val); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) string_obj); + bt_ctf_object_put_ref(string_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element( + struct bt_ctf_private_value *array_obj) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *empty_array_obj = NULL; + + empty_array_obj = bt_ctf_private_value_array_create(); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) empty_array_obj); + bt_ctf_object_put_ref(empty_array_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element( + struct bt_ctf_private_value *array_obj) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *map_obj = NULL; + + map_obj = bt_ctf_private_value_map_create(); + ret = bt_ctf_private_value_array_append_element(array_obj, + (void *) map_obj); + bt_ctf_object_put_ref(map_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index( + struct bt_ctf_private_value *array_obj, uint64_t index, + struct bt_ctf_value *element_obj) +{ + struct bt_ctf_value_array *typed_array_obj = + BT_CTF_VALUE_TO_ARRAY(array_obj); + + BT_CTF_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); + BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_CTF_VALUE_TYPE_ARRAY); + BT_CTF_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); + BT_CTF_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, + typed_array_obj->garray->len); + bt_ctf_object_put_ref(g_ptr_array_index(typed_array_obj->garray, index)); + g_ptr_array_index(typed_array_obj->garray, index) = element_obj; + bt_ctf_object_get_ref(element_obj); + BT_LOGV("Set array value's element: array-value-addr=%p, " + "index=%" PRIu64 ", element-value-addr=%p", + array_obj, index, element_obj); + return BT_CTF_VALUE_STATUS_OK; +} + +BT_HIDDEN +uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); + return (uint64_t) g_hash_table_size(BT_CTF_VALUE_TO_MAP(map_obj)->ght); +} + +BT_HIDDEN +struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value(const struct bt_ctf_value *map_obj, + const char *key) +{ + BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); + return g_hash_table_lookup(BT_CTF_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key))); +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value( + const struct bt_ctf_private_value *map_obj, const char *key) +{ + return (void *) bt_ctf_value_map_borrow_entry_value((void *) map_obj, key); +} + +BT_HIDDEN +bt_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, const char *key) +{ + BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); + return bt_g_hash_table_contains(BT_CTF_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key))); +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry( + struct bt_ctf_private_value *map_obj, + const char *key, struct bt_ctf_value *element_obj) +{ + BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Map value object"); + BT_CTF_ASSERT_PRE_NON_NULL(key, "Key"); + BT_CTF_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); + BT_CTF_ASSERT_PRE_VALUE_HOT(map_obj, "Map value object"); + g_hash_table_insert(BT_CTF_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key)), element_obj); + bt_ctf_object_get_ref(element_obj); + BT_LOGV("Inserted value into map value: map-value-addr=%p, " + "key=\"%s\", element-value-addr=%p", + map_obj, key, element_obj); + return BT_CTF_VALUE_STATUS_OK; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry( + struct bt_ctf_private_value *map_obj, const char *key, bt_bool val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *bool_obj = NULL; + + bool_obj = bt_ctf_private_value_bool_create_init(val); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) bool_obj); + bt_ctf_object_put_ref(bool_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry( + struct bt_ctf_private_value *map_obj, const char *key, int64_t val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *integer_obj = NULL; + + integer_obj = bt_ctf_private_value_integer_create_init(val); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) integer_obj); + bt_ctf_object_put_ref(integer_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry( + struct bt_ctf_private_value *map_obj, const char *key, double val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *real_obj = NULL; + + real_obj = bt_ctf_private_value_real_create_init(val); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) real_obj); + bt_ctf_object_put_ref(real_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry( + struct bt_ctf_private_value *map_obj, const char *key, + const char *val) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *string_obj = NULL; + + string_obj = bt_ctf_private_value_string_create_init(val); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) string_obj); + bt_ctf_object_put_ref(string_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry( + struct bt_ctf_private_value *map_obj, const char *key) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *array_obj = NULL; + + array_obj = bt_ctf_private_value_array_create(); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) array_obj); + bt_ctf_object_put_ref(array_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry( + struct bt_ctf_private_value *map_obj, const char *key) +{ + enum bt_ctf_value_status ret; + struct bt_ctf_private_value *empty_map_obj = NULL; + + empty_map_obj = bt_ctf_private_value_map_create(); + ret = bt_ctf_private_value_map_insert_entry(map_obj, key, + (void *) empty_map_obj); + bt_ctf_object_put_ref(empty_map_obj); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_map_foreach_entry(const struct bt_ctf_value *map_obj, + bt_ctf_value_map_foreach_entry_cb cb, void *data) +{ + enum bt_ctf_value_status ret = BT_CTF_VALUE_STATUS_OK; + gpointer key, element_obj; + GHashTableIter iter; + struct bt_ctf_value_map *typed_map_obj = BT_CTF_VALUE_TO_MAP(map_obj); + + BT_CTF_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_CTF_ASSERT_PRE_NON_NULL(cb, "Callback"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_CTF_VALUE_TYPE_MAP); + g_hash_table_iter_init(&iter, typed_map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + if (!cb(key_str, element_obj, data)) { + BT_LOGV("User canceled the loop: key=\"%s\", " + "value-addr=%p, data=%p", + key_str, element_obj, data); + ret = BT_CTF_VALUE_STATUS_CANCELED; + break; + } + } + + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry( + const struct bt_ctf_private_value *map_obj, + bt_ctf_private_value_map_foreach_entry_cb cb, void *data) +{ + return bt_ctf_value_map_foreach_entry((void *) map_obj, + (bt_ctf_value_map_foreach_entry_cb) cb, data); +} + +struct extend_map_element_data { + struct bt_ctf_private_value *extended_obj; + enum bt_ctf_value_status status; +}; + +static +bt_bool extend_map_element(const char *key, + struct bt_ctf_value *extension_obj_elem, void *data) +{ + bt_bool ret = BT_TRUE; + struct extend_map_element_data *extend_data = data; + struct bt_ctf_private_value *extension_obj_elem_copy = NULL; + + /* Copy object which is to replace the current one */ + extend_data->status = bt_ctf_value_copy(&extension_obj_elem_copy, + extension_obj_elem); + if (extend_data->status) { + BT_LOGE("Cannot copy map element: addr=%p", + extension_obj_elem); + goto error; + } + + BT_ASSERT(extension_obj_elem_copy); + + /* Replace in extended object */ + extend_data->status = bt_ctf_private_value_map_insert_entry( + extend_data->extended_obj, key, + (void *) extension_obj_elem_copy); + if (extend_data->status) { + BT_LOGE("Cannot replace value in extended value: key=\"%s\", " + "extended-value-addr=%p, element-value-addr=%p", + key, extend_data->extended_obj, + extension_obj_elem_copy); + goto error; + } + + goto end; + +error: + BT_ASSERT(extend_data->status != BT_CTF_VALUE_STATUS_OK); + ret = BT_FALSE; + +end: + BT_CTF_OBJECT_PUT_REF_AND_RESET(extension_obj_elem_copy); + return ret; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_map_extend( + struct bt_ctf_private_value **extended_map_obj, + const struct bt_ctf_value *base_map_obj, + const struct bt_ctf_value *extension_obj) +{ + struct extend_map_element_data extend_data = { + .extended_obj = NULL, + .status = BT_CTF_VALUE_STATUS_OK, + }; + + BT_CTF_ASSERT_PRE_NON_NULL(base_map_obj, "Base value object"); + BT_CTF_ASSERT_PRE_NON_NULL(extension_obj, "Extension value object"); + BT_CTF_ASSERT_PRE_NON_NULL(extended_map_obj, + "Extended value object (output)"); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(base_map_obj, BT_CTF_VALUE_TYPE_MAP); + BT_CTF_ASSERT_PRE_VALUE_IS_TYPE(extension_obj, BT_CTF_VALUE_TYPE_MAP); + BT_LOGD("Extending map value: base-value-addr=%p, extension-value-addr=%p", + base_map_obj, extension_obj); + *extended_map_obj = NULL; + + /* Create copy of base map object to start with */ + extend_data.status = bt_ctf_value_copy(extended_map_obj, base_map_obj); + if (extend_data.status) { + BT_LOGE("Cannot copy base value: base-value-addr=%p", + base_map_obj); + goto error; + } + + BT_ASSERT(extended_map_obj); + + /* + * For each key in the extension map object, replace this key + * in the copied map object. + */ + extend_data.extended_obj = *extended_map_obj; + + if (bt_ctf_value_map_foreach_entry(extension_obj, extend_map_element, + &extend_data)) { + BT_LOGE("Cannot iterate on the extension object's elements: " + "extension-value-addr=%p", extension_obj); + goto error; + } + + if (extend_data.status) { + BT_LOGE("Failed to successfully iterate on the extension object's elements: " + "extension-value-addr=%p", extension_obj); + goto error; + } + + BT_LOGD("Extended map value: extended-value-addr=%p", + *extended_map_obj); + goto end; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(*extended_map_obj); + *extended_map_obj = NULL; + +end: + return extend_data.status; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy_obj, + const struct bt_ctf_value *object) +{ + enum bt_ctf_value_status status = BT_CTF_VALUE_STATUS_OK; + + BT_CTF_ASSERT_PRE_NON_NULL(object, "Value object"); + BT_CTF_ASSERT_PRE_NON_NULL(copy_obj, "Value object copy (output)"); + BT_LOGD("Copying value object: addr=%p", object); + *copy_obj = copy_funcs[object->type](object); + if (*copy_obj) { + BT_LOGD("Copied value object: copy-value-addr=%p", + copy_obj); + } else { + status = BT_CTF_VALUE_STATUS_NOMEM; + *copy_obj = NULL; + BT_LOGE_STR("Failed to copy value object."); + } + + return status; +} + +BT_HIDDEN +bt_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b) +{ + bt_bool ret = BT_FALSE; + + BT_CTF_ASSERT_PRE_NON_NULL(object_a, "Value object A"); + BT_CTF_ASSERT_PRE_NON_NULL(object_b, "Value object B"); + + if (object_a->type != object_b->type) { + BT_LOGV("Values are different: type mismatch: " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-type=%d, value-b-type=%d", + object_a, object_b, object_a->type, object_b->type); + goto end; + } + + ret = compare_funcs[object_a->type](object_a, object_b); + +end: + return ret; +} diff --git a/src/ctf-writer/values.h b/src/ctf-writer/values.h new file mode 100644 index 00000000..b9137ef9 --- /dev/null +++ b/src/ctf-writer/values.h @@ -0,0 +1,357 @@ +#ifndef BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H +#define BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H + +/* + * Copyright (c) 2015-2017 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" + +struct bt_ctf_value; +struct bt_ctf_private_value; + +/** +@brief Status codes. +*/ +enum bt_ctf_value_status { + /// Operation canceled. + BT_CTF_VALUE_STATUS_CANCELED = 125, + + /// Cannot allocate memory. + BT_CTF_VALUE_STATUS_NOMEM = -12, + + /// Okay, no error. + BT_CTF_VALUE_STATUS_OK = 0, +}; + +BT_HIDDEN +enum bt_ctf_value_status _bt_ctf_value_freeze(struct bt_ctf_value *object); + +#ifdef BT_DEV_MODE +# define bt_ctf_value_freeze _bt_ctf_value_freeze +#else +# define bt_ctf_value_freeze(_value) +#endif /* BT_DEV_MODE */ + +extern struct bt_ctf_value *const bt_ctf_value_null; + +enum bt_ctf_value_type { + /// Null value object. + BT_CTF_VALUE_TYPE_NULL = 0, + + /// Boolean value object (holds #BT_TRUE or #BT_FALSE). + BT_CTF_VALUE_TYPE_BOOL = 1, + + /// Integer value object (holds a signed 64-bit integer raw value). + BT_CTF_VALUE_TYPE_INTEGER = 2, + + /// Floating point number value object (holds a \c double raw value). + BT_CTF_VALUE_TYPE_REAL = 3, + + /// String value object. + BT_CTF_VALUE_TYPE_STRING = 4, + + /// Array value object. + BT_CTF_VALUE_TYPE_ARRAY = 5, + + /// Map value object. + BT_CTF_VALUE_TYPE_MAP = 6, +}; + +BT_HIDDEN +enum bt_ctf_value_type bt_ctf_value_get_type(const struct bt_ctf_value *object); + +static inline +bt_bool bt_ctf_value_is_null(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_NULL; +} + +static inline +bt_bool bt_ctf_value_is_bool(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_BOOL; +} + +static inline +bt_bool bt_ctf_value_is_integer(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_INTEGER; +} + +static inline +bt_bool bt_ctf_value_is_real(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_REAL; +} + +static inline +bt_bool bt_ctf_value_is_string(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_STRING; +} + +static inline +bt_bool bt_ctf_value_is_array(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_ARRAY; +} + +static inline +bt_bool bt_ctf_value_is_map(const struct bt_ctf_value *object) +{ + return bt_ctf_value_get_type(object) == BT_CTF_VALUE_TYPE_MAP; +} + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_copy(struct bt_ctf_private_value **copy, + const struct bt_ctf_value *object); + +BT_HIDDEN +bt_bool bt_ctf_value_compare(const struct bt_ctf_value *object_a, + const struct bt_ctf_value *object_b); + +BT_HIDDEN +bt_bool bt_ctf_value_bool_get(const struct bt_ctf_value *bool_obj); + +BT_HIDDEN +int64_t bt_ctf_value_integer_get(const struct bt_ctf_value *integer_obj); + +BT_HIDDEN +double bt_ctf_value_real_get(const struct bt_ctf_value *real_obj); + +BT_HIDDEN +const char *bt_ctf_value_string_get(const struct bt_ctf_value *string_obj); + +BT_HIDDEN +uint64_t bt_ctf_value_array_get_size(const struct bt_ctf_value *array_obj); + +static inline +bt_bool bt_ctf_value_array_is_empty(const struct bt_ctf_value *array_obj) +{ + return bt_ctf_value_array_get_size(array_obj) == 0; +} + +BT_HIDDEN +struct bt_ctf_value *bt_ctf_value_array_borrow_element_by_index( + const struct bt_ctf_value *array_obj, uint64_t index); + +BT_HIDDEN +uint64_t bt_ctf_value_map_get_size(const struct bt_ctf_value *map_obj); + +static inline +bt_bool bt_ctf_value_map_is_empty(const struct bt_ctf_value *map_obj) +{ + return bt_ctf_value_map_get_size(map_obj) == 0; +} + +BT_HIDDEN +struct bt_ctf_value *bt_ctf_value_map_borrow_entry_value( + const struct bt_ctf_value *map_obj, const char *key); + +typedef bt_bool (* bt_ctf_value_map_foreach_entry_cb)(const char *key, + struct bt_ctf_value *object, void *data); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_map_foreach_entry( + const struct bt_ctf_value *map_obj, + bt_ctf_value_map_foreach_entry_cb cb, void *data); + +BT_HIDDEN +bt_bool bt_ctf_value_map_has_entry(const struct bt_ctf_value *map_obj, + const char *key); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_value_map_extend( + struct bt_ctf_private_value **extended_map_obj, + const struct bt_ctf_value *base_map_obj, + const struct bt_ctf_value *extension_map_obj); + + +struct bt_ctf_value; +struct bt_ctf_private_value; + +extern struct bt_ctf_private_value *const bt_ctf_private_value_null; + +static inline +struct bt_ctf_value *bt_ctf_private_value_as_value( + struct bt_ctf_private_value *priv_value) +{ + return (void *) priv_value; +} + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_bool_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_bool_create_init(bt_bool val); + +BT_HIDDEN +void bt_ctf_private_value_bool_set(struct bt_ctf_private_value *bool_obj, + bt_bool val); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_integer_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_integer_create_init( + int64_t val); + +BT_HIDDEN +void bt_ctf_private_value_integer_set( + struct bt_ctf_private_value *integer_obj, int64_t val); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_real_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_real_create_init(double val); + +BT_HIDDEN +void bt_ctf_private_value_real_set( + struct bt_ctf_private_value *real_obj, double val); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_string_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_string_create_init( + const char *val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_string_set( + struct bt_ctf_private_value *string_obj, + const char *val); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_array_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_array_borrow_element_by_index( + const struct bt_ctf_private_value *array_obj, uint64_t index); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_element( + struct bt_ctf_private_value *array_obj, + struct bt_ctf_value *element_obj); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_bool_element( + struct bt_ctf_private_value *array_obj, + bt_bool val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_integer_element( + struct bt_ctf_private_value *array_obj, + int64_t val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_real_element( + struct bt_ctf_private_value *array_obj, + double val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_string_element( + struct bt_ctf_private_value *array_obj, const char *val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_array_element( + struct bt_ctf_private_value *array_obj); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_append_empty_map_element( + struct bt_ctf_private_value *array_obj); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_array_set_element_by_index( + struct bt_ctf_private_value *array_obj, uint64_t index, + struct bt_ctf_value *element_obj); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_map_create(void); + +BT_HIDDEN +struct bt_ctf_private_value *bt_ctf_private_value_map_borrow_entry_value( + const struct bt_ctf_private_value *map_obj, const char *key); + +typedef bt_bool (* bt_ctf_private_value_map_foreach_entry_cb)(const char *key, + struct bt_ctf_private_value *object, void *data); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_foreach_entry( + const struct bt_ctf_private_value *map_obj, + bt_ctf_private_value_map_foreach_entry_cb cb, void *data); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_entry( + struct bt_ctf_private_value *map_obj, const char *key, + struct bt_ctf_value *element_obj); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_bool_entry( + struct bt_ctf_private_value *map_obj, const char *key, bt_bool val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_integer_entry( + struct bt_ctf_private_value *map_obj, const char *key, int64_t val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_real_entry( + struct bt_ctf_private_value *map_obj, const char *key, double val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_string_entry( + struct bt_ctf_private_value *map_obj, const char *key, + const char *val); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_array_entry( + struct bt_ctf_private_value *map_obj, const char *key); + +BT_HIDDEN +enum bt_ctf_value_status bt_ctf_private_value_map_insert_empty_map_entry( + struct bt_ctf_private_value *map_obj, const char *key); + +static inline +const char *bt_ctf_value_type_string(enum bt_ctf_value_type type) +{ + switch (type) { + case BT_CTF_VALUE_TYPE_NULL: + return "BT_CTF_VALUE_TYPE_NULL"; + case BT_CTF_VALUE_TYPE_BOOL: + return "BT_CTF_VALUE_TYPE_BOOL"; + case BT_CTF_VALUE_TYPE_INTEGER: + return "BT_CTF_VALUE_TYPE_INTEGER"; + case BT_CTF_VALUE_TYPE_REAL: + return "BT_CTF_VALUE_TYPE_REAL"; + case BT_CTF_VALUE_TYPE_STRING: + return "BT_CTF_VALUE_TYPE_STRING"; + case BT_CTF_VALUE_TYPE_ARRAY: + return "BT_CTF_VALUE_TYPE_ARRAY"; + case BT_CTF_VALUE_TYPE_MAP: + return "BT_CTF_VALUE_TYPE_MAP"; + default: + return "(unknown)"; + } +}; + +#endif /* BABELTRACE_CTF_WRITER_VALUES_INTERNAL_H */ diff --git a/src/ctf-writer/visitor.c b/src/ctf-writer/visitor.c new file mode 100644 index 00000000..6cb5bab4 --- /dev/null +++ b/src/ctf-writer/visitor.c @@ -0,0 +1,99 @@ +/* + * visitor.c + * + * Babeltrace CTF writer - Visitor + * + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "common/babeltrace.h" + +#include "visitor.h" + +BT_HIDDEN +int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root, + bt_ctf_child_count_accessor child_counter, + bt_ctf_child_accessor child_accessor, + bt_ctf_child_visitor child_visitor, + bt_ctf_visitor visitor, + void *data) +{ + int ret, child_count, i; + + ret = visitor(root, data); + if (ret) { + goto end; + } + + child_count = child_counter(root->object); + if (child_count < 0) { + ret = child_count; + goto end; + } + + for (i = 0; i < child_count; i++) { + void *child; + + child = child_accessor(root->object, i); + if (!child) { + ret = -1; + goto end; + } + ret = child_visitor(child, visitor, data); + BT_CTF_OBJECT_PUT_REF_AND_RESET(child); + if (ret) { + goto end; + } + } +end: + return ret; +} + +enum bt_ctf_visitor_object_type bt_ctf_visitor_object_get_type( + struct bt_ctf_visitor_object *object) +{ + enum bt_ctf_visitor_object_type ret = BT_CTF_VISITOR_OBJECT_TYPE_UNKNOWN; + + if (!object) { + goto end; + } + + ret = object->type; +end: + return ret; +} + +void *bt_ctf_visitor_object_get_object(struct bt_ctf_visitor_object *object) +{ + void *ret = NULL; + + if (!object) { + goto end; + } + + ret = object->object; +end: + return ret; +} diff --git a/src/ctf-writer/visitor.h b/src/ctf-writer/visitor.h new file mode 100644 index 00000000..549285e2 --- /dev/null +++ b/src/ctf-writer/visitor.h @@ -0,0 +1,49 @@ +#ifndef BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H +#define BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H + +/* + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +typedef void *(*bt_ctf_child_accessor)(void *object, int index); +typedef int64_t (*bt_ctf_child_count_accessor)(void *object); +typedef int (*bt_ctf_child_visitor)(void *object, bt_ctf_visitor visitor, + void *data); + +struct bt_ctf_visitor_object { + enum bt_ctf_visitor_object_type type; + void *object; +}; + +BT_HIDDEN +int bt_ctf_visitor_helper(struct bt_ctf_visitor_object *root, + bt_ctf_child_count_accessor child_counter, + bt_ctf_child_accessor child_accessor, + bt_ctf_child_visitor child_visitor, + bt_ctf_visitor visitor, + void *data); + +#endif /* BABELTRACE_CTF_WRITER_VISITOR_INTERNAL_H */ diff --git a/src/ctf-writer/writer.c b/src/ctf-writer/writer.c new file mode 100644 index 00000000..6c72acaf --- /dev/null +++ b/src/ctf-writer/writer.c @@ -0,0 +1,458 @@ +/* + * writer.c + * + * Babeltrace CTF Writer + * + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTF-WRITER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/assert.h" +#include "compat/compiler.h" +#include "compat/endian.h" +#include "compat/uuid.h" + +#include "clock.h" +#include "fields.h" +#include "field-types.h" +#include "functor.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" +#include "writer.h" + +static +void bt_ctf_writer_destroy(struct bt_ctf_object *obj); + +static +int init_trace_packet_header(struct bt_ctf_trace *trace) +{ + int ret = 0; + struct bt_ctf_field_type *_uint32_t = + get_field_type(FIELD_TYPE_ALIAS_UINT32_T); + struct bt_ctf_field_type *_uint8_t = + get_field_type(FIELD_TYPE_ALIAS_UINT8_T); + struct bt_ctf_field_type *trace_packet_header_type = + bt_ctf_field_type_structure_create(); + struct bt_ctf_field_type *uuid_array_type = + bt_ctf_field_type_array_create(_uint8_t, 16); + + if (!trace_packet_header_type || !uuid_array_type) { + ret = -1; + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + _uint32_t, "magic"); + if (ret) { + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + uuid_array_type, "uuid"); + if (ret) { + goto end; + } + + ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type, + _uint32_t, "stream_id"); + if (ret) { + goto end; + } + + ret = bt_ctf_trace_set_packet_header_field_type(trace, + trace_packet_header_type); + if (ret) { + goto end; + } +end: + bt_ctf_object_put_ref(uuid_array_type); + bt_ctf_object_put_ref(_uint32_t); + bt_ctf_object_put_ref(_uint8_t); + bt_ctf_object_put_ref(trace_packet_header_type); + return ret; +} + +struct bt_ctf_writer *bt_ctf_writer_create(const char *path) +{ + int ret; + struct bt_ctf_writer *writer = NULL; + unsigned char uuid[16]; + char *metadata_path = NULL; + + if (!path) { + goto error; + } + + writer = g_new0(struct bt_ctf_writer, 1); + if (!writer) { + goto error; + } + + metadata_path = g_build_filename(path, "metadata", NULL); + + bt_ctf_object_init_shared(&writer->base, bt_ctf_writer_destroy); + writer->path = g_string_new(path); + if (!writer->path) { + goto error_destroy; + } + + writer->trace = bt_ctf_trace_create(); + if (!writer->trace) { + goto error_destroy; + } + + ret = init_trace_packet_header(writer->trace); + if (ret) { + goto error_destroy; + } + + /* Generate a UUID for this writer's trace */ + ret = bt_uuid_generate(uuid); + if (ret) { + BT_LOGE_STR("Cannot generate UUID for CTF writer's trace."); + goto error_destroy; + } + + ret = bt_ctf_trace_set_uuid(writer->trace, uuid); + if (ret) { + goto error_destroy; + } + + bt_ctf_object_set_parent(&writer->trace->common.base, &writer->base); + bt_ctf_object_put_ref(writer->trace); + + /* Default to little-endian */ + ret = bt_ctf_writer_set_byte_order(writer, BT_CTF_BYTE_ORDER_NATIVE); + BT_ASSERT(ret == 0); + + /* Create trace directory if necessary and open a metadata file */ + if (g_mkdir_with_parents(path, S_IRWXU | S_IRWXG)) { + perror("g_mkdir_with_parents"); + goto error_destroy; + } + + writer->metadata_fd = open(metadata_path, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (writer->metadata_fd < 0) { + perror("open"); + goto error_destroy; + } + + g_free(metadata_path); + return writer; + +error_destroy: + BT_CTF_OBJECT_PUT_REF_AND_RESET(writer); +error: + g_free(metadata_path); + return writer; +} + +void bt_ctf_writer_destroy(struct bt_ctf_object *obj) +{ + struct bt_ctf_writer *writer; + + writer = container_of(obj, struct bt_ctf_writer, base); + bt_ctf_writer_flush_metadata(writer); + if (writer->path) { + g_string_free(writer->path, TRUE); + } + + if (writer->metadata_fd > 0) { + if (close(writer->metadata_fd)) { + perror("close"); + } + } + + bt_ctf_object_try_spec_release(&writer->trace->common.base); + g_free(writer); +} + +struct bt_ctf_trace *bt_ctf_writer_get_trace(struct bt_ctf_writer *writer) +{ + struct bt_ctf_trace *trace = NULL; + + if (!writer) { + goto end; + } + + trace = writer->trace; + bt_ctf_object_get_ref(trace); +end: + return trace; +} + +struct bt_ctf_stream *bt_ctf_writer_create_stream(struct bt_ctf_writer *writer, + struct bt_ctf_stream_class *stream_class) +{ + struct bt_ctf_stream *stream = NULL; + int stream_class_count; + bt_bool stream_class_found = BT_FALSE; + int i; + + if (!writer || !stream_class) { + goto error; + } + + /* Make sure the stream class is part of the writer's trace */ + stream_class_count = bt_ctf_trace_get_stream_class_count(writer->trace); + if (stream_class_count < 0) { + goto error; + } + + for (i = 0; i < stream_class_count; i++) { + struct bt_ctf_stream_class *existing_stream_class = + bt_ctf_trace_get_stream_class_by_index( + writer->trace, i); + + if (existing_stream_class == stream_class) { + stream_class_found = BT_TRUE; + } + + BT_CTF_OBJECT_PUT_REF_AND_RESET(existing_stream_class); + + if (stream_class_found) { + break; + } + } + + if (!stream_class_found) { + int ret = bt_ctf_trace_add_stream_class(writer->trace, + stream_class); + + if (ret) { + goto error; + } + } + + stream = bt_ctf_stream_create_with_id(stream_class, NULL, -1ULL); + if (!stream) { + goto error; + } + + return stream; + +error: + BT_CTF_OBJECT_PUT_REF_AND_RESET(stream); + return stream; +} + +int bt_ctf_writer_add_environment_field(struct bt_ctf_writer *writer, + const char *name, + const char *value) +{ + int ret = -1; + + if (!writer || !name || !value) { + goto end; + } + + ret = bt_ctf_trace_set_environment_field_string(writer->trace, + name, value); +end: + return ret; +} + +int bt_ctf_writer_add_environment_field_int64(struct bt_ctf_writer *writer, + const char *name, int64_t value) +{ + int ret = -1; + + if (!writer || !name) { + goto end; + } + + ret = bt_ctf_trace_set_environment_field_integer(writer->trace, name, + value); +end: + return ret; +} + +int bt_ctf_writer_add_clock(struct bt_ctf_writer *writer, + struct bt_ctf_clock *clock) +{ + int ret = -1; + + if (!writer || !clock) { + goto end; + } + + ret = bt_ctf_trace_add_clock_class(writer->trace, clock->clock_class); +end: + return ret; +} + +char *bt_ctf_writer_get_metadata_string(struct bt_ctf_writer *writer) +{ + char *metadata_string = NULL; + + if (!writer) { + goto end; + } + + metadata_string = bt_ctf_trace_get_metadata_string( + writer->trace); +end: + return metadata_string; +} + +void bt_ctf_writer_flush_metadata(struct bt_ctf_writer *writer) +{ + int ret; + char *metadata_string = NULL; + + if (!writer) { + goto end; + } + + metadata_string = bt_ctf_trace_get_metadata_string( + writer->trace); + if (!metadata_string) { + goto end; + } + + if (lseek(writer->metadata_fd, 0, SEEK_SET) == (off_t)-1) { + perror("lseek"); + goto end; + } + + if (ftruncate(writer->metadata_fd, 0)) { + perror("ftruncate"); + goto end; + } + + ret = write(writer->metadata_fd, metadata_string, + strlen(metadata_string)); + if (ret < 0) { + perror("write"); + goto end; + } +end: + g_free(metadata_string); +} + +int bt_ctf_writer_set_byte_order(struct bt_ctf_writer *writer, + enum bt_ctf_byte_order byte_order) +{ + int ret = 0; + + if (!writer || writer->frozen) { + ret = -1; + goto end; + } + + if (byte_order == BT_CTF_BYTE_ORDER_NATIVE) { + if (BYTE_ORDER == LITTLE_ENDIAN) { + byte_order = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN; + } else { + byte_order = BT_CTF_BYTE_ORDER_BIG_ENDIAN; + } + } + + ret = bt_ctf_trace_set_native_byte_order(writer->trace, + byte_order); +end: + return ret; +} + +BT_HIDDEN +void bt_ctf_writer_freeze(struct bt_ctf_writer *writer) +{ + writer->frozen = 1; +} + +static +const unsigned int field_type_aliases_alignments[] = { + [FIELD_TYPE_ALIAS_UINT5_T] = 1, + [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8, + [FIELD_TYPE_ALIAS_UINT27_T] = 1, + [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8, +}; + +static +const unsigned int field_type_aliases_sizes[] = { + [FIELD_TYPE_ALIAS_UINT5_T] = 5, + [FIELD_TYPE_ALIAS_UINT8_T] = 8, + [FIELD_TYPE_ALIAS_UINT16_T] = 16, + [FIELD_TYPE_ALIAS_UINT27_T] = 27, + [FIELD_TYPE_ALIAS_UINT32_T] = 32, + [FIELD_TYPE_ALIAS_UINT64_T] = 64, +}; + +BT_HIDDEN +struct bt_ctf_field_type *get_field_type(enum field_type_alias alias) +{ + int ret; + unsigned int alignment, size; + struct bt_ctf_field_type *field_type = NULL; + + if (alias >= NR_FIELD_TYPE_ALIAS) { + goto end; + } + + alignment = field_type_aliases_alignments[alias]; + size = field_type_aliases_sizes[alias]; + field_type = bt_ctf_field_type_integer_create(size); + ret = bt_ctf_field_type_set_alignment(field_type, alignment); + if (ret) { + BT_CTF_OBJECT_PUT_REF_AND_RESET(field_type); + } +end: + return field_type; +} + +BT_HIDDEN +const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order) +{ + const char *string; + + switch (byte_order) { + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + string = "le"; + break; + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + string = "be"; + break; + case BT_CTF_BYTE_ORDER_NATIVE: + string = "native"; + break; + default: + abort(); + } + + return string; +} diff --git a/src/ctf-writer/writer.h b/src/ctf-writer/writer.h new file mode 100644 index 00000000..37e6a58a --- /dev/null +++ b/src/ctf-writer/writer.h @@ -0,0 +1,72 @@ +#ifndef BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H +#define BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H + +/* + * Copyright 2013, 2014 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include "common/babeltrace.h" + +#include "object.h" + +struct metadata_context { + GString *string; + GString *field_name; + unsigned int current_indentation_level; +}; + +struct bt_ctf_writer { + struct bt_ctf_object base; + int frozen; /* Protects attributes that can't be changed mid-trace */ + struct bt_ctf_trace *trace; + GString *path; + int metadata_fd; +}; + +enum field_type_alias { + FIELD_TYPE_ALIAS_UINT5_T = 0, + FIELD_TYPE_ALIAS_UINT8_T, + FIELD_TYPE_ALIAS_UINT16_T, + FIELD_TYPE_ALIAS_UINT27_T, + FIELD_TYPE_ALIAS_UINT32_T, + FIELD_TYPE_ALIAS_UINT64_T, + NR_FIELD_TYPE_ALIAS, +}; + +BT_HIDDEN +struct bt_ctf_field_type *get_field_type(enum field_type_alias alias); + +BT_HIDDEN +const char *bt_ctf_get_byte_order_string(enum bt_ctf_byte_order byte_order); + +BT_HIDDEN +void bt_ctf_writer_freeze(struct bt_ctf_writer *writer); + +#endif /* BABELTRACE_CTF_WRITER_WRITER_INTERNAL_H */ diff --git a/src/ctfser/Makefile.am b/src/ctfser/Makefile.am new file mode 100644 index 00000000..e6ae86f5 --- /dev/null +++ b/src/ctfser/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" + +noinst_LTLIBRARIES = libbabeltrace2-ctfser.la + +libbabeltrace2_ctfser_la_SOURCES = \ + ctfser.c \ + ctfser.h \ + logging.c \ + logging.h diff --git a/src/ctfser/ctfser.c b/src/ctfser/ctfser.c new file mode 100644 index 00000000..62032469 --- /dev/null +++ b/src/ctfser/ctfser.c @@ -0,0 +1,280 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CTFSER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include +#include +#include +#include +#include "common/babeltrace.h" +#include "common/common.h" +#include "ctfser/ctfser.h" +#include "compat/unistd.h" +#include "compat/fcntl.h" + +static inline +uint64_t get_packet_size_increment_bytes(void) +{ + return bt_common_get_page_size() * 8; +} + +static inline +void mmap_align_ctfser(struct bt_ctfser *ctfser) +{ + ctfser->base_mma = mmap_align(ctfser->cur_packet_size_bytes, + PROT_READ | PROT_WRITE, + MAP_SHARED, ctfser->fd, ctfser->mmap_offset); +} + +BT_HIDDEN +int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser) +{ + int ret; + + BT_ASSERT(ctfser); + BT_LOGV("Increasing stream file's current packet size: " + "path=\"%s\", fd=%d, " + "offset-in-cur-packet-bits=%" PRIu64 ", " + "cur-packet-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->offset_in_cur_packet_bits, + ctfser->cur_packet_size_bytes); + ret = munmap_align(ctfser->base_mma); + if (ret) { + BT_LOGE_ERRNO("Failed to perform an aligned memory unmapping", + ": ret=%d", ret); + goto end; + } + + ctfser->cur_packet_size_bytes += get_packet_size_increment_bytes(); + + do { + ret = bt_posix_fallocate(ctfser->fd, ctfser->mmap_offset, + ctfser->cur_packet_size_bytes); + } while (ret == EINTR); + + if (ret) { + BT_LOGE("Failed to preallocate memory space: ret=%d", ret); + goto end; + } + + mmap_align_ctfser(ctfser); + if (ctfser->base_mma == MAP_FAILED) { + BT_LOGE_ERRNO("Failed to perform an aligned memory mapping", + ": ret=%d", ret); + ret = -1; + goto end; + } + + BT_LOGV("Increased packet size: " + "path=\"%s\", fd=%d, " + "offset-in-cur-packet-bits=%" PRIu64 ", " + "new-packet-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->offset_in_cur_packet_bits, + ctfser->cur_packet_size_bytes); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path) +{ + int ret = 0; + + BT_ASSERT(ctfser); + memset(ctfser, 0, sizeof(*ctfser)); + ctfser->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (ctfser->fd < 0) { + BT_LOGW_ERRNO("Failed to open stream file for writing", + ": path=\"%s\", ret=%d", + path, ctfser->fd); + ret = -1; + goto end; + } + + ctfser->path = g_string_new(path); + +end: + return ret; +} + +BT_HIDDEN +int bt_ctfser_fini(struct bt_ctfser *ctfser) +{ + int ret = 0; + + if (ctfser->fd == -1) { + goto free_path; + } + + /* + * Truncate the stream file's size to the minimum required to + * fit the last packet as we might have grown it too much during + * the last memory map. + */ + do { + ret = ftruncate(ctfser->fd, ctfser->stream_size_bytes); + } while (ret == -1 && errno == EINTR); + + if (ret) { + BT_LOGE_ERRNO("Failed to truncate stream file", + ": ret=%d, size-bytes=%" PRIu64, + ret, ctfser->stream_size_bytes); + goto end; + } + + if (ctfser->base_mma) { + /* Unmap old base */ + ret = munmap_align(ctfser->base_mma); + if (ret) { + BT_LOGE_ERRNO("Failed to unmap stream file", + ": ret=%d, size-bytes=%" PRIu64, + ret, ctfser->stream_size_bytes); + goto end; + } + + ctfser->base_mma = NULL; + } + + ret = close(ctfser->fd); + if (ret) { + BT_LOGE_ERRNO("Failed to close stream file", + ": ret=%d", ret); + goto end; + } + + ctfser->fd = -1; + +free_path: + if (ctfser->path) { + g_string_free(ctfser->path, TRUE); + ctfser->path = NULL; + } + +end: + return ret; +} + +BT_HIDDEN +int bt_ctfser_open_packet(struct bt_ctfser *ctfser) +{ + int ret = 0; + + BT_LOGV("Opening packet: path=\"%s\", fd=%d, " + "prev-packet-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->prev_packet_size_bytes); + + if (ctfser->base_mma) { + /* Unmap old base (previous packet) */ + ret = munmap_align(ctfser->base_mma); + if (ret) { + BT_LOGE_ERRNO("Failed to unmap stream file", + ": ret=%d, size-bytes=%" PRIu64, + ret, ctfser->stream_size_bytes); + goto end; + } + + ctfser->base_mma = NULL; + } + + /* + * Add the previous packet's size to the memory map address + * offset to start writing immediately after it. + */ + ctfser->mmap_offset += ctfser->prev_packet_size_bytes; + ctfser->prev_packet_size_bytes = 0; + + /* Make initial space for the current packet */ + ctfser->cur_packet_size_bytes = get_packet_size_increment_bytes(); + + do { + ret = bt_posix_fallocate(ctfser->fd, ctfser->mmap_offset, + ctfser->cur_packet_size_bytes); + } while (ret == EINTR); + + if (ret) { + BT_LOGE("Failed to preallocate memory space: ret=%d", ret); + goto end; + } + + /* Start writing at the beginning of the current packet */ + ctfser->offset_in_cur_packet_bits = 0; + + /* Get new base address */ + mmap_align_ctfser(ctfser); + if (ctfser->base_mma == MAP_FAILED) { + BT_LOGE_ERRNO("Failed to perform an aligned memory mapping", + ": ret=%d", ret); + ret = -1; + goto end; + } + + BT_LOGV("Opened packet: path=\"%s\", fd=%d, " + "cur-packet-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->cur_packet_size_bytes); + +end: + return ret; +} + +BT_HIDDEN +void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser, + uint64_t packet_size_bytes) +{ + BT_LOGV("Closing packet: path=\"%s\", fd=%d, " + "offset-in-cur-packet-bits=%" PRIu64 + "cur-packet-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->offset_in_cur_packet_bits, + ctfser->cur_packet_size_bytes); + + /* + * This will be used during the next call to + * bt_ctfser_open_packet(): we add + * `ctfser->prev_packet_size_bytes` to the current memory map + * address offset (first byte of _this_ packet), effectively + * making _this_ packet the required size. + */ + ctfser->prev_packet_size_bytes = packet_size_bytes; + ctfser->stream_size_bytes += packet_size_bytes; + BT_LOGV("Closed packet: path=\"%s\", fd=%d, " + "stream-file-size-bytes=%" PRIu64, + ctfser->path->str, ctfser->fd, + ctfser->stream_size_bytes); +} diff --git a/src/ctfser/ctfser.h b/src/ctfser/ctfser.h new file mode 100644 index 00000000..c270faf9 --- /dev/null +++ b/src/ctfser/ctfser.h @@ -0,0 +1,576 @@ +#ifndef BABELTRACE_CTFSER_INTERNAL_H +#define BABELTRACE_CTFSER_INTERNAL_H + +/* + * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation + * Copyright 2013, 2014 Jérémie Galarneau + * Copyright 2017-2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include +#include +#include +#include "compat/mman.h" +#include +#include +#include +#include "common/align.h" +#include "compat/endian.h" +#include "common/common.h" +#include "common/mmap-align.h" +#include +#include "common/assert.h" +#include "compat/bitfield.h" +#include + +struct bt_ctfser { + /* Stream file's descriptor */ + int fd; + + /* Offset (bytes) of memory map (current packet) in the stream file */ + off_t mmap_offset; + + /* Offset (bytes) of packet's first byte in the memory map */ + off_t mmap_base_offset; + + /* Current offset (bits) within current packet */ + uint64_t offset_in_cur_packet_bits; + + /* Current packet size (bytes) */ + uint64_t cur_packet_size_bytes; + + /* Previous packet size (bytes) */ + uint64_t prev_packet_size_bytes; + + /* Current stream size (bytes) */ + uint64_t stream_size_bytes; + + /* Memory map base address */ + struct mmap_align *base_mma; + + /* Stream file's path (for debugging) */ + GString *path; +}; + +/* + * Initializes a CTF serializer. + * + * This function opens the file `path` for writing. + */ +BT_HIDDEN +int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path); + +/* + * Finalizes a CTF serializer. + * + * This function truncates the stream file so that there's no extra + * padding after the last packet, and then closes the file. + */ +BT_HIDDEN +int bt_ctfser_fini(struct bt_ctfser *ctfser); + +/* + * Opens a new packet. + * + * All the next writing functions are performed within this new packet. + */ +BT_HIDDEN +int bt_ctfser_open_packet(struct bt_ctfser *ctfser); + +/* + * Closes the current packet, making its size `packet_size_bytes`. + */ +BT_HIDDEN +void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser, + uint64_t packet_size_bytes); + +BT_HIDDEN +int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser); + +static inline +uint64_t _bt_ctfser_cur_packet_size_bits(struct bt_ctfser *ctfser) +{ + return ctfser->cur_packet_size_bytes * 8; +} + +static inline +uint64_t _bt_ctfser_prev_packet_size_bits(struct bt_ctfser *ctfser) +{ + return ctfser->prev_packet_size_bytes * 8; +} + +static inline +uint64_t _bt_ctfser_offset_bytes(struct bt_ctfser *ctfser) +{ + return ctfser->offset_in_cur_packet_bits / 8; +} + +static inline +uint8_t *_bt_ctfser_get_addr(struct bt_ctfser *ctfser) +{ + /* Only makes sense to get the address after aligning on byte */ + BT_ASSERT(ctfser->offset_in_cur_packet_bits % 8 == 0); + return ((uint8_t *) mmap_align_addr(ctfser->base_mma)) + + ctfser->mmap_base_offset + _bt_ctfser_offset_bytes(ctfser); +} + +static inline +bool _bt_ctfser_has_space_left(struct bt_ctfser *ctfser, uint64_t size_bits) +{ + bool has_space_left = true; + + if (unlikely((ctfser->offset_in_cur_packet_bits + size_bits > + _bt_ctfser_cur_packet_size_bits(ctfser)))) { + has_space_left = false; + goto end; + } + + if (unlikely(size_bits > UINT64_MAX - ctfser->offset_in_cur_packet_bits)) { + has_space_left = false; + goto end; + } + +end: + return has_space_left; +} + +static inline +void _bt_ctfser_incr_offset(struct bt_ctfser *ctfser, uint64_t size_bits) +{ + BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); + ctfser->offset_in_cur_packet_bits += size_bits; +} + +/* + * Aligns the current offset within the current packet to + * `alignment_bits` bits (power of two, > 0). + */ +static inline +int bt_ctfser_align_offset_in_current_packet(struct bt_ctfser *ctfser, + uint64_t alignment_bits) +{ + int ret = 0; + uint64_t align_size_bits; + + BT_ASSERT(alignment_bits > 0); + align_size_bits = ALIGN(ctfser->offset_in_cur_packet_bits, + alignment_bits) - ctfser->offset_in_cur_packet_bits; + + if (unlikely(!_bt_ctfser_has_space_left(ctfser, align_size_bits))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + _bt_ctfser_incr_offset(ctfser, align_size_bits); + +end: + return ret; +} + +static inline +int _bt_ctfser_write_byte_aligned_unsigned_int_no_align( + struct bt_ctfser *ctfser, uint64_t value, + unsigned int size_bits, int byte_order) +{ + int ret = 0; + + /* Reverse byte order? */ + bool rbo = byte_order != BYTE_ORDER; + + BT_ASSERT(size_bits % 8 == 0); + BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); + + switch (size_bits) { + case 8: + { + uint8_t v = (uint8_t) value; + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 16: + { + uint16_t v = (uint16_t) value; + + if (rbo) { + v = GUINT16_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 32: + { + uint32_t v = (uint32_t) value; + + if (rbo) { + v = GUINT32_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 64: + { + uint64_t v = (uint64_t) value; + + if (rbo) { + v = GUINT64_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + default: + abort(); + } + + _bt_ctfser_incr_offset(ctfser, size_bits); + return ret; +} + +static inline +int _bt_ctfser_write_byte_aligned_signed_int_no_align( + struct bt_ctfser *ctfser, int64_t value, + unsigned int size_bits, int byte_order) +{ + int ret = 0; + + /* Reverse byte order? */ + bool rbo = byte_order != BYTE_ORDER; + + BT_ASSERT(size_bits % 8 == 0); + BT_ASSERT(_bt_ctfser_has_space_left(ctfser, size_bits)); + + switch (size_bits) { + case 8: + { + int8_t v = (int8_t) value; + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 16: + { + int16_t v = (int16_t) value; + + if (rbo) { + v = GUINT16_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 32: + { + int32_t v = (int32_t) value; + + if (rbo) { + v = GUINT32_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + case 64: + { + int64_t v = (int64_t) value; + + if (rbo) { + v = GUINT64_SWAP_LE_BE(v); + } + + memcpy(_bt_ctfser_get_addr(ctfser), &v, sizeof(v)); + break; + } + default: + abort(); + } + + _bt_ctfser_incr_offset(ctfser, size_bits); + return ret; +} + +/* + * Writes an unsigned integer known to have a size that is a multiple of + * 8 and an alignment that is >= 8 at the current offset within the + * current packet. + */ +static inline +int bt_ctfser_write_byte_aligned_unsigned_int(struct bt_ctfser *ctfser, + uint64_t value, unsigned int alignment_bits, + unsigned int size_bits, int byte_order) +{ + int ret; + + BT_ASSERT(alignment_bits % 8 == 0); + ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); + if (unlikely(ret)) { + goto end; + } + + if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + ret = _bt_ctfser_write_byte_aligned_unsigned_int_no_align(ctfser, value, + size_bits, byte_order); + if (unlikely(ret)) { + goto end; + } + +end: + return ret; +} + +/* + * Writes a signed integer known to have a size that is a multiple of 8 + * and an alignment that is >= 8 at the current offset within the + * current packet. + */ +static inline +int bt_ctfser_write_byte_aligned_signed_int(struct bt_ctfser *ctfser, + int64_t value, unsigned int alignment_bits, + unsigned int size_bits, int byte_order) +{ + int ret; + + BT_ASSERT(alignment_bits % 8 == 0); + ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); + if (unlikely(ret)) { + goto end; + } + + if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + ret = _bt_ctfser_write_byte_aligned_signed_int_no_align(ctfser, value, + size_bits, byte_order); + if (unlikely(ret)) { + goto end; + } + +end: + return ret; +} + +/* + * Writes an unsigned integer at the current offset within the current + * packet. + */ +static inline +int bt_ctfser_write_unsigned_int(struct bt_ctfser *ctfser, uint64_t value, + unsigned int alignment_bits, unsigned int size_bits, + int byte_order) +{ + int ret = 0; + + ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); + if (unlikely(ret)) { + goto end; + } + + if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + if (alignment_bits % 8 == 0 && size_bits % 8 == 0) { + ret = _bt_ctfser_write_byte_aligned_unsigned_int_no_align( + ctfser, value, size_bits, byte_order); + goto end; + } + + if (byte_order == LITTLE_ENDIAN) { + bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) + + ctfser->mmap_base_offset, uint8_t, + ctfser->offset_in_cur_packet_bits, size_bits, value); + } else { + bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) + + ctfser->mmap_base_offset, uint8_t, + ctfser->offset_in_cur_packet_bits, size_bits, value); + } + + _bt_ctfser_incr_offset(ctfser, size_bits); + +end: + return ret; +} + +/* + * Writes a signed integer at the current offset within the current + * packet. + */ +static inline +int bt_ctfser_write_signed_int(struct bt_ctfser *ctfser, int64_t value, + unsigned int alignment_bits, unsigned int size_bits, + int byte_order) +{ + int ret = 0; + + ret = bt_ctfser_align_offset_in_current_packet(ctfser, alignment_bits); + if (unlikely(ret)) { + goto end; + } + + if (unlikely(!_bt_ctfser_has_space_left(ctfser, size_bits))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + if (alignment_bits % 8 == 0 && size_bits % 8 == 0) { + ret = _bt_ctfser_write_byte_aligned_signed_int_no_align( + ctfser, value, size_bits, byte_order); + goto end; + } + + if (byte_order == LITTLE_ENDIAN) { + bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) + + ctfser->mmap_base_offset, uint8_t, + ctfser->offset_in_cur_packet_bits, size_bits, value); + } else { + bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) + + ctfser->mmap_base_offset, uint8_t, + ctfser->offset_in_cur_packet_bits, size_bits, value); + } + + _bt_ctfser_incr_offset(ctfser, size_bits); + +end: + return ret; +} + +/* + * Writes a 32-bit floating point number at the current offset within + * the current packet. + */ +static inline +int bt_ctfser_write_float32(struct bt_ctfser *ctfser, double value, + unsigned int alignment_bits, int byte_order) +{ + union u32f { + uint32_t u; + float f; + } u32f; + + u32f.f = (float) value; + return bt_ctfser_write_unsigned_int(ctfser, (uint64_t) u32f.u, + alignment_bits, 32, byte_order); +} + +/* + * Writes a 64-bit floating point number at the current offset within + * the current packet. + */ +static inline +int bt_ctfser_write_float64(struct bt_ctfser *ctfser, double value, + unsigned int alignment_bits, int byte_order) +{ + union u64f { + uint64_t u; + float f; + } u64f; + + u64f.f = value; + return bt_ctfser_write_unsigned_int(ctfser, u64f.u, alignment_bits, + 64, byte_order); +} + +/* + * Writes a C string, including the terminating null character, at the + * current offset within the current packet. + */ +static inline +int bt_ctfser_write_string(struct bt_ctfser *ctfser, const char *value) +{ + int ret = 0; + const char *at = value; + + ret = bt_ctfser_align_offset_in_current_packet(ctfser, 8); + if (unlikely(ret)) { + goto end; + } + + while (true) { + if (unlikely(!_bt_ctfser_has_space_left(ctfser, 8))) { + ret = _bt_ctfser_increase_cur_packet_size(ctfser); + if (unlikely(ret)) { + goto end; + } + } + + memcpy(_bt_ctfser_get_addr(ctfser), at, sizeof(*at)); + _bt_ctfser_incr_offset(ctfser, 8); + + if (unlikely(*at == '\0')) { + break; + } + + at++; + } + +end: + return ret; +} + +/* + * Returns the current offset within the current packet (bits). + */ +static inline +uint64_t bt_ctfser_get_offset_in_current_packet_bits(struct bt_ctfser *ctfser) +{ + return ctfser->offset_in_cur_packet_bits; +} + +/* + * Sets the current offset within the current packet (bits). + */ +static inline +void bt_ctfser_set_offset_in_current_packet_bits(struct bt_ctfser *ctfser, + uint64_t offset_bits) +{ + BT_ASSERT(offset_bits <= _bt_ctfser_cur_packet_size_bits(ctfser)); + ctfser->offset_in_cur_packet_bits = offset_bits; +} + +static inline +const char *bt_ctfser_get_file_path(struct bt_ctfser *ctfser) +{ + return ctfser->path->str; +} + +#endif /* BABELTRACE_CTFSER_INTERNAL_H */ diff --git a/src/ctfser/logging.c b/src/ctfser/logging.c new file mode 100644 index 00000000..779078d6 --- /dev/null +++ b/src/ctfser/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_ctfser_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_ctfser_log_level, "BABELTRACE_CTFSER_LOG_LEVEL"); diff --git a/src/ctfser/logging.h b/src/ctfser/logging.h new file mode 100644 index 00000000..547f1ac1 --- /dev/null +++ b/src/ctfser/logging.h @@ -0,0 +1,31 @@ +#ifndef COMMON_LOGGING_H +#define COMMON_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_ctfser_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_ctfser_log_level); + +#endif /* COMMON_LOGGING_H */ diff --git a/src/fd-cache/Makefile.am b/src/fd-cache/Makefile.am new file mode 100644 index 00000000..c4a12882 --- /dev/null +++ b/src/fd-cache/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS += -DINSTALL_LIBDIR=\"$(libdir)\" + +noinst_LTLIBRARIES = libbabeltrace2-fd-cache.la + +libbabeltrace2_fd_cache_la_SOURCES = \ + fd-cache.c \ + fd-cache.h \ + logging.c \ + logging.h diff --git a/src/fd-cache/fd-cache.c b/src/fd-cache/fd-cache.c new file mode 100644 index 00000000..6d625189 --- /dev/null +++ b/src/fd-cache/fd-cache.c @@ -0,0 +1,245 @@ +/* + * fd-cache.c + * + * Babeltrace - File descriptor cache + * + * Copyright 2019 Francis Deslauriers + * + * Author: Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "FD-CACHE" +#include "logging.h" + +#include +#include +#include +#include +#include + +#include "common/assert.h" +#include "fd-cache.h" + +struct file_key { + uint64_t dev; + uint64_t ino; +}; + +struct fd_handle_internal { + struct bt_fd_cache_handle fd_handle; + uint64_t ref_count; + struct file_key *key; +}; + +static +void fd_cache_handle_internal_destroy( + struct fd_handle_internal *internal_fd) +{ + if (!internal_fd) { + goto end; + } + + if (internal_fd->fd_handle.fd >= 0) { + close(internal_fd->fd_handle.fd); + internal_fd->fd_handle.fd = -1; + } + +end: + g_free(internal_fd); +} + +/* + * Using simple hash algorithm found on stackoverflow: + * https://stackoverflow.com/questions/664014/ + */ +static inline +uint64_t hash_uint64_t(uint64_t x) { + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +} + +static +guint file_key_hash(gconstpointer v) +{ + const struct file_key *fk = v; + return hash_uint64_t(fk->dev) ^ hash_uint64_t(fk->ino); +} + +static +gboolean file_key_equal(gconstpointer v1, gconstpointer v2) +{ + const struct file_key *fk1 = v1; + const struct file_key *fk2 = v2; + + return (fk1->dev == fk2->dev) && (fk1->ino == fk2->ino); +} + +static +void file_key_destroy(gpointer data) +{ + struct file_key *fk = data; + g_free(fk); +} + +BT_HIDDEN +int bt_fd_cache_init(struct bt_fd_cache *fdc) +{ + int ret = 0; + + fdc->cache = g_hash_table_new_full(file_key_hash, file_key_equal, + file_key_destroy, (GDestroyNotify) fd_cache_handle_internal_destroy); + if (!fdc->cache) { + ret = -1; + } + + return ret; +} + +BT_HIDDEN +void bt_fd_cache_fini(struct bt_fd_cache *fdc) +{ + BT_ASSERT(fdc->cache); + /* + * All handle should have been removed for the hashtable at this point. + */ + BT_ASSERT(g_hash_table_size(fdc->cache) == 0); + g_hash_table_destroy(fdc->cache); + + return; +} + +BT_HIDDEN +struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc, + const char *path) +{ + struct fd_handle_internal *fd_internal = NULL; + struct stat statbuf; + struct file_key fk; + int ret, fd = -1; + + ret = stat(path, &statbuf); + if (ret < 0) { + /* + * This is not necessarily an error as we sometimes try to open + * files to see if they exist. Log the error as DEBUG severity + * level. + */ + BT_LOGD_ERRNO("Failed to stat file", ": path=%s", path); + goto end; + } + + /* + * Use the device number and inode number to uniquely identify a file. + * Even if the file has the same path, it may have been replaced so we + * must open a new FD for it. This replacement of file is more likely + * to happen with a lttng-live source component. + */ + fk.dev = statbuf.st_dev; + fk.ino = statbuf.st_ino; + + fd_internal = g_hash_table_lookup(fdc->cache, &fk); + if (!fd_internal) { + struct file_key *file_key; + + fd = open(path, O_RDONLY); + if (fd < 0) { + BT_LOGE_ERRNO("Failed to open file", "path=%s", path); + goto error; + } + + fd_internal = g_new0(struct fd_handle_internal, 1); + if (!fd_internal) { + BT_LOGE("Failed to allocate fd internal handle"); + goto error; + } + + file_key = g_new0(struct file_key, 1); + if (!fd_internal) { + BT_LOGE("Failed to allocate file key"); + goto error; + } + + *file_key = fk; + + fd_internal->fd_handle.fd = fd; + fd_internal->ref_count = 0; + fd_internal->key = file_key; + + /* Insert the newly created fd handle. */ + g_hash_table_insert(fdc->cache, fd_internal->key, fd_internal); + } + + fd_internal->ref_count++; + goto end; + +error: + /* + * Close file descriptor if it was open() and we are currently on error + * path. + */ + if (fd != -1) { + ret = close(fd); + if (ret) { + BT_LOGE_ERRNO("Failed to close file descriptor", + ": fd=%i, path=%s", fd, path); + } + } + + fd_cache_handle_internal_destroy(fd_internal); + fd_internal = NULL; +end: + return (struct bt_fd_cache_handle *) fd_internal; +} + +BT_HIDDEN +void bt_fd_cache_put_handle(struct bt_fd_cache *fdc, + struct bt_fd_cache_handle *handle) +{ + struct fd_handle_internal *fd_internal; + + if (!handle) { + goto end; + } + + fd_internal = (struct fd_handle_internal *) handle; + + BT_ASSERT(fd_internal->ref_count > 0); + + if (fd_internal->ref_count > 1) { + fd_internal->ref_count--; + } else { + gboolean ret; + int close_ret; + + close_ret = close(fd_internal->fd_handle.fd); + if (close_ret == -1) { + BT_LOGW_ERRNO("Failed to close file descriptor", + ": fd=%d", fd_internal->fd_handle.fd); + } + ret = g_hash_table_remove(fdc->cache, fd_internal->key); + BT_ASSERT(ret); + } + +end: + return; +} diff --git a/src/fd-cache/fd-cache.h b/src/fd-cache/fd-cache.h new file mode 100644 index 00000000..fb70a076 --- /dev/null +++ b/src/fd-cache/fd-cache.h @@ -0,0 +1,61 @@ +#ifndef BABELTRACE_FD_CACHE_INTERNAL_H +#define BABELTRACE_FD_CACHE_INTERNAL_H +/* + * fd-cache.h + * + * Babeltrace - File descriptor cache + * + * Copyright 2019 Francis Deslauriers + * + * Author: Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" + +struct bt_fd_cache_handle { + int fd; +}; + +struct bt_fd_cache { + GHashTable *cache; +}; + +static inline +int bt_fd_cache_handle_get_fd(struct bt_fd_cache_handle *handle) +{ + return handle->fd; +} + +BT_HIDDEN +int bt_fd_cache_init(struct bt_fd_cache *fdc); + +BT_HIDDEN +void bt_fd_cache_fini(struct bt_fd_cache *fdc); + +BT_HIDDEN +struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc, + const char *path); + +BT_HIDDEN +void bt_fd_cache_put_handle(struct bt_fd_cache *fdc, + struct bt_fd_cache_handle *handle); + +#endif /* BABELTRACE_FD_CACHE_INTERNAL_H */ diff --git a/src/fd-cache/logging.c b/src/fd-cache/logging.c new file mode 100644 index 00000000..875f3bae --- /dev/null +++ b/src/fd-cache/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_fd_cache_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_fd_cache_log_level, "BABELTRACE_FD_CACHE_LOG_LEVEL"); diff --git a/src/fd-cache/logging.h b/src/fd-cache/logging.h new file mode 100644 index 00000000..89a783b9 --- /dev/null +++ b/src/fd-cache/logging.h @@ -0,0 +1,31 @@ +#ifndef FD_CACHE_LOGGING_H +#define FD_CACHE_LOGGING_H + +/* + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_fd_cache_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_fd_cache_log_level); + +#endif /* FD_CACHE_LOGGING_H */ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 00000000..51da351b --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,33 @@ +SUBDIRS = trace-ir prio-heap plugin graph + +lib_LTLIBRARIES = libbabeltrace2.la + +libbabeltrace2_la_SOURCES = \ + assert-pre.h \ + babeltrace2.c \ + lib-logging.c \ + lib-logging.h \ + logging.c \ + object.h \ + object-pool.c \ + object-pool.h \ + property.h \ + util.c \ + value.c \ + value.h + +libbabeltrace2_la_LDFLAGS = $(LT_NO_UNDEFINED) \ + -version-info $(BABELTRACE_LIBRARY_VERSION) + +libbabeltrace2_la_LIBADD = \ + prio-heap/libprio-heap.la \ + graph/libgraph.la \ + plugin/libplugin.la \ + trace-ir/libtrace-ir.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/compat/libcompat.la + +if ENABLE_BUILT_IN_PYTHON_PLUGIN_SUPPORT +libbabeltrace2_la_LIBADD += $(top_builddir)/src/python-plugin-provider/libbabeltrace2-python-plugin-provider.la +endif diff --git a/src/lib/assert-pre.h b/src/lib/assert-pre.h new file mode 100644 index 00000000..6264c8ac --- /dev/null +++ b/src/lib/assert-pre.h @@ -0,0 +1,129 @@ +#ifndef BABELTRACE_ASSERT_PRE_INTERNAL_H +#define BABELTRACE_ASSERT_PRE_INTERNAL_H + +/* + * Copyright (c) 2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * The macros in this header use macros defined in + * . We don't want this header to + * automatically include because you + * need to manually define BT_LOG_TAG before including + * and it is unexpected that you + * also need to define it before including this header. + * + * This is a reminder that in order to use + * , you also need to use logging + * explicitly. + */ + +#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H +# error Include before this header. +#endif + +#include +#include +#include "common/babeltrace.h" + +#ifdef BT_DEV_MODE +/* + * Asserts that the library precondition _cond is satisfied. + * + * If _cond is false, log a fatal statement using _fmt and the optional + * arguments using BT_LIB_LOGF(), and abort. + * + * To assert that a postcondition is satisfied or that some internal + * object/context/value is in the expected state, use BT_ASSERT(). + */ +# define BT_ASSERT_PRE(_cond, _fmt, ...) \ + do { \ + if (!(_cond)) { \ + BT_LOGF_STR("Library precondition not satisfied; error is:"); \ + BT_LIB_LOGF((_fmt), ##__VA_ARGS__); \ + BT_LOGF_STR("Aborting..."); \ + abort(); \ + } \ + } while (0) + +/* + * Marks a function as being only used within a BT_ASSERT_PRE() context. + */ +# define BT_ASSERT_PRE_FUNC + +/* + * Prints the details of an unsatisfied precondition without immediately + * aborting. You should use this within a function which checks + * preconditions, but which is called from a BT_ASSERT_PRE() context, so + * that the function can still return its result for BT_ASSERT_PRE() to + * evaluate it. + * + * Example: + * + * BT_ASSERT_PRE_FUNC + * static inline bool check_complex_precond(...) + * { + * ... + * + * if (...) { + * BT_ASSERT_PRE_MSG("Invalid object: ...", ...); + * return false; + * } + * + * ... + * } + * + * ... + * + * BT_ASSERT_PRE(check_complex_precond(...), + * "Precondition is not satisfied: ...", ...); + */ +# define BT_ASSERT_PRE_MSG BT_LIB_LOGF +#else +# define BT_ASSERT_PRE(_cond, _fmt, ...) ((void) sizeof((void) (_cond), 0)) +# define BT_ASSERT_PRE_FUNC BT_UNUSED +# define BT_ASSERT_PRE_MSG(_fmt, ...) +#endif /* BT_DEV_MODE */ + +/* + * Developer mode: asserts that a given variable is not NULL. + */ +#define BT_ASSERT_PRE_NON_NULL(_obj, _obj_name) \ + BT_ASSERT_PRE((_obj) != NULL, "%s is NULL: ", _obj_name) + +/* + * Developer mode: asserts that a given object is NOT frozen. This macro + * checks the `frozen` field of _obj. + */ +#define BT_ASSERT_PRE_HOT(_obj, _obj_name, _fmt, ...) \ + BT_ASSERT_PRE(!(_obj)->frozen, "%s is frozen" _fmt, _obj_name, \ + ##__VA_ARGS__) + +/* + * Developer mode: asserts that a given index is less than a given size. + */ +#define BT_ASSERT_PRE_VALID_INDEX(_index, _length) \ + BT_ASSERT_PRE((_index) < (_length), \ + "Index is out of bounds: index=%" PRIu64 ", " \ + "count=%" PRIu64, (uint64_t) (_index), (uint64_t) (_length)) + +#endif /* BABELTRACE_ASSERT_PRE_INTERNAL_H */ diff --git a/src/lib/babeltrace2.c b/src/lib/babeltrace2.c new file mode 100644 index 00000000..4a74214a --- /dev/null +++ b/src/lib/babeltrace2.c @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation + * + * Author: Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +int bt_version_get_major(void) +{ + return BT_VERSION_MAJOR; +} + +int bt_version_get_minor(void) +{ + return BT_VERSION_MINOR; +} + +int bt_version_get_patch(void) { + return BT_VERSION_PATCH; +} + +const char *bt_version_get_extra(void) +{ + return BT_VERSION_EXTRA; +} diff --git a/src/lib/graph/Makefile.am b/src/lib/graph/Makefile.am new file mode 100644 index 00000000..a7376fb2 --- /dev/null +++ b/src/lib/graph/Makefile.am @@ -0,0 +1,30 @@ +SUBDIRS = message + +noinst_LTLIBRARIES = libgraph.la + +# Graph library +libgraph_la_SOURCES = \ + component.c \ + component-class.c \ + component-class.h \ + component-class-sink-colander.c \ + component-class-sink-colander.h \ + component-filter.c \ + component-filter.h \ + component.h \ + component-sink.c \ + component-sink.h \ + component-source.c \ + component-source.h \ + connection.c \ + connection.h \ + graph.c \ + graph.h \ + iterator.c \ + port.c \ + port.h \ + query-executor.c \ + query-executor.h + +libgraph_la_LIBADD = \ + message/libgraph-message.la diff --git a/src/lib/graph/component-class-sink-colander.c b/src/lib/graph/component-class-sink-colander.c new file mode 100644 index 00000000..667b997c --- /dev/null +++ b/src/lib/graph/component-class-sink-colander.c @@ -0,0 +1,200 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COLANDER" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "lib/object.h" +#include +#include +#include +#include +#include +#include + +#include "component-class-sink-colander.h" + +static +struct bt_component_class_sink *colander_comp_cls; + +static +enum bt_self_component_status colander_init( + struct bt_self_component_sink *self_comp, + const struct bt_value *params, void *init_method_data) +{ + enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct bt_component_class_sink_colander_priv_data *colander_data = NULL; + struct bt_component_class_sink_colander_data *user_provided_data = + init_method_data; + + if (!init_method_data) { + BT_LOGW_STR("Component initialization method data is NULL."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + colander_data = g_new0( + struct bt_component_class_sink_colander_priv_data, 1); + if (!colander_data) { + BT_LOGE_STR("Failed to allocate colander data."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + colander_data->msgs = user_provided_data->msgs; + colander_data->count_addr = user_provided_data->count_addr; + status = bt_self_component_sink_add_input_port(self_comp, "in", + NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGE_STR("Cannot add input port."); + goto end; + } + + bt_self_component_set_data( + bt_self_component_sink_as_self_component(self_comp), + colander_data); + +end: + return status; +} + +static +void colander_finalize(struct bt_self_component_sink *self_comp) +{ + struct bt_component_class_sink_colander_priv_data *colander_data = + bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + if (!colander_data) { + return; + } + + BT_OBJECT_PUT_REF_AND_RESET(colander_data->msg_iter); + g_free(colander_data); +} + +static +enum bt_self_component_status colander_graph_is_configured( + bt_self_component_sink *self_comp) +{ + enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct bt_component_class_sink_colander_priv_data *colander_data = + bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + struct bt_self_component_port_input *self_port = + bt_self_component_sink_borrow_input_port_by_name(self_comp, "in"); + BT_ASSERT(self_port); + + BT_ASSERT(colander_data); + BT_OBJECT_PUT_REF_AND_RESET(colander_data->msg_iter); + colander_data->msg_iter = + bt_self_component_port_input_message_iterator_create( + self_port); + if (!colander_data->msg_iter) { + BT_LIB_LOGE("Cannot create message iterator on " + "self component input port: %![port-]+p", + self_port); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + +end: + return status; +} + +static +enum bt_self_component_status colander_consume( + struct bt_self_component_sink *self_comp) +{ + enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + enum bt_message_iterator_status msg_iter_status; + struct bt_component_class_sink_colander_priv_data *colander_data = + bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + bt_message_array_const msgs; + + BT_ASSERT(colander_data); + + if (!colander_data->msg_iter) { + BT_LIB_LOGW("Trying to consume without an " + "upstream message iterator: %![comp-]+c", + self_comp); + goto end; + } + + msg_iter_status = + bt_self_component_port_input_message_iterator_next( + colander_data->msg_iter, &msgs, + colander_data->count_addr); + switch (msg_iter_status) { + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + status = BT_SELF_COMPONENT_STATUS_AGAIN; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_END: + status = BT_SELF_COMPONENT_STATUS_END; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_OK: + /* Move messages to user (count already set) */ + memcpy(colander_data->msgs, msgs, + sizeof(*msgs) * *colander_data->count_addr); + break; + default: + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +struct bt_component_class_sink *bt_component_class_sink_colander_get(void) +{ + if (colander_comp_cls) { + goto end; + } + + colander_comp_cls = bt_component_class_sink_create("colander", + colander_consume); + if (!colander_comp_cls) { + BT_LOGE_STR("Cannot create sink colander component class."); + goto end; + } + + (void) bt_component_class_sink_set_init_method( + colander_comp_cls, colander_init); + (void) bt_component_class_sink_set_finalize_method( + colander_comp_cls, colander_finalize); + (void) bt_component_class_sink_set_graph_is_configured_method( + colander_comp_cls, colander_graph_is_configured); + +end: + bt_object_get_ref(colander_comp_cls); + return (void *) colander_comp_cls; +} + +__attribute__((destructor)) static +void put_colander(void) { + BT_OBJECT_PUT_REF_AND_RESET(colander_comp_cls); +} diff --git a/src/lib/graph/component-class-sink-colander.h b/src/lib/graph/component-class-sink-colander.h new file mode 100644 index 00000000..faa7a6eb --- /dev/null +++ b/src/lib/graph/component-class-sink-colander.h @@ -0,0 +1,52 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H +#define BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_component_class_sink_colander_priv_data { + bt_message_array_const msgs; + uint64_t *count_addr; + struct bt_self_component_port_input_message_iterator *msg_iter; +}; + +struct bt_component_class_sink_colander_data { + bt_message_array_const msgs; + uint64_t *count_addr; +}; + +extern struct bt_component_class_sink * +bt_component_class_sink_colander_get(void); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_SINK_COLANDER_H */ diff --git a/src/lib/graph/component-class.c b/src/lib/graph/component-class.c new file mode 100644 index 00000000..87bdb936 --- /dev/null +++ b/src/lib/graph/component-class.c @@ -0,0 +1,796 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMP-CLASS" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "component-class.h" + +#define BT_ASSERT_PRE_COMP_CLS_HOT(_cc) \ + BT_ASSERT_PRE_HOT(((const struct bt_component_class *) (_cc)), \ + "Component class", ": %!+C", (_cc)) + +static +void destroy_component_class(struct bt_object *obj) +{ + struct bt_component_class *class; + int i; + + BT_ASSERT(obj); + class = container_of(obj, struct bt_component_class, base); + + BT_LIB_LOGD("Destroying component class: %!+C", class); + + /* Call destroy listeners in reverse registration order */ + for (i = class->destroy_listeners->len - 1; i >= 0; i--) { + struct bt_component_class_destroy_listener *listener = + &g_array_index(class->destroy_listeners, + struct bt_component_class_destroy_listener, + i); + + BT_LOGD("Calling destroy listener: func-addr=%p, data-addr=%p", + listener->func, listener->data); + listener->func(class, listener->data); + } + + if (class->name) { + g_string_free(class->name, TRUE); + class->name = NULL; + } + + if (class->description) { + g_string_free(class->description, TRUE); + class->description = NULL; + } + + if (class->help) { + g_string_free(class->help, TRUE); + class->help = NULL; + } + + if (class->destroy_listeners) { + g_array_free(class->destroy_listeners, TRUE); + class->destroy_listeners = NULL; + } + + g_free(class); +} + +static +int bt_component_class_init(struct bt_component_class *class, + enum bt_component_class_type type, const char *name) +{ + int ret = 0; + + bt_object_init_shared(&class->base, destroy_component_class); + class->type = type; + class->name = g_string_new(name); + if (!class->name) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + class->description = g_string_new(NULL); + if (!class->description) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + class->help = g_string_new(NULL); + if (!class->help) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + class->destroy_listeners = g_array_new(FALSE, TRUE, + sizeof(struct bt_component_class_destroy_listener)); + if (!class->destroy_listeners) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(class); + ret = -1; + +end: + return ret; +} + +struct bt_component_class_source *bt_component_class_source_create( + const char *name, + bt_component_class_source_message_iterator_next_method method) +{ + struct bt_component_class_source *source_class = NULL; + int ret; + + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(method, "Message iterator next method"); + BT_LOGD("Creating source component class: " + "name=\"%s\", msg-iter-next-method-addr=%p", + name, method); + source_class = g_new0(struct bt_component_class_source, 1); + if (!source_class) { + BT_LOGE_STR("Failed to allocate one source component class."); + goto end; + } + + /* bt_component_class_init() logs errors */ + ret = bt_component_class_init(&source_class->parent, + BT_COMPONENT_CLASS_TYPE_SOURCE, name); + if (ret) { + /* + * If bt_component_class_init() fails, the component + * class is put, therefore its memory is already + * freed. + */ + source_class = NULL; + goto end; + } + + source_class->methods.msg_iter_next = method; + BT_LIB_LOGD("Created source component class: %!+C", source_class); + +end: + return (void *) source_class; +} + +struct bt_component_class_filter *bt_component_class_filter_create( + const char *name, + bt_component_class_filter_message_iterator_next_method method) +{ + struct bt_component_class_filter *filter_class = NULL; + int ret; + + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(method, "Message iterator next method"); + BT_LOGD("Creating filter component class: " + "name=\"%s\", msg-iter-next-method-addr=%p", + name, method); + filter_class = g_new0(struct bt_component_class_filter, 1); + if (!filter_class) { + BT_LOGE_STR("Failed to allocate one filter component class."); + goto end; + } + + /* bt_component_class_init() logs errors */ + ret = bt_component_class_init(&filter_class->parent, + BT_COMPONENT_CLASS_TYPE_FILTER, name); + if (ret) { + /* + * If bt_component_class_init() fails, the component + * class is put, therefore its memory is already + * freed. + */ + filter_class = NULL; + goto end; + } + + filter_class->methods.msg_iter_next = method; + BT_LIB_LOGD("Created filter component class: %!+C", filter_class); + +end: + return (void *) filter_class; +} + +struct bt_component_class_sink *bt_component_class_sink_create( + const char *name, bt_component_class_sink_consume_method method) +{ + struct bt_component_class_sink *sink_class = NULL; + int ret; + + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(method, "Consume next method"); + BT_LOGD("Creating sink component class: " + "name=\"%s\", consume-method-addr=%p", + name, method); + sink_class = g_new0(struct bt_component_class_sink, 1); + if (!sink_class) { + BT_LOGE_STR("Failed to allocate one sink component class."); + goto end; + } + + /* bt_component_class_init() logs errors */ + ret = bt_component_class_init(&sink_class->parent, + BT_COMPONENT_CLASS_TYPE_SINK, name); + if (ret) { + /* + * If bt_component_class_init() fails, the component + * class is put, therefore its memory is already + * freed. + */ + sink_class = NULL; + goto end; + } + + sink_class->methods.consume = method; + BT_LIB_LOGD("Created sink component class: %!+C", sink_class); + +end: + return (void *) sink_class; +} + +enum bt_component_class_status +bt_component_class_source_set_init_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_init_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.init = method; + BT_LIB_LOGV("Set source component class's initialization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_init_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_init_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.init = method; + BT_LIB_LOGV("Set filter component class's initialization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_init_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_init_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.init = method; + BT_LIB_LOGV("Set sink component class's initialization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_finalize_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_finalize_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.finalize = method; + BT_LIB_LOGV("Set source component class's finalization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_finalize_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_finalize_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.finalize = method; + BT_LIB_LOGV("Set filter component class's finalization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_finalize_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_finalize_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.finalize = method; + BT_LIB_LOGV("Set sink component class's finalization method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_query_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_query_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.query = method; + BT_LIB_LOGV("Set source component class's query method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_query_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_query_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.query = method; + BT_LIB_LOGV("Set filter component class's query method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_query_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_query_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.query = method; + BT_LIB_LOGV("Set sink component class's query method: " + "%!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_accept_input_port_connection_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_accept_input_port_connection_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.accept_input_port_connection = method; + BT_LIB_LOGV("Set filter component class's \"accept input port connection\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_accept_input_port_connection_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_accept_input_port_connection_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.accept_input_port_connection = method; + BT_LIB_LOGV("Set sink component class's \"accept input port connection\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_accept_output_port_connection_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_accept_output_port_connection_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.accept_output_port_connection = method; + BT_LIB_LOGV("Set source component class's \"accept output port connection\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_accept_output_port_connection_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_accept_output_port_connection_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.accept_output_port_connection = method; + BT_LIB_LOGV("Set filter component class's \"accept output port connection\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_input_port_connected_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_input_port_connected_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.input_port_connected = method; + BT_LIB_LOGV("Set filter component class's \"input port connected\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_input_port_connected_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_input_port_connected_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.input_port_connected = method; + BT_LIB_LOGV("Set sink component class's \"input port connected\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_output_port_connected_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_output_port_connected_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.output_port_connected = method; + BT_LIB_LOGV("Set source component class's \"output port connected\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_output_port_connected_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_output_port_connected_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.output_port_connected = method; + BT_LIB_LOGV("Set filter component class's \"output port connected\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_sink_set_graph_is_configured_method( + struct bt_component_class_sink *comp_cls, + bt_component_class_sink_graph_is_configured_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.graph_is_configured = method; + BT_LIB_LOGV("Set sink component class's \"graph is configured\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +int bt_component_class_source_set_message_iterator_init_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_init_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_init = method; + BT_LIB_LOGV("Set source component class's message iterator initialization method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_init_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_init_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_init = method; + BT_LIB_LOGV("Set filter component class's message iterator initialization method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_message_iterator_finalize_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_finalize_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_finalize = method; + BT_LIB_LOGV("Set source component class's message iterator finalization method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_finalize_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_finalize_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_finalize = method; + BT_LIB_LOGV("Set filter component class's message iterator finalization method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_seek_ns_from_origin_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_seek_ns_from_origin = method; + BT_LIB_LOGV("Set filter component class's message iterator \"seek nanoseconds from origin\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_seek_ns_from_origin_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_seek_ns_from_origin = method; + BT_LIB_LOGV("Set source component class's message iterator \"seek nanoseconds from origin\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_seek_beginning_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_seek_beginning_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_seek_beginning = method; + BT_LIB_LOGV("Set filter component class's message iterator \"seek beginning\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_message_iterator_seek_beginning_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_seek_beginning_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_seek_beginning = method; + BT_LIB_LOGV("Set source component class's message iterator \"seek beginning\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_can_seek_beginning_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_can_seek_beginning_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_can_seek_beginning = method; + BT_LIB_LOGV("Set filter component class's message iterator \"can seek beginning\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_message_iterator_can_seek_beginning_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_can_seek_beginning_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_can_seek_beginning = method; + BT_LIB_LOGV("Set source component class's message iterator \"can seek beginning\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( + struct bt_component_class_filter *comp_cls, + bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_can_seek_ns_from_origin = method; + BT_LIB_LOGV("Set filter component class's message iterator \"can seek nanoseconds from origin\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +enum bt_component_class_status +bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( + struct bt_component_class_source *comp_cls, + bt_component_class_source_message_iterator_can_seek_ns_from_origin_method method) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(method, "Method"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + comp_cls->methods.msg_iter_can_seek_ns_from_origin = method; + BT_LIB_LOGV("Set source component class's message iterator \"can seek nanoseconds from origin\" method" + ": %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +bt_component_class_status bt_component_class_set_description( + struct bt_component_class *comp_cls, + const char *description) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(description, "Description"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + g_string_assign(comp_cls->description, description); + BT_LIB_LOGV("Set component class's description: " + "addr=%p, name=\"%s\", type=%s", + comp_cls, + bt_component_class_get_name(comp_cls), + bt_component_class_type_string(comp_cls->type)); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +bt_component_class_status bt_component_class_set_help( + struct bt_component_class *comp_cls, + const char *help) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(help, "Help"); + BT_ASSERT_PRE_COMP_CLS_HOT(comp_cls); + g_string_assign(comp_cls->help, help); + BT_LIB_LOGV("Set component class's help text: %!+C", comp_cls); + return BT_COMPONENT_CLASS_STATUS_OK; +} + +const char *bt_component_class_get_name(const struct bt_component_class *comp_cls) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return comp_cls->name->str; +} + +enum bt_component_class_type bt_component_class_get_type( + const struct bt_component_class *comp_cls) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return comp_cls->type; +} + +const char *bt_component_class_get_description( + const struct bt_component_class *comp_cls) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return comp_cls->description && + comp_cls->description->str[0] != '\0' ? + comp_cls->description->str : NULL; +} + +const char *bt_component_class_get_help( + const struct bt_component_class *comp_cls) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return comp_cls->help && + comp_cls->help->str[0] != '\0' ? comp_cls->help->str : NULL; +} + +BT_HIDDEN +void bt_component_class_add_destroy_listener( + struct bt_component_class *comp_cls, + bt_component_class_destroy_listener_func func, void *data) +{ + struct bt_component_class_destroy_listener listener; + + BT_ASSERT(comp_cls); + BT_ASSERT(func); + listener.func = func; + listener.data = data; + g_array_append_val(comp_cls->destroy_listeners, listener); + BT_LIB_LOGV("Added destroy listener to component class: " + "%![cc-]+C, listener-func-addr=%p", comp_cls, func); +} + +BT_HIDDEN +void _bt_component_class_freeze(const struct bt_component_class *comp_cls) +{ + BT_ASSERT(comp_cls); + BT_LIB_LOGD("Freezing component class: %!+C", comp_cls); + ((struct bt_component_class *) comp_cls)->frozen = true; +} + +void bt_component_class_get_ref( + const struct bt_component_class *component_class) +{ + bt_object_get_ref(component_class); +} + +void bt_component_class_put_ref( + const struct bt_component_class *component_class) +{ + bt_object_put_ref(component_class); +} + +void bt_component_class_source_get_ref( + const struct bt_component_class_source *component_class_source) +{ + bt_object_get_ref(component_class_source); +} + +void bt_component_class_source_put_ref( + const struct bt_component_class_source *component_class_source) +{ + bt_object_put_ref(component_class_source); +} + +void bt_component_class_filter_get_ref( + const struct bt_component_class_filter *component_class_filter) +{ + bt_object_get_ref(component_class_filter); +} + +void bt_component_class_filter_put_ref( + const struct bt_component_class_filter *component_class_filter) +{ + bt_object_put_ref(component_class_filter); +} + +void bt_component_class_sink_get_ref( + const struct bt_component_class_sink *component_class_sink) +{ + bt_object_get_ref(component_class_sink); +} + +void bt_component_class_sink_put_ref( + const struct bt_component_class_sink *component_class_sink) +{ + bt_object_put_ref(component_class_sink); +} diff --git a/src/lib/graph/component-class.h b/src/lib/graph/component-class.h new file mode 100644 index 00000000..1c49a769 --- /dev/null +++ b/src/lib/graph/component-class.h @@ -0,0 +1,145 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H +#define BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "common/babeltrace.h" +#include "lib/object.h" +#include "common/list.h" +#include +#include + +struct bt_component_class; +struct bt_plugin_so_shared_lib_handle; + +typedef void (*bt_component_class_destroy_listener_func)( + struct bt_component_class *class, void *data); + +struct bt_component_class_destroy_listener { + bt_component_class_destroy_listener_func func; + void *data; +}; + +struct bt_component_class { + struct bt_object base; + enum bt_component_class_type type; + GString *name; + GString *description; + GString *help; + + /* Array of struct bt_component_class_destroy_listener */ + GArray *destroy_listeners; + bool frozen; + struct bt_list_head node; + struct bt_plugin_so_shared_lib_handle *so_handle; +}; + +struct bt_component_class_source { + struct bt_component_class parent; + struct { + bt_component_class_source_init_method init; + bt_component_class_source_finalize_method finalize; + bt_component_class_source_message_iterator_init_method msg_iter_init; + bt_component_class_source_message_iterator_finalize_method msg_iter_finalize; + bt_component_class_source_message_iterator_next_method msg_iter_next; + bt_component_class_source_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; + bt_component_class_source_message_iterator_seek_beginning_method msg_iter_seek_beginning; + bt_component_class_source_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; + bt_component_class_source_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; + bt_component_class_source_query_method query; + bt_component_class_source_accept_output_port_connection_method accept_output_port_connection; + bt_component_class_source_output_port_connected_method output_port_connected; + } methods; +}; + +struct bt_component_class_sink { + struct bt_component_class parent; + struct { + bt_component_class_sink_init_method init; + bt_component_class_sink_finalize_method finalize; + bt_component_class_sink_query_method query; + bt_component_class_sink_accept_input_port_connection_method accept_input_port_connection; + bt_component_class_sink_input_port_connected_method input_port_connected; + bt_component_class_sink_graph_is_configured_method graph_is_configured; + bt_component_class_sink_consume_method consume; + } methods; +}; + +struct bt_component_class_filter { + struct bt_component_class parent; + struct { + bt_component_class_filter_init_method init; + bt_component_class_filter_finalize_method finalize; + bt_component_class_filter_message_iterator_init_method msg_iter_init; + bt_component_class_filter_message_iterator_finalize_method msg_iter_finalize; + bt_component_class_filter_message_iterator_next_method msg_iter_next; + bt_component_class_filter_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; + bt_component_class_filter_message_iterator_seek_beginning_method msg_iter_seek_beginning; + bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; + bt_component_class_filter_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; + bt_component_class_filter_query_method query; + bt_component_class_filter_accept_input_port_connection_method accept_input_port_connection; + bt_component_class_filter_accept_output_port_connection_method accept_output_port_connection; + bt_component_class_filter_input_port_connected_method input_port_connected; + bt_component_class_filter_output_port_connected_method output_port_connected; + } methods; +}; + +BT_HIDDEN +void bt_component_class_add_destroy_listener(struct bt_component_class *class, + bt_component_class_destroy_listener_func func, void *data); + +BT_HIDDEN +void _bt_component_class_freeze( + const struct bt_component_class *component_class); + +#ifdef BT_DEV_MODE +# define bt_component_class_freeze _bt_component_class_freeze +#else +# define bt_component_class_freeze(_cc) +#endif + +static inline +const char *bt_component_class_type_string(enum bt_component_class_type type) +{ + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + return "BT_COMPONENT_CLASS_TYPE_SOURCE"; + case BT_COMPONENT_CLASS_TYPE_SINK: + return "BT_COMPONENT_CLASS_TYPE_SINK"; + case BT_COMPONENT_CLASS_TYPE_FILTER: + return "BT_COMPONENT_CLASS_TYPE_FILTER"; + default: + return "(unknown)"; + } +} + +#endif /* BABELTRACE_GRAPH_COMPONENT_CLASS_INTERNAL_H */ diff --git a/src/lib/graph/component-filter.c b/src/lib/graph/component-filter.c new file mode 100644 index 00000000..ab48fb75 --- /dev/null +++ b/src/lib/graph/component-filter.c @@ -0,0 +1,218 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMP-FILTER" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include +#include +#include + +#include "component-filter.h" +#include "component.h" +#include "component-class.h" + +BT_HIDDEN +void bt_component_filter_destroy(struct bt_component *component) +{ +} + +BT_HIDDEN +struct bt_component *bt_component_filter_create( + const struct bt_component_class *class) +{ + struct bt_component_filter *filter = NULL; + + filter = g_new0(struct bt_component_filter, 1); + if (!filter) { + BT_LOGE_STR("Failed to allocate one filter component."); + goto end; + } + +end: + return (void *) filter; +} + +const bt_component_class_filter * +bt_component_filter_borrow_class_const( + const bt_component_filter *component) +{ + struct bt_component_class *cls; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + + cls = component->parent.class; + + BT_ASSERT(cls); + BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_FILTER); + + return (bt_component_class_filter *) cls; +} + +uint64_t bt_component_filter_get_output_port_count( + const struct bt_component_filter *comp) +{ + return bt_component_get_output_port_count((void *) comp); +} + +const struct bt_port_output * +bt_component_filter_borrow_output_port_by_name_const( + const struct bt_component_filter *comp, const char *name) +{ + return bt_component_borrow_output_port_by_name( + (void *) comp, name); +} + +struct bt_self_component_port_output * +bt_self_component_filter_borrow_output_port_by_name( + struct bt_self_component_filter *comp, const char *name) +{ + return (void *) bt_component_borrow_output_port_by_name( + (void *) comp, name); +} + +const struct bt_port_output * +bt_component_filter_borrow_output_port_by_index_const( + const struct bt_component_filter *comp, uint64_t index) +{ + return bt_component_borrow_output_port_by_index( + (void *) comp, index); +} + +struct bt_self_component_port_output * +bt_self_component_filter_borrow_output_port_by_index( + struct bt_self_component_filter *comp, uint64_t index) +{ + return (void *) bt_component_borrow_output_port_by_index( + (void *) comp, index); +} + +enum bt_self_component_status bt_self_component_filter_add_output_port( + struct bt_self_component_filter *self_comp, + const char *name, void *user_data, + struct bt_self_component_port_output **self_port) +{ + struct bt_component *comp = (void *) self_comp; + enum bt_self_component_status status; + struct bt_port *port = NULL; + + /* bt_component_add_output_port() logs details and errors */ + status = bt_component_add_output_port(comp, name, user_data, &port); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + if (self_port) { + /* Move reference to user */ + *self_port = (void *) port; + port = NULL; + } + +end: + bt_object_put_ref(port); + return status; +} + +uint64_t bt_component_filter_get_input_port_count( + const struct bt_component_filter *component) +{ + /* bt_component_get_input_port_count() logs details/errors */ + return bt_component_get_input_port_count((void *) component); +} + +const struct bt_port_input *bt_component_filter_borrow_input_port_by_name_const( + const struct bt_component_filter *component, const char *name) +{ + /* bt_component_borrow_input_port_by_name() logs details/errors */ + return bt_component_borrow_input_port_by_name( + (void *) component, name); +} + +struct bt_self_component_port_input * +bt_self_component_filter_borrow_input_port_by_name( + struct bt_self_component_filter *component, const char *name) +{ + /* bt_component_borrow_input_port_by_name() logs details/errors */ + return (void *) bt_component_borrow_input_port_by_name( + (void *) component, name); +} + +const struct bt_port_input * +bt_component_filter_borrow_input_port_by_index_const( + const struct bt_component_filter *component, uint64_t index) +{ + /* bt_component_borrow_input_port_by_index() logs details/errors */ + return bt_component_borrow_input_port_by_index( + (void *) component, index); +} + +struct bt_self_component_port_input * +bt_self_component_filter_borrow_input_port_by_index( + struct bt_self_component_filter *component, uint64_t index) +{ + /* bt_component_borrow_input_port_by_index() logs details/errors */ + return (void *) bt_component_borrow_input_port_by_index( + (void *) component, index); +} + +enum bt_self_component_status bt_self_component_filter_add_input_port( + struct bt_self_component_filter *self_comp, + const char *name, void *user_data, + struct bt_self_component_port_input **self_port) +{ + enum bt_self_component_status status; + struct bt_port *port = NULL; + struct bt_component *comp = (void *) self_comp; + + /* bt_component_add_input_port() logs details/errors */ + status = bt_component_add_input_port(comp, name, user_data, &port); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + if (self_port) { + /* Move reference to user */ + *self_port = (void *) port; + port = NULL; + } + +end: + bt_object_put_ref(port); + return status; +} + +void bt_component_filter_get_ref( + const struct bt_component_filter *component_filter) +{ + bt_object_get_ref(component_filter); +} + +void bt_component_filter_put_ref( + const struct bt_component_filter *component_filter) +{ + bt_object_put_ref(component_filter); +} diff --git a/src/lib/graph/component-filter.h b/src/lib/graph/component-filter.h new file mode 100644 index 00000000..7d5a792e --- /dev/null +++ b/src/lib/graph/component-filter.h @@ -0,0 +1,44 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H +#define BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include + +#include "component-class.h" +#include "component.h" + +struct bt_component_filter { + struct bt_component parent; +}; + +BT_HIDDEN +struct bt_component *bt_component_filter_create( + const struct bt_component_class *class); + +BT_HIDDEN +void bt_component_filter_destroy(struct bt_component *component); + +#endif /* BABELTRACE_GRAPH_COMPONENT_FILTER_INTERNAL_H */ diff --git a/src/lib/graph/component-sink.c b/src/lib/graph/component-sink.c new file mode 100644 index 00000000..59839245 --- /dev/null +++ b/src/lib/graph/component-sink.c @@ -0,0 +1,152 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMP-SINK" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include +#include +#include + +#include "component-sink.h" +#include "component.h" + +BT_HIDDEN +void bt_component_sink_destroy(struct bt_component *component) +{ +} + +BT_HIDDEN +struct bt_component *bt_component_sink_create( + const struct bt_component_class *class) +{ + struct bt_component_sink *sink = NULL; + + sink = g_new0(struct bt_component_sink, 1); + if (!sink) { + BT_LOGE_STR("Failed to allocate one sink component."); + goto end; + } + +end: + return (void *) sink; +} + +const bt_component_class_sink * +bt_component_sink_borrow_class_const( + const bt_component_sink *component) +{ + struct bt_component_class *cls; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + + cls = component->parent.class; + + BT_ASSERT(cls); + BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_SINK); + + return (bt_component_class_sink *) cls; +} + +uint64_t bt_component_sink_get_input_port_count( + const struct bt_component_sink *component) +{ + /* bt_component_get_input_port_count() logs details/errors */ + return bt_component_get_input_port_count((void *) component); +} + +const struct bt_port_input * +bt_component_sink_borrow_input_port_by_name_const( + const struct bt_component_sink *component, const char *name) +{ + /* bt_component_borrow_input_port_by_name() logs details/errors */ + return bt_component_borrow_input_port_by_name((void *) component, name); +} + +struct bt_self_component_port_input * +bt_self_component_sink_borrow_input_port_by_name( + struct bt_self_component_sink *component, const char *name) +{ + /* bt_component_borrow_input_port_by_name() logs details/errors */ + return (void *) bt_component_borrow_input_port_by_name( + (void *) component, name); +} + +const struct bt_port_input *bt_component_sink_borrow_input_port_by_index_const( + const struct bt_component_sink *component, uint64_t index) +{ + /* bt_component_borrow_input_port_by_index() logs details/errors */ + return bt_component_borrow_input_port_by_index( + (void *) component, index); +} + +struct bt_self_component_port_input * +bt_self_component_sink_borrow_input_port_by_index( + struct bt_self_component_sink *component, uint64_t index) +{ + /* bt_component_borrow_input_port_by_index() logs details/errors */ + return (void *) bt_component_borrow_input_port_by_index( + (void *) component, index); +} + +enum bt_self_component_status bt_self_component_sink_add_input_port( + struct bt_self_component_sink *self_comp, + const char *name, void *user_data, + struct bt_self_component_port_input **self_port) +{ + enum bt_self_component_status status; + struct bt_port *port = NULL; + struct bt_component *comp = (void *) self_comp; + + /* bt_component_add_input_port() logs details/errors */ + status = bt_component_add_input_port(comp, name, user_data, &port); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + if (self_port) { + /* Move reference to user */ + *self_port = (void *) port; + port = NULL; + } + +end: + bt_object_put_ref(port); + return status; +} + +void bt_component_sink_get_ref( + const struct bt_component_sink *component_sink) +{ + bt_object_get_ref(component_sink); +} + +void bt_component_sink_put_ref( + const struct bt_component_sink *component_sink) +{ + bt_object_put_ref(component_sink); +} diff --git a/src/lib/graph/component-sink.h b/src/lib/graph/component-sink.h new file mode 100644 index 00000000..a6706351 --- /dev/null +++ b/src/lib/graph/component-sink.h @@ -0,0 +1,46 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H +#define BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "compat/compiler.h" +#include + +#include "component-class.h" +#include "component.h" + +struct bt_component_sink { + struct bt_component parent; + bool graph_is_configured_method_called; +}; + +BT_HIDDEN +struct bt_component *bt_component_sink_create( + const struct bt_component_class *class); + +BT_HIDDEN +void bt_component_sink_destroy(struct bt_component *component); + +#endif /* BABELTRACE_GRAPH_COMPONENT_SINK_INTERNAL_H */ diff --git a/src/lib/graph/component-source.c b/src/lib/graph/component-source.c new file mode 100644 index 00000000..c7c01ab1 --- /dev/null +++ b/src/lib/graph/component-source.c @@ -0,0 +1,149 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMP-SOURCE" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include +#include +#include + +#include "component-source.h" +#include "component.h" +#include "port.h" +#include "message/iterator.h" + +BT_HIDDEN +void bt_component_source_destroy(struct bt_component *component) +{ +} + +BT_HIDDEN +struct bt_component *bt_component_source_create( + const struct bt_component_class *class) +{ + struct bt_component_source *source = NULL; + + source = g_new0(struct bt_component_source, 1); + if (!source) { + BT_LOGE_STR("Failed to allocate one source component."); + goto end; + } + +end: + return (void *) source; +} + +const bt_component_class_source * +bt_component_source_borrow_class_const( + const bt_component_source *component) +{ + struct bt_component_class *cls; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + + cls = component->parent.class; + + BT_ASSERT(cls); + BT_ASSERT(cls->type == BT_COMPONENT_CLASS_TYPE_SOURCE); + + return (bt_component_class_source *) cls; +} + +uint64_t bt_component_source_get_output_port_count( + const struct bt_component_source *comp) +{ + return bt_component_get_output_port_count((void *) comp); +} + +const struct bt_port_output * +bt_component_source_borrow_output_port_by_name_const( + const struct bt_component_source *comp, const char *name) +{ + return bt_component_borrow_output_port_by_name((void *) comp, name); +} + +struct bt_self_component_port_output * +bt_self_component_source_borrow_output_port_by_name( + struct bt_self_component_source *comp, const char *name) +{ + return (void *) bt_component_borrow_output_port_by_name( + (void *) comp, name); +} + +const struct bt_port_output * +bt_component_source_borrow_output_port_by_index_const( + const struct bt_component_source *comp, uint64_t index) +{ + return bt_component_borrow_output_port_by_index((void *) comp, index); +} + +struct bt_self_component_port_output * +bt_self_component_source_borrow_output_port_by_index( + struct bt_self_component_source *comp, uint64_t index) +{ + return (void *) bt_component_borrow_output_port_by_index( + (void *) comp, index); +} + +enum bt_self_component_status bt_self_component_source_add_output_port( + struct bt_self_component_source *self_comp, + const char *name, void *user_data, + struct bt_self_component_port_output **self_port) +{ + struct bt_component *comp = (void *) self_comp; + enum bt_self_component_status status; + struct bt_port *port = NULL; + + /* bt_component_add_output_port() logs details and errors */ + status = bt_component_add_output_port(comp, name, user_data, &port); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + if (self_port) { + /* Move reference to user */ + *self_port = (void *) port; + port = NULL; + } + +end: + bt_object_put_ref(port); + return status; +} + +void bt_component_source_get_ref( + const struct bt_component_source *component_source) +{ + bt_object_get_ref(component_source); +} + +void bt_component_source_put_ref( + const struct bt_component_source *component_source) +{ + bt_object_put_ref(component_source); +} diff --git a/src/lib/graph/component-source.h b/src/lib/graph/component-source.h new file mode 100644 index 00000000..747d760e --- /dev/null +++ b/src/lib/graph/component-source.h @@ -0,0 +1,43 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H +#define BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" + +#include "component-class.h" +#include "component.h" + +struct bt_component_source { + struct bt_component parent; +}; + +BT_HIDDEN +struct bt_component *bt_component_source_create( + const struct bt_component_class *class); + +BT_HIDDEN +void bt_component_source_destroy(struct bt_component *component); + +#endif /* BABELTRACE_GRAPH_COMPONENT_SOURCE_INTERNAL_H */ diff --git a/src/lib/graph/component.c b/src/lib/graph/component.c new file mode 100644 index 00000000..f4e4be1e --- /dev/null +++ b/src/lib/graph/component.c @@ -0,0 +1,685 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "COMP" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "common/babeltrace.h" +#include "compat/compiler.h" +#include +#include +#include "lib/value.h" +#include +#include + +#include "component.h" +#include "component-class.h" +#include "component-source.h" +#include "component-filter.h" +#include "component-sink.h" +#include "connection.h" +#include "graph.h" +#include "message/iterator.h" +#include "port.h" + +static +struct bt_component * (* const component_create_funcs[])( + const struct bt_component_class *) = { + [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_create, + [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_create, + [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_create, +}; + +static +void (*component_destroy_funcs[])(struct bt_component *) = { + [BT_COMPONENT_CLASS_TYPE_SOURCE] = bt_component_source_destroy, + [BT_COMPONENT_CLASS_TYPE_SINK] = bt_component_sink_destroy, + [BT_COMPONENT_CLASS_TYPE_FILTER] = bt_component_filter_destroy, +}; + +static +void finalize_component(struct bt_component *comp) +{ + typedef void (*method_t)(void *); + + method_t method = NULL; + + BT_ASSERT(comp); + + switch (comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_cc = (void *) comp->class; + + method = (method_t) src_cc->methods.finalize; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_cc = (void *) comp->class; + + method = (method_t) flt_cc->methods.finalize; + break; + } + case BT_COMPONENT_CLASS_TYPE_SINK: + { + struct bt_component_class_sink *sink_cc = (void *) comp->class; + + method = (method_t) sink_cc->methods.finalize; + break; + } + default: + abort(); + } + + if (method) { + BT_LIB_LOGD("Calling user's finalization method: " + "%![comp-]+c", comp); + method(comp); + } +} + +static +void destroy_component(struct bt_object *obj) +{ + struct bt_component *component = NULL; + int i; + + if (!obj) { + return; + } + + /* + * The component's reference count is 0 if we're here. Increment + * it to avoid a double-destroy (possibly infinitely recursive). + * This could happen for example if the component's finalization + * function does bt_object_get_ref() (or anything that causes + * bt_object_get_ref() to be called) on itself (ref. count goes + * from 0 to 1), and then bt_object_put_ref(): the reference + * count would go from 1 to 0 again and this function would be + * called again. + */ + obj->ref_count++; + component = container_of(obj, struct bt_component, base); + BT_LIB_LOGD("Destroying component: %![comp-]+c, %![graph-]+g", + component, bt_component_borrow_graph(component)); + + /* Call destroy listeners in reverse registration order */ + BT_LOGD_STR("Calling destroy listeners."); + + for (i = component->destroy_listeners->len - 1; i >= 0; i--) { + struct bt_component_destroy_listener *listener = + &g_array_index(component->destroy_listeners, + struct bt_component_destroy_listener, i); + + listener->func(component, listener->data); + } + + /* + * User data is destroyed first, followed by the concrete + * component instance. Do not finalize if the component's user + * initialization method failed in the first place. + */ + if (component->initialized) { + finalize_component(component); + } + + if (component->destroy) { + BT_LOGD_STR("Destroying type-specific data."); + component->destroy(component); + } + + if (component->input_ports) { + BT_LOGD_STR("Destroying input ports."); + g_ptr_array_free(component->input_ports, TRUE); + component->input_ports = NULL; + } + + if (component->output_ports) { + BT_LOGD_STR("Destroying output ports."); + g_ptr_array_free(component->output_ports, TRUE); + component->output_ports = NULL; + } + + if (component->destroy_listeners) { + g_array_free(component->destroy_listeners, TRUE); + component->destroy_listeners = NULL; + } + + if (component->name) { + g_string_free(component->name, TRUE); + component->name = NULL; + } + + BT_LOGD_STR("Putting component class."); + BT_OBJECT_PUT_REF_AND_RESET(component->class); + g_free(component); +} + +enum bt_component_class_type bt_component_get_class_type( + const struct bt_component *component) +{ + BT_ASSERT_PRE_NON_NULL(component, "Component"); + return component->class->type; +} + +static +enum bt_self_component_status add_port( + struct bt_component *component, GPtrArray *ports, + enum bt_port_type port_type, const char *name, void *user_data, + struct bt_port **port) +{ + struct bt_port *new_port = NULL; + struct bt_graph *graph = NULL; + enum bt_self_component_status status; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE(strlen(name) > 0, "Name is empty"); + graph = bt_component_borrow_graph(component); + BT_ASSERT_PRE(graph && !bt_graph_is_canceled(graph), + "Component's graph is canceled: %![comp-]+c, %![graph-]+g", + component, graph); + BT_ASSERT_PRE( + graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Component's graph is already configured: " + "%![comp-]+c, %![graph-]+g", component, graph); + + // TODO: Validate that the name is not already used. + + BT_LIB_LOGD("Adding port to component: %![comp-]+c, " + "port-type=%s, port-name=\"%s\"", component, + bt_port_type_string(port_type), name); + + new_port = bt_port_create(component, port_type, name, user_data); + if (!new_port) { + BT_LOGE_STR("Cannot create port object."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto error; + } + + /* + * No name clash, add the port. + * The component is now the port's parent; it should _not_ + * hold a reference to the port since the port's lifetime + * is now protected by the component's own lifetime. + */ + g_ptr_array_add(ports, new_port); + + /* + * Notify the graph's creator that a new port was added. + */ + graph = bt_component_borrow_graph(component); + if (graph) { + enum bt_graph_listener_status listener_status; + + listener_status = bt_graph_notify_port_added(graph, new_port); + if (listener_status != BT_GRAPH_LISTENER_STATUS_OK) { + bt_graph_make_faulty(graph); + status = listener_status; + goto error; + } + } + + BT_LIB_LOGD("Created and added port to component: " + "%![comp-]+c, %![port-]+p", component, new_port); + + *port = new_port; + status = BT_SELF_COMPONENT_STATUS_OK; + + goto end; +error: + /* + * We need to release the reference that we would otherwise have + * returned to the caller. + */ + BT_PORT_PUT_REF_AND_RESET(new_port); + +end: + return status; +} + +BT_HIDDEN +uint64_t bt_component_get_input_port_count(const struct bt_component *comp) +{ + BT_ASSERT_PRE_NON_NULL(comp, "Component"); + return (uint64_t) comp->input_ports->len; +} + +BT_HIDDEN +uint64_t bt_component_get_output_port_count(const struct bt_component *comp) +{ + BT_ASSERT_PRE_NON_NULL(comp, "Component"); + return (uint64_t) comp->output_ports->len; +} + +BT_HIDDEN +int bt_component_create(struct bt_component_class *component_class, + const char *name, struct bt_component **user_component) +{ + int ret = 0; + struct bt_component *component = NULL; + enum bt_component_class_type type; + + BT_ASSERT(user_component); + BT_ASSERT(component_class); + BT_ASSERT(name); + type = bt_component_class_get_type(component_class); + BT_LIB_LOGD("Creating empty component from component class: %![cc-]+C, " + "comp-name=\"%s\"", component_class, name); + component = component_create_funcs[type](component_class); + if (!component) { + BT_LOGE_STR("Cannot create specific component object."); + ret = -1; + goto end; + } + + bt_object_init_shared_with_parent(&component->base, destroy_component); + component->class = component_class; + bt_object_get_no_null_check(component->class); + component->destroy = component_destroy_funcs[type]; + component->name = g_string_new(name); + if (!component->name) { + BT_LOGE_STR("Failed to allocate one GString."); + ret = -1; + goto end; + } + + component->input_ports = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!component->input_ports) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + ret = -1; + goto end; + } + + component->output_ports = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!component->output_ports) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + ret = -1; + goto end; + } + + component->destroy_listeners = g_array_new(FALSE, TRUE, + sizeof(struct bt_component_destroy_listener)); + if (!component->destroy_listeners) { + BT_LOGE_STR("Failed to allocate one GArray."); + ret = -1; + goto end; + } + + BT_LIB_LOGD("Created empty component from component class: " + "%![cc-]+C, %![comp-]+c", component_class, component); + BT_OBJECT_MOVE_REF(*user_component, component); + +end: + bt_object_put_ref(component); + return ret; +} + +const char *bt_component_get_name(const struct bt_component *component) +{ + BT_ASSERT_PRE_NON_NULL(component, "Component"); + return component->name->str; +} + +const struct bt_component_class *bt_component_borrow_class_const( + const struct bt_component *component) +{ + BT_ASSERT_PRE_NON_NULL(component, "Component"); + return component->class; +} + +void *bt_self_component_get_data(const struct bt_self_component *self_comp) +{ + struct bt_component *component = (void *) self_comp; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + return component->user_data; +} + +void bt_self_component_set_data(struct bt_self_component *self_comp, + void *data) +{ + struct bt_component *component = (void *) self_comp; + + BT_ASSERT_PRE_NON_NULL(component, "Component"); + component->user_data = data; + BT_LIB_LOGV("Set component's user data: %!+c", component); +} + +BT_HIDDEN +void bt_component_set_graph(struct bt_component *component, + struct bt_graph *graph) +{ + bt_object_set_parent(&component->base, + graph ? &graph->base : NULL); +} + +bt_bool bt_component_graph_is_canceled(const struct bt_component *component) +{ + return bt_graph_is_canceled( + (void *) bt_object_borrow_parent(&component->base)); +} + +static +struct bt_port *borrow_port_by_name(GPtrArray *ports, + const char *name) +{ + uint64_t i; + struct bt_port *ret_port = NULL; + + BT_ASSERT(name); + + for (i = 0; i < ports->len; i++) { + struct bt_port *port = g_ptr_array_index(ports, i); + + if (!strcmp(name, port->name->str)) { + ret_port = port; + break; + } + } + + return ret_port; +} + +BT_HIDDEN +struct bt_port_input *bt_component_borrow_input_port_by_name( + struct bt_component *comp, const char *name) +{ + BT_ASSERT(comp); + return (void *) borrow_port_by_name(comp->input_ports, name); +} + +BT_HIDDEN +struct bt_port_output *bt_component_borrow_output_port_by_name( + struct bt_component *comp, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(comp, "Component"); + return (void *) + borrow_port_by_name(comp->output_ports, name); +} + +static +struct bt_port *borrow_port_by_index(GPtrArray *ports, uint64_t index) +{ + BT_ASSERT(index < ports->len); + return g_ptr_array_index(ports, index); +} + +BT_HIDDEN +struct bt_port_input *bt_component_borrow_input_port_by_index( + struct bt_component *comp, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(comp, "Component"); + BT_ASSERT_PRE_VALID_INDEX(index, comp->input_ports->len); + return (void *) + borrow_port_by_index(comp->input_ports, index); +} + +BT_HIDDEN +struct bt_port_output *bt_component_borrow_output_port_by_index( + struct bt_component *comp, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(comp, "Component"); + BT_ASSERT_PRE_VALID_INDEX(index, comp->output_ports->len); + return (void *) + borrow_port_by_index(comp->output_ports, index); +} + +BT_HIDDEN +enum bt_self_component_status bt_component_add_input_port( + struct bt_component *component, const char *name, + void *user_data, struct bt_port **port) +{ + /* add_port() logs details */ + return add_port(component, component->input_ports, + BT_PORT_TYPE_INPUT, name, user_data, port); +} + +BT_HIDDEN +enum bt_self_component_status bt_component_add_output_port( + struct bt_component *component, const char *name, + void *user_data, struct bt_port **port) +{ + /* add_port() logs details */ + return add_port(component, component->output_ports, + BT_PORT_TYPE_OUTPUT, name, user_data, port); +} + +BT_HIDDEN +enum bt_self_component_status bt_component_accept_port_connection( + struct bt_component *comp, struct bt_port *self_port, + struct bt_port *other_port) +{ + typedef enum bt_self_component_status (*method_t)( + void *, void *, const void *); + + enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + method_t method = NULL; + + BT_ASSERT(comp); + BT_ASSERT(self_port); + BT_ASSERT(other_port); + + switch (comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_OUTPUT: + method = (method_t) src_cc->methods.accept_output_port_connection; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_INPUT: + method = (method_t) flt_cc->methods.accept_input_port_connection; + break; + case BT_PORT_TYPE_OUTPUT: + method = (method_t) flt_cc->methods.accept_output_port_connection; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_SINK: + { + struct bt_component_class_sink *sink_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_INPUT: + method = (method_t) sink_cc->methods.accept_input_port_connection; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + + if (method) { + BT_LIB_LOGD("Calling user's \"accept port connection\" method: " + "%![comp-]+c, %![self-port-]+p, %![other-port-]+p", + comp, self_port, other_port); + status = method(comp, self_port, (void *) other_port); + BT_LOGD("User method returned: status=%s", + bt_self_component_status_string(status)); + } + + return status; +} + +BT_HIDDEN +enum bt_self_component_status bt_component_port_connected( + struct bt_component *comp, struct bt_port *self_port, + struct bt_port *other_port) +{ + typedef enum bt_self_component_status (*method_t)( + void *, void *, const void *); + + enum bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + method_t method = NULL; + + BT_ASSERT(comp); + BT_ASSERT(self_port); + BT_ASSERT(other_port); + + switch (comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_OUTPUT: + method = (method_t) src_cc->methods.output_port_connected; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_INPUT: + method = (method_t) flt_cc->methods.input_port_connected; + break; + case BT_PORT_TYPE_OUTPUT: + method = (method_t) flt_cc->methods.output_port_connected; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_SINK: + { + struct bt_component_class_sink *sink_cc = (void *) comp->class; + + switch (self_port->type) { + case BT_PORT_TYPE_INPUT: + method = (method_t) sink_cc->methods.input_port_connected; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + + if (method) { + BT_LIB_LOGD("Calling user's \"port connected\" method: " + "%![comp-]+c, %![self-port-]+p, %![other-port-]+p", + comp, self_port, other_port); + status = method(comp, self_port, (void *) other_port); + BT_LOGD("User method returned: status=%s", + bt_self_component_status_string(status)); + BT_ASSERT_PRE(status == BT_SELF_COMPONENT_STATUS_OK || + status == BT_SELF_COMPONENT_STATUS_ERROR || + status == BT_SELF_COMPONENT_STATUS_NOMEM, + "Unexpected returned component status: status=%s", + bt_self_component_status_string(status)); + } + + return status; +} + +BT_HIDDEN +void bt_component_add_destroy_listener(struct bt_component *component, + bt_component_destroy_listener_func func, void *data) +{ + struct bt_component_destroy_listener listener; + + BT_ASSERT(component); + BT_ASSERT(func); + listener.func = func; + listener.data = data; + g_array_append_val(component->destroy_listeners, listener); + BT_LIB_LOGV("Added destroy listener: %![comp-]+c, " + "func-addr=%p, data-addr=%p", + component, func, data); +} + +BT_HIDDEN +void bt_component_remove_destroy_listener(struct bt_component *component, + bt_component_destroy_listener_func func, void *data) +{ + uint64_t i; + + BT_ASSERT(component); + BT_ASSERT(func); + + for (i = 0; i < component->destroy_listeners->len; i++) { + struct bt_component_destroy_listener *listener = + &g_array_index(component->destroy_listeners, + struct bt_component_destroy_listener, i); + + if (listener->func == func && listener->data == data) { + g_array_remove_index(component->destroy_listeners, i); + i--; + BT_LIB_LOGV("Removed destroy listener: %![comp-]+c, " + "func-addr=%p, data-addr=%p", + component, func, data); + } + } +} + +void bt_component_get_ref(const struct bt_component *component) +{ + bt_object_get_ref(component); +} + +void bt_component_put_ref(const struct bt_component *component) +{ + bt_object_put_ref(component); +} diff --git a/src/lib/graph/component.h b/src/lib/graph/component.h new file mode 100644 index 00000000..2753cfe9 --- /dev/null +++ b/src/lib/graph/component.h @@ -0,0 +1,163 @@ +#ifndef BABELTRACE_GRAPH_COMPONENT_INTERNAL_H +#define BABELTRACE_GRAPH_COMPONENT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include "lib/object.h" +#include +#include "common/assert.h" +#include +#include + +#include "component-class.h" +#include "port.h" + +typedef void (*bt_component_destroy_listener_func)( + struct bt_component *class, void *data); + +struct bt_component_destroy_listener { + bt_component_destroy_listener_func func; + void *data; +}; + +struct bt_graph; + +struct bt_component { + struct bt_object base; + struct bt_component_class *class; + GString *name; + + /* + * Internal destroy function specific to a source, filter, or + * sink component object. + */ + void (*destroy)(struct bt_component *); + + /* User-defined data */ + void *user_data; + + /* Input and output ports (weak references) */ + GPtrArray *input_ports; + GPtrArray *output_ports; + + /* Array of struct bt_component_destroy_listener */ + GArray *destroy_listeners; + + bool initialized; +}; + +static inline +struct bt_graph *bt_component_borrow_graph(struct bt_component *comp) +{ + BT_ASSERT(comp); + return (void *) bt_object_borrow_parent(&comp->base); +} + +BT_HIDDEN +int bt_component_create(struct bt_component_class *component_class, + const char *name, struct bt_component **component); + +BT_HIDDEN +enum bt_self_component_status bt_component_accept_port_connection( + struct bt_component *component, struct bt_port *self_port, + struct bt_port *other_port); + +BT_HIDDEN +enum bt_self_component_status bt_component_port_connected( + struct bt_component *comp, + struct bt_port *self_port, struct bt_port *other_port); + +BT_HIDDEN +void bt_component_set_graph(struct bt_component *component, + struct bt_graph *graph); + +BT_HIDDEN +uint64_t bt_component_get_input_port_count(const struct bt_component *comp); + +BT_HIDDEN +uint64_t bt_component_get_output_port_count(const struct bt_component *comp); + +BT_HIDDEN +struct bt_port_input *bt_component_borrow_input_port_by_index( + struct bt_component *comp, uint64_t index); + +BT_HIDDEN +struct bt_port_output *bt_component_borrow_output_port_by_index( + struct bt_component *comp, uint64_t index); + +BT_HIDDEN +struct bt_port_input *bt_component_borrow_input_port_by_name( + struct bt_component *comp, const char *name); + +BT_HIDDEN +struct bt_port_output *bt_component_borrow_output_port_by_name( + struct bt_component *comp, const char *name); + +BT_HIDDEN +enum bt_self_component_status bt_component_add_input_port( + struct bt_component *component, const char *name, + void *user_data, struct bt_port **port); + +BT_HIDDEN +enum bt_self_component_status bt_component_add_output_port( + struct bt_component *component, const char *name, + void *user_data, struct bt_port **port); + +BT_HIDDEN +void bt_component_remove_port(struct bt_component *component, + struct bt_port *port); + +BT_HIDDEN +void bt_component_add_destroy_listener(struct bt_component *component, + bt_component_destroy_listener_func func, void *data); + +BT_HIDDEN +void bt_component_remove_destroy_listener(struct bt_component *component, + bt_component_destroy_listener_func func, void *data); + +static inline +const char *bt_self_component_status_string( + enum bt_self_component_status status) +{ + switch (status) { + case BT_SELF_COMPONENT_STATUS_OK: + return "BT_SELF_COMPONENT_STATUS_OK"; + case BT_SELF_COMPONENT_STATUS_END: + return "BT_SELF_COMPONENT_STATUS_END"; + case BT_SELF_COMPONENT_STATUS_AGAIN: + return "BT_SELF_COMPONENT_STATUS_AGAIN"; + case BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION: + return "BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION"; + case BT_SELF_COMPONENT_STATUS_ERROR: + return "BT_SELF_COMPONENT_STATUS_ERROR"; + case BT_SELF_COMPONENT_STATUS_NOMEM: + return "BT_SELF_COMPONENT_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +#endif /* BABELTRACE_GRAPH_COMPONENT_INTERNAL_H */ diff --git a/src/lib/graph/connection.c b/src/lib/graph/connection.c new file mode 100644 index 00000000..f76d7158 --- /dev/null +++ b/src/lib/graph/connection.c @@ -0,0 +1,263 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CONNECTION" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include + +#include "component.h" +#include "connection.h" +#include "graph.h" +#include "message/iterator.h" +#include "port.h" + +static +void destroy_connection(struct bt_object *obj) +{ + struct bt_connection *connection = container_of(obj, + struct bt_connection, base); + + BT_LIB_LOGD("Destroying connection: %!+x", connection); + + /* + * Make sure that each message iterator which was created for + * this connection is finalized before we destroy it. Once a + * message iterator is finalized, all its method return NULL or + * the BT_MESSAGE_ITERATOR_STATUS_CANCELED status. + * + * Because connections are destroyed before components within a + * graph, this ensures that message iterators are always + * finalized before their upstream component. + * + * Ending the connection does exactly this. We pass `false` to + * bt_connection_end() here to avoid removing this connection + * from the graph: if we're here, we're already in the graph's + * destructor. + */ + bt_connection_end(connection, false); + g_ptr_array_free(connection->iterators, TRUE); + connection->iterators = NULL; + + /* + * No bt_object_put_ref on ports as a connection only holds _weak_ + * references to them. + */ + g_free(connection); +} + +static +void try_remove_connection_from_graph(struct bt_connection *connection) +{ + void *graph = (void *) bt_object_borrow_parent(&connection->base); + + if (connection->base.ref_count > 0 || + connection->downstream_port || + connection->upstream_port || + connection->iterators->len > 0) { + return; + } + + /* + * At this point we know that: + * + * 1. The connection is ended (ports were disconnected). + * 2. All the message iterators that this connection + * created, if any, are finalized. + * 3. The connection's reference count is 0, so only the + * parent (graph) owns this connection after this call. + * + * In other words, no other object than the graph knows this + * connection. + * + * It is safe to remove the connection from the graph, therefore + * destroying it. + */ + BT_LIB_LOGD("Removing self from graph's connections: " + "%![graph-]+g, %![conn-]+x", graph, connection); + bt_graph_remove_connection(graph, connection); +} + +static +void parent_is_owner(struct bt_object *obj) +{ + struct bt_connection *connection = container_of(obj, + struct bt_connection, base); + + try_remove_connection_from_graph(connection); +} + +BT_HIDDEN +struct bt_connection *bt_connection_create(struct bt_graph *graph, + struct bt_port *upstream_port, + struct bt_port *downstream_port) +{ + struct bt_connection *connection = NULL; + + BT_LIB_LOGD("Creating connection: " + "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", + graph, upstream_port, downstream_port); + connection = g_new0(struct bt_connection, 1); + if (!connection) { + BT_LOGE_STR("Failed to allocate one connection."); + goto end; + } + + bt_object_init_shared_with_parent(&connection->base, + destroy_connection); + bt_object_set_parent_is_owner_listener_func(&connection->base, + parent_is_owner); + connection->iterators = g_ptr_array_new(); + if (!connection->iterators) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + BT_OBJECT_PUT_REF_AND_RESET(connection); + goto end; + } + + /* Weak references are taken, see comment in header. */ + connection->upstream_port = upstream_port; + connection->downstream_port = downstream_port; + BT_LIB_LOGD("Setting upstream port's connection: %!+p", upstream_port); + bt_port_set_connection(upstream_port, connection); + BT_LIB_LOGD("Setting downstream port's connection: %!+p", + downstream_port); + bt_port_set_connection(downstream_port, connection); + bt_object_set_parent(&connection->base, &graph->base); + BT_LIB_LOGD("Created connection: %!+x", connection); + +end: + return connection; +} + +BT_HIDDEN +void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph) +{ + struct bt_port *downstream_port = conn->downstream_port; + struct bt_port *upstream_port = conn->upstream_port; + size_t i; + + BT_LIB_LOGD("Ending connection: %!+x, try-remove-from-graph=%d", + conn, try_remove_from_graph); + + /* + * Any of the following message callback functions could + * remove one of the connection's ports from its component. To + * make sure that at least logging in called functions works + * with existing objects, get a local reference on both ports. + */ + bt_object_get_ref(downstream_port); + bt_object_get_ref(upstream_port); + + if (downstream_port) { + BT_LIB_LOGD("Disconnecting connection's downstream port: %!+p", + downstream_port); + bt_port_set_connection(downstream_port, NULL); + conn->downstream_port = NULL; + } + + if (upstream_port) { + BT_LIB_LOGD("Disconnecting connection's upstream port: %!+p", + upstream_port); + bt_port_set_connection(upstream_port, NULL); + conn->upstream_port = NULL; + } + + /* + * It is safe to put the local port references now that we don't + * need them anymore. This could indeed destroy them. + */ + bt_object_put_ref(downstream_port); + bt_object_put_ref(upstream_port); + + /* + * Because this connection is ended, finalize each message + * iterator created from it. + * + * In practice, this only happens when the connection is + * destroyed and not all its message iterators were finalized, + * which is on graph destruction. + */ + for (i = 0; i < conn->iterators->len; i++) { + struct bt_self_component_port_input_message_iterator *iterator = + g_ptr_array_index(conn->iterators, i); + + BT_LIB_LOGD("Finalizing message iterator created by " + "this ended connection: %![iter-]+i", iterator); + bt_self_component_port_input_message_iterator_try_finalize( + iterator); + + /* + * Make sure this iterator does not try to remove itself + * from this connection's iterators on destruction + * because this connection won't exist anymore. + */ + bt_self_component_port_input_message_iterator_set_connection( + iterator, NULL); + } + + g_ptr_array_set_size(conn->iterators, 0); + + if (try_remove_from_graph) { + try_remove_connection_from_graph(conn); + } +} + +const struct bt_port_output *bt_connection_borrow_upstream_port_const( + const struct bt_connection *connection) +{ + BT_ASSERT_PRE_NON_NULL(connection, "Connection"); + return (void *) connection->upstream_port; +} + +const struct bt_port_input *bt_connection_borrow_downstream_port_const( + const struct bt_connection *connection) +{ + BT_ASSERT_PRE_NON_NULL(connection, "Connection"); + return (void *) connection->downstream_port; +} + +BT_HIDDEN +void bt_connection_remove_iterator(struct bt_connection *conn, + struct bt_self_component_port_input_message_iterator *iterator) +{ + g_ptr_array_remove(conn->iterators, iterator); + BT_LIB_LOGV("Removed message iterator from connection: " + "%![conn-]+x, %![iter-]+i", conn, iterator); + try_remove_connection_from_graph(conn); +} + +void bt_connection_get_ref(const struct bt_connection *connection) +{ + bt_object_get_ref(connection); +} + +void bt_connection_put_ref(const struct bt_connection *connection) +{ + bt_object_put_ref(connection); +} diff --git a/src/lib/graph/connection.h b/src/lib/graph/connection.h new file mode 100644 index 00000000..e6190fa5 --- /dev/null +++ b/src/lib/graph/connection.h @@ -0,0 +1,83 @@ +#ifndef BABELTRACE_GRAPH_CONNECTION_INTERNAL_H +#define BABELTRACE_GRAPH_CONNECTION_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "lib/object.h" +#include "common/assert.h" +#include + +#include "message/iterator.h" + +struct bt_graph; + +struct bt_connection { + /* + * The graph is a connection's parent and the connection is the parent + * of all iterators it has created. + */ + struct bt_object base; + /* + * Weak references are held to both ports. Their existence is guaranteed + * by the existence of the graph and thus, of their respective + * components. + */ + /* Downstream port. */ + struct bt_port *downstream_port; + /* Upstream port. */ + struct bt_port *upstream_port; + + /* + * Weak references to all the message iterators that were + * created on this connection. + */ + GPtrArray *iterators; + + bool notified_upstream_port_connected; + bool notified_downstream_port_connected; + bool notified_graph_ports_connected; +}; + +BT_HIDDEN +struct bt_connection *bt_connection_create(struct bt_graph *graph, + struct bt_port *upstream_port, + struct bt_port *downstream_port); + +BT_HIDDEN +void bt_connection_end(struct bt_connection *conn, bool try_remove_from_graph); + +BT_HIDDEN +void bt_connection_remove_iterator(struct bt_connection *conn, + struct bt_self_component_port_input_message_iterator *iterator); + +static inline +struct bt_graph *bt_connection_borrow_graph(struct bt_connection *conn) +{ + BT_ASSERT(conn); + return (void *) conn->base.parent; +} + +#endif /* BABELTRACE_GRAPH_CONNECTION_INTERNAL_H */ diff --git a/src/lib/graph/graph.c b/src/lib/graph/graph.c new file mode 100644 index 00000000..4b87d735 --- /dev/null +++ b/src/lib/graph/graph.c @@ -0,0 +1,1557 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "GRAPH" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "lib/graph/message/message.h" +#include "compat/compiler.h" +#include "common/common.h" +#include +#include +#include +#include "lib/value.h" +#include +#include + +#include "component.h" +#include "component-sink.h" +#include "connection.h" +#include "graph.h" +#include "message/event.h" +#include "message/packet.h" + +typedef enum bt_graph_listener_status (*port_added_func_t)( + const void *, const void *, void *); + +typedef enum bt_graph_listener_status (*ports_connected_func_t)( + const void *, const void *, const void *, const void *, void *); + +typedef enum bt_self_component_status (*comp_init_method_t)(const void *, + const void *, void *); + +struct bt_graph_listener { + bt_graph_listener_removed_func removed; + void *data; +}; + +struct bt_graph_listener_port_added { + struct bt_graph_listener base; + port_added_func_t func; +}; + +struct bt_graph_listener_ports_connected { + struct bt_graph_listener base; + ports_connected_func_t func; +}; + +#define INIT_LISTENERS_ARRAY(_type, _listeners) \ + do { \ + _listeners = g_array_new(FALSE, TRUE, sizeof(_type)); \ + if (!(_listeners)) { \ + BT_LOGE_STR("Failed to allocate one GArray."); \ + } \ + } while (0) + +#define CALL_REMOVE_LISTENERS(_type, _listeners) \ + do { \ + size_t i; \ + \ + if (!_listeners) { \ + break; \ + } \ + for (i = 0; i < (_listeners)->len; i++) { \ + _type *listener = \ + &g_array_index((_listeners), _type, i); \ + \ + if (listener->base.removed) { \ + listener->base.removed(listener->base.data); \ + } \ + } \ + } while (0) + +static +void destroy_graph(struct bt_object *obj) +{ + struct bt_graph *graph = container_of(obj, struct bt_graph, base); + + /* + * The graph's reference count is 0 if we're here. Increment + * it to avoid a double-destroy (possibly infinitely recursive) + * in this situation: + * + * 1. We put and destroy a connection. + * 2. This connection's destructor finalizes its active message + * iterators. + * 3. A message iterator's finalization function gets a new + * reference on its component (reference count goes from 0 to + * 1). + * 4. Since this component's reference count goes to 1, it takes + * a reference on its parent (this graph). This graph's + * reference count goes from 0 to 1. + * 5. The message iterator's finalization function puts its + * component reference (reference count goes from 1 to 0). + * 6. Since this component's reference count goes from 1 to 0, + * it puts its parent (this graph). This graph's reference + * count goes from 1 to 0. + * 7. Since this graph's reference count goes from 1 to 0, its + * destructor is called (this function). + * + * With the incrementation below, the graph's reference count at + * step 4 goes from 1 to 2, and from 2 to 1 at step 6. This + * ensures that this function is not called two times. + */ + BT_LIB_LOGD("Destroying graph: %!+g", graph); + obj->ref_count++; + + /* + * Cancel the graph to disallow some operations, like creating + * message iterators and adding ports to components. + */ + (void) bt_graph_cancel((void *) graph); + + /* Call all remove listeners */ + CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, + graph->listeners.source_output_port_added); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, + graph->listeners.filter_output_port_added); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, + graph->listeners.filter_input_port_added); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_port_added, + graph->listeners.sink_input_port_added); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, + graph->listeners.source_filter_ports_connected); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, + graph->listeners.filter_filter_ports_connected); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, + graph->listeners.source_sink_ports_connected); + CALL_REMOVE_LISTENERS(struct bt_graph_listener_ports_connected, + graph->listeners.filter_sink_ports_connected); + + if (graph->messages) { + g_ptr_array_free(graph->messages, TRUE); + graph->messages = NULL; + } + + if (graph->connections) { + BT_LOGD_STR("Destroying connections."); + g_ptr_array_free(graph->connections, TRUE); + graph->connections = NULL; + } + + if (graph->components) { + BT_LOGD_STR("Destroying components."); + g_ptr_array_free(graph->components, TRUE); + graph->components = NULL; + } + + if (graph->sinks_to_consume) { + g_queue_free(graph->sinks_to_consume); + graph->sinks_to_consume = NULL; + } + + if (graph->listeners.source_output_port_added) { + g_array_free(graph->listeners.source_output_port_added, TRUE); + graph->listeners.source_output_port_added = NULL; + } + + if (graph->listeners.filter_output_port_added) { + g_array_free(graph->listeners.filter_output_port_added, TRUE); + graph->listeners.filter_output_port_added = NULL; + } + + if (graph->listeners.filter_input_port_added) { + g_array_free(graph->listeners.filter_input_port_added, TRUE); + graph->listeners.filter_input_port_added = NULL; + } + + if (graph->listeners.sink_input_port_added) { + g_array_free(graph->listeners.sink_input_port_added, TRUE); + graph->listeners.sink_input_port_added = NULL; + } + + if (graph->listeners.source_filter_ports_connected) { + g_array_free(graph->listeners.source_filter_ports_connected, + TRUE); + graph->listeners.source_filter_ports_connected = NULL; + } + + if (graph->listeners.filter_filter_ports_connected) { + g_array_free(graph->listeners.filter_filter_ports_connected, + TRUE); + graph->listeners.filter_filter_ports_connected = NULL; + } + + if (graph->listeners.source_sink_ports_connected) { + g_array_free(graph->listeners.source_sink_ports_connected, + TRUE); + graph->listeners.source_sink_ports_connected = NULL; + } + + if (graph->listeners.filter_sink_ports_connected) { + g_array_free(graph->listeners.filter_sink_ports_connected, + TRUE); + graph->listeners.filter_sink_ports_connected = NULL; + } + + bt_object_pool_finalize(&graph->event_msg_pool); + bt_object_pool_finalize(&graph->packet_begin_msg_pool); + bt_object_pool_finalize(&graph->packet_end_msg_pool); + g_free(graph); +} + +static +void destroy_message_event(struct bt_message *msg, + struct bt_graph *graph) +{ + bt_message_event_destroy(msg); +} + +static +void destroy_message_packet_begin(struct bt_message *msg, + struct bt_graph *graph) +{ + bt_message_packet_destroy(msg); +} + +static +void destroy_message_packet_end(struct bt_message *msg, + struct bt_graph *graph) +{ + bt_message_packet_destroy(msg); +} + +static +void notify_message_graph_is_destroyed(struct bt_message *msg) +{ + bt_message_unlink_graph(msg); +} + +struct bt_graph *bt_graph_create(void) +{ + struct bt_graph *graph; + int ret; + + BT_LOGD_STR("Creating graph object."); + graph = g_new0(struct bt_graph, 1); + if (!graph) { + BT_LOGE_STR("Failed to allocate one graph."); + goto end; + } + + bt_object_init_shared(&graph->base, destroy_graph); + graph->connections = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!graph->connections) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + graph->components = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!graph->components) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + graph->sinks_to_consume = g_queue_new(); + if (!graph->sinks_to_consume) { + BT_LOGE_STR("Failed to allocate one GQueue."); + goto error; + } + + bt_graph_set_can_consume(graph, true); + INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, + graph->listeners.source_output_port_added); + + if (!graph->listeners.source_output_port_added) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, + graph->listeners.filter_output_port_added); + + if (!graph->listeners.filter_output_port_added) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, + graph->listeners.filter_input_port_added); + + if (!graph->listeners.filter_input_port_added) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_port_added, + graph->listeners.sink_input_port_added); + + if (!graph->listeners.sink_input_port_added) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, + graph->listeners.source_filter_ports_connected); + + if (!graph->listeners.source_filter_ports_connected) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, + graph->listeners.source_sink_ports_connected); + + if (!graph->listeners.source_sink_ports_connected) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, + graph->listeners.filter_filter_ports_connected); + + if (!graph->listeners.filter_filter_ports_connected) { + ret = -1; + goto error; + } + + INIT_LISTENERS_ARRAY(struct bt_graph_listener_ports_connected, + graph->listeners.filter_sink_ports_connected); + + if (!graph->listeners.filter_sink_ports_connected) { + ret = -1; + goto error; + } + + ret = bt_object_pool_initialize(&graph->event_msg_pool, + (bt_object_pool_new_object_func) bt_message_event_new, + (bt_object_pool_destroy_object_func) destroy_message_event, + graph); + if (ret) { + BT_LOGE("Failed to initialize event message pool: ret=%d", + ret); + goto error; + } + + ret = bt_object_pool_initialize(&graph->packet_begin_msg_pool, + (bt_object_pool_new_object_func) bt_message_packet_beginning_new, + (bt_object_pool_destroy_object_func) destroy_message_packet_begin, + graph); + if (ret) { + BT_LOGE("Failed to initialize packet beginning message pool: ret=%d", + ret); + goto error; + } + + ret = bt_object_pool_initialize(&graph->packet_end_msg_pool, + (bt_object_pool_new_object_func) bt_message_packet_end_new, + (bt_object_pool_destroy_object_func) destroy_message_packet_end, + graph); + if (ret) { + BT_LOGE("Failed to initialize packet end message pool: ret=%d", + ret); + goto error; + } + + graph->messages = g_ptr_array_new_with_free_func( + (GDestroyNotify) notify_message_graph_is_destroyed); + BT_LIB_LOGD("Created graph object: %!+g", graph); + +end: + return (void *) graph; + +error: + BT_OBJECT_PUT_REF_AND_RESET(graph); + goto end; +} + +enum bt_graph_status bt_graph_connect_ports( + struct bt_graph *graph, + const struct bt_port_output *upstream_port_out, + const struct bt_port_input *downstream_port_in, + const struct bt_connection **user_connection) +{ + enum bt_graph_status status = BT_GRAPH_STATUS_OK; + enum bt_graph_listener_status listener_status; + struct bt_connection *connection = NULL; + struct bt_port *upstream_port = (void *) upstream_port_out; + struct bt_port *downstream_port = (void *) downstream_port_in; + struct bt_component *upstream_component = NULL; + struct bt_component *downstream_component = NULL; + enum bt_self_component_status component_status; + bool init_can_consume; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(upstream_port, "Upstream port"); + BT_ASSERT_PRE_NON_NULL(downstream_port, "Downstream port port"); + BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); + BT_ASSERT_PRE( + graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not in the \"configuring\" state: %!+g", graph); + BT_ASSERT_PRE(!bt_port_is_connected(upstream_port), + "Upstream port is already connected: %!+p", upstream_port); + BT_ASSERT_PRE(!bt_port_is_connected(downstream_port), + "Downstream port is already connected: %!+p", downstream_port); + BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) upstream_port), + "Upstream port does not belong to a component: %!+p", + upstream_port); + BT_ASSERT_PRE(bt_port_borrow_component_inline((void *) downstream_port), + "Downstream port does not belong to a component: %!+p", + downstream_port); + init_can_consume = graph->can_consume; + BT_LIB_LOGD("Connecting component ports within graph: " + "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", + graph, upstream_port, downstream_port); + bt_graph_set_can_consume(graph, false); + upstream_component = bt_port_borrow_component_inline( + (void *) upstream_port); + downstream_component = bt_port_borrow_component_inline( + (void *) downstream_port); + + /* + * At this point the ports are not connected yet. Both + * components need to accept an eventual connection to their + * port by the other port before we continue. + */ + BT_LIB_LOGD("Asking upstream component to accept the connection: " + "%![comp-]+c", upstream_component); + component_status = bt_component_accept_port_connection( + upstream_component, (void *) upstream_port, + (void *) downstream_port); + if (component_status != BT_SELF_COMPONENT_STATUS_OK) { + if (component_status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { + BT_LOGD_STR("Upstream component refused the connection."); + } else { + BT_LOGW("Cannot ask upstream component to accept the connection: " + "status=%s", bt_self_component_status_string(component_status)); + } + + status = (int) component_status; + goto end; + } + + BT_LIB_LOGD("Asking downstream component to accept the connection: " + "%![comp-]+c", downstream_component); + component_status = bt_component_accept_port_connection( + downstream_component, (void *) downstream_port, + (void *) upstream_port); + if (component_status != BT_SELF_COMPONENT_STATUS_OK) { + if (component_status == BT_SELF_COMPONENT_STATUS_REFUSE_PORT_CONNECTION) { + BT_LOGD_STR("Downstream component refused the connection."); + } else { + BT_LOGW("Cannot ask downstream component to accept the connection: " + "status=%s", bt_self_component_status_string(component_status)); + } + + status = (int) component_status; + goto end; + } + + BT_LOGD_STR("Creating connection."); + connection = bt_connection_create(graph, (void *) upstream_port, + (void *) downstream_port); + if (!connection) { + BT_LOGW("Cannot create connection object."); + status = BT_GRAPH_STATUS_NOMEM; + goto end; + } + + BT_LIB_LOGD("Connection object created: %!+x", connection); + + /* + * Ownership of upstream_component/downstream_component and of + * the connection object is transferred to the graph. + */ + g_ptr_array_add(graph->connections, connection); + + /* + * Notify both components that their port is connected. + */ + BT_LIB_LOGD("Notifying upstream component that its port is connected: " + "%![comp-]+c, %![port-]+p", upstream_component, upstream_port); + component_status = bt_component_port_connected(upstream_component, + (void *) upstream_port, (void *) downstream_port); + if (component_status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LIB_LOGW("Error while notifying upstream component that its port is connected: " + "status=%s, %![graph-]+g, %![up-comp-]+c, " + "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", + bt_self_component_status_string(component_status), + graph, upstream_component, downstream_component, + upstream_port, downstream_port); + bt_connection_end(connection, true); + status = (int) component_status; + goto end; + } + + connection->notified_upstream_port_connected = true; + BT_LIB_LOGD("Notifying downstream component that its port is connected: " + "%![comp-]+c, %![port-]+p", downstream_component, + downstream_port); + component_status = bt_component_port_connected(downstream_component, + (void *) downstream_port, (void *) upstream_port); + if (component_status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LIB_LOGW("Error while notifying downstream component that its port is connected: " + "status=%s, %![graph-]+g, %![up-comp-]+c, " + "%![down-comp-]+c, %![up-port-]+p, %![down-port-]+p", + bt_self_component_status_string(component_status), + graph, upstream_component, downstream_component, + upstream_port, downstream_port); + bt_connection_end(connection, true); + status = (int) component_status; + goto end; + } + + connection->notified_downstream_port_connected = true; + + /* + * Notify the graph's creator that both ports are connected. + */ + BT_LOGD_STR("Notifying graph's user that new component ports are connected."); + listener_status = bt_graph_notify_ports_connected(graph, upstream_port, downstream_port); + if (listener_status != BT_GRAPH_LISTENER_STATUS_OK) { + status = (int) listener_status; + goto end; + } + + connection->notified_graph_ports_connected = true; + BT_LIB_LOGD("Connected component ports within graph: " + "%![graph-]+g, %![up-comp-]+c, %![down-comp-]+c, " + "%![up-port-]+p, %![down-port-]+p", + graph, upstream_component, downstream_component, + upstream_port, downstream_port); + + if (user_connection) { + /* Move reference to user */ + *user_connection = connection; + connection = NULL; + } + +end: + if (status != BT_GRAPH_STATUS_OK) { + bt_graph_make_faulty(graph); + } + + bt_object_put_ref(connection); + (void) init_can_consume; + bt_graph_set_can_consume(graph, init_can_consume); + return status; +} + +static inline +enum bt_graph_status consume_graph_sink(struct bt_component_sink *comp) +{ + enum bt_self_component_status comp_status; + struct bt_component_class_sink *sink_class = NULL; + + BT_ASSERT(comp); + sink_class = (void *) comp->parent.class; + BT_ASSERT(sink_class->methods.consume); + BT_LIB_LOGD("Calling user's consume method: %!+c", comp); + comp_status = sink_class->methods.consume((void *) comp); + BT_LOGD("User method returned: status=%s", + bt_self_component_status_string(comp_status)); + BT_ASSERT_PRE(comp_status == BT_SELF_COMPONENT_STATUS_OK || + comp_status == BT_SELF_COMPONENT_STATUS_END || + comp_status == BT_SELF_COMPONENT_STATUS_AGAIN || + comp_status == BT_SELF_COMPONENT_STATUS_ERROR || + comp_status == BT_SELF_COMPONENT_STATUS_NOMEM, + "Invalid component status returned by consuming method: " + "status=%s", bt_self_component_status_string(comp_status)); + if (comp_status < 0) { + BT_LOGW_STR("Consume method failed."); + goto end; + } + + BT_LIB_LOGV("Consumed from sink: %![comp-]+c, status=%s", + comp, bt_self_component_status_string(comp_status)); + +end: + return (int) comp_status; +} + +/* + * `node` is removed from the queue of sinks to consume when passed to + * this function. This function adds it back to the queue if there's + * still something to consume afterwards. + */ +static inline +enum bt_graph_status consume_sink_node(struct bt_graph *graph, GList *node) +{ + enum bt_graph_status status; + struct bt_component_sink *sink; + + sink = node->data; + status = consume_graph_sink(sink); + if (unlikely(status != BT_GRAPH_STATUS_END)) { + g_queue_push_tail_link(graph->sinks_to_consume, node); + goto end; + } + + /* End reached, the node is not added back to the queue and free'd. */ + g_queue_delete_link(graph->sinks_to_consume, node); + + /* Don't forward an END status if there are sinks left to consume. */ + if (!g_queue_is_empty(graph->sinks_to_consume)) { + status = BT_GRAPH_STATUS_OK; + goto end; + } + +end: + BT_LIB_LOGV("Consumed sink node: %![comp-]+c, status=%s", + sink, bt_graph_status_string(status)); + return status; +} + +BT_HIDDEN +enum bt_graph_status bt_graph_consume_sink_no_check(struct bt_graph *graph, + struct bt_component_sink *sink) +{ + enum bt_graph_status status; + GList *sink_node; + int index; + + BT_LIB_LOGV("Making specific sink consume: %![comp-]+c", sink); + BT_ASSERT(bt_component_borrow_graph((void *) sink) == graph); + + if (g_queue_is_empty(graph->sinks_to_consume)) { + BT_LOGV_STR("Graph's sink queue is empty: end of graph."); + status = BT_GRAPH_STATUS_END; + goto end; + } + + index = g_queue_index(graph->sinks_to_consume, sink); + if (index < 0) { + BT_LOGV_STR("Sink is not marked as consumable: sink is ended."); + status = BT_GRAPH_STATUS_END; + goto end; + } + + sink_node = g_queue_pop_nth_link(graph->sinks_to_consume, index); + BT_ASSERT(sink_node); + status = consume_sink_node(graph, sink_node); + +end: + return status; +} + +static inline +enum bt_graph_status consume_no_check(struct bt_graph *graph) +{ + enum bt_graph_status status = BT_GRAPH_STATUS_OK; + struct bt_component *sink; + GList *current_node; + + BT_ASSERT_PRE(graph->has_sink, + "Graph has no sink component: %!+g", graph); + BT_LIB_LOGV("Making next sink consume: %![graph-]+g", graph); + + if (unlikely(g_queue_is_empty(graph->sinks_to_consume))) { + BT_LOGV_STR("Graph's sink queue is empty: end of graph."); + status = BT_GRAPH_STATUS_END; + goto end; + } + + current_node = g_queue_pop_head_link(graph->sinks_to_consume); + sink = current_node->data; + BT_LIB_LOGV("Chose next sink to consume: %!+c", sink); + status = consume_sink_node(graph, current_node); + +end: + return status; +} + +enum bt_graph_status bt_graph_consume(struct bt_graph *graph) +{ + enum bt_graph_status status; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); + BT_ASSERT_PRE(graph->can_consume, + "Cannot consume graph in its current state: %!+g", graph); + BT_ASSERT_PRE(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY, + "Graph is in a faulty state: %!+g", graph); + bt_graph_set_can_consume(graph, false); + status = bt_graph_configure(graph); + if (unlikely(status)) { + /* bt_graph_configure() logs errors */ + goto end; + } + + status = consume_no_check(graph); + bt_graph_set_can_consume(graph, true); + +end: + return status; +} + +enum bt_graph_status bt_graph_run(struct bt_graph *graph) +{ + enum bt_graph_status status; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); + BT_ASSERT_PRE(graph->can_consume, + "Cannot consume graph in its current state: %!+g", graph); + BT_ASSERT_PRE(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY, + "Graph is in a faulty state: %!+g", graph); + bt_graph_set_can_consume(graph, false); + status = bt_graph_configure(graph); + if (unlikely(status)) { + /* bt_graph_configure() logs errors */ + goto end; + } + + BT_LIB_LOGV("Running graph: %!+g", graph); + + do { + /* + * Check if the graph is canceled at each iteration. If + * the graph was canceled by another thread or by a + * signal handler, this is not a warning nor an error, + * it was intentional: log with a DEBUG level only. + */ + if (unlikely(graph->canceled)) { + BT_LIB_LOGD("Stopping the graph: graph is canceled: " + "%!+g", graph); + status = BT_GRAPH_STATUS_CANCELED; + goto end; + } + + status = consume_no_check(graph); + if (unlikely(status == BT_GRAPH_STATUS_AGAIN)) { + /* + * If AGAIN is received and there are multiple + * sinks, go ahead and consume from the next + * sink. + * + * However, in the case where a single sink is + * left, the caller can decide to busy-wait and + * call bt_graph_run() continuously + * until the source is ready or it can decide to + * sleep for an arbitrary amount of time. + */ + if (graph->sinks_to_consume->length > 1) { + status = BT_GRAPH_STATUS_OK; + } + } + } while (status == BT_GRAPH_STATUS_OK); + + if (g_queue_is_empty(graph->sinks_to_consume)) { + status = BT_GRAPH_STATUS_END; + } + +end: + BT_LIB_LOGV("Graph ran: %![graph-]+g, status=%s", graph, + bt_graph_status_string(status)); + bt_graph_set_can_consume(graph, true); + return status; +} + +enum bt_graph_status +bt_graph_add_source_component_output_port_added_listener( + struct bt_graph *graph, + bt_graph_source_component_output_port_added_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_port_added listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (port_added_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.source_output_port_added, listener); + listener_id = graph->listeners.source_output_port_added->len - 1; + BT_LIB_LOGV("Added \"source component output port added\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_filter_component_output_port_added_listener( + struct bt_graph *graph, + bt_graph_filter_component_output_port_added_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_port_added listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (port_added_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.filter_output_port_added, listener); + listener_id = graph->listeners.filter_output_port_added->len - 1; + BT_LIB_LOGV("Added \"filter component output port added\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_filter_component_input_port_added_listener( + struct bt_graph *graph, + bt_graph_filter_component_input_port_added_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_port_added listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (port_added_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.filter_input_port_added, listener); + listener_id = graph->listeners.filter_input_port_added->len - 1; + BT_LIB_LOGV("Added \"filter component input port added\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_sink_component_input_port_added_listener( + struct bt_graph *graph, + bt_graph_sink_component_input_port_added_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_port_added listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (port_added_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.sink_input_port_added, listener); + listener_id = graph->listeners.sink_input_port_added->len - 1; + BT_LIB_LOGV("Added \"sink component input port added\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_source_filter_component_ports_connected_listener( + struct bt_graph *graph, + bt_graph_source_filter_component_ports_connected_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_ports_connected listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (ports_connected_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.source_filter_ports_connected, + listener); + listener_id = graph->listeners.source_filter_ports_connected->len - 1; + BT_LIB_LOGV("Added \"source to filter component ports connected\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_source_sink_component_ports_connected_listener( + struct bt_graph *graph, + bt_graph_source_sink_component_ports_connected_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_ports_connected listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (ports_connected_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.source_sink_ports_connected, + listener); + listener_id = graph->listeners.source_sink_ports_connected->len - 1; + BT_LIB_LOGV("Added \"source to sink component ports connected\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_filter_filter_component_ports_connected_listener( + struct bt_graph *graph, + bt_graph_filter_filter_component_ports_connected_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_ports_connected listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (ports_connected_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.filter_filter_ports_connected, + listener); + listener_id = graph->listeners.filter_filter_ports_connected->len - 1; + BT_LIB_LOGV("Added \"filter to filter component ports connected\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +enum bt_graph_status +bt_graph_add_filter_sink_component_ports_connected_listener( + struct bt_graph *graph, + bt_graph_filter_sink_component_ports_connected_listener_func func, + bt_graph_listener_removed_func listener_removed, void *data, + int *out_listener_id) +{ + struct bt_graph_listener_ports_connected listener = { + .base = { + .removed = listener_removed, + .data = data, + }, + .func = (ports_connected_func_t) func, + }; + int listener_id; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(func, "Listener"); + BT_ASSERT_PRE_NON_NULL(func, "\"Listener removed\" listener"); + BT_ASSERT_PRE(!graph->in_remove_listener, + "Graph currently executing a \"listener removed\" listener: " + "%!+g", graph); + g_array_append_val(graph->listeners.filter_sink_ports_connected, + listener); + listener_id = graph->listeners.filter_sink_ports_connected->len - 1; + BT_LIB_LOGV("Added \"filter to sink component ports connected\" listener to graph: " + "%![graph-]+g, listener-addr=%p, id=%d", graph, listener, + listener_id); + + if (listener_id) { + *out_listener_id = listener_id; + } + + return BT_GRAPH_STATUS_OK; +} + +BT_HIDDEN +enum bt_graph_listener_status bt_graph_notify_port_added( + struct bt_graph *graph, struct bt_port *port) +{ + uint64_t i; + GArray *listeners; + struct bt_component *comp; + enum bt_graph_listener_status status = BT_GRAPH_LISTENER_STATUS_OK; + + BT_ASSERT(graph); + BT_ASSERT(port); + BT_LIB_LOGV("Notifying graph listeners that a port was added: " + "%![graph-]+g, %![port-]+p", graph, port); + comp = bt_port_borrow_component_inline(port); + BT_ASSERT(comp); + + switch (comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + switch (port->type) { + case BT_PORT_TYPE_OUTPUT: + listeners = graph->listeners.source_output_port_added; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + switch (port->type) { + case BT_PORT_TYPE_INPUT: + listeners = graph->listeners.filter_input_port_added; + break; + case BT_PORT_TYPE_OUTPUT: + listeners = graph->listeners.filter_output_port_added; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_SINK: + { + switch (port->type) { + case BT_PORT_TYPE_INPUT: + listeners = graph->listeners.sink_input_port_added; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + + for (i = 0; i < listeners->len; i++) { + struct bt_graph_listener_port_added *listener = + &g_array_index(listeners, + struct bt_graph_listener_port_added, i); + + + BT_ASSERT(listener->func); + status = listener->func(comp, port, listener->base.data); + if (status != BT_GRAPH_LISTENER_STATUS_OK) { + goto end; + } + } + +end: + return status; +} + +BT_HIDDEN +enum bt_graph_listener_status bt_graph_notify_ports_connected( + struct bt_graph *graph, struct bt_port *upstream_port, + struct bt_port *downstream_port) +{ + uint64_t i; + GArray *listeners; + struct bt_component *upstream_comp; + struct bt_component *downstream_comp; + enum bt_graph_listener_status status = BT_GRAPH_LISTENER_STATUS_OK; + + BT_ASSERT(graph); + BT_ASSERT(upstream_port); + BT_ASSERT(downstream_port); + BT_LIB_LOGV("Notifying graph listeners that ports were connected: " + "%![graph-]+g, %![up-port-]+p, %![down-port-]+p", + graph, upstream_port, downstream_port); + upstream_comp = bt_port_borrow_component_inline(upstream_port); + BT_ASSERT(upstream_comp); + downstream_comp = bt_port_borrow_component_inline(downstream_port); + BT_ASSERT(downstream_comp); + + switch (upstream_comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + switch (downstream_comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_FILTER: + listeners = + graph->listeners.source_filter_ports_connected; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + listeners = + graph->listeners.source_sink_ports_connected; + break; + default: + abort(); + } + + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + switch (downstream_comp->class->type) { + case BT_COMPONENT_CLASS_TYPE_FILTER: + listeners = + graph->listeners.filter_filter_ports_connected; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + listeners = + graph->listeners.filter_sink_ports_connected; + break; + default: + abort(); + } + + break; + } + default: + abort(); + } + + for (i = 0; i < listeners->len; i++) { + struct bt_graph_listener_ports_connected *listener = + &g_array_index(listeners, + struct bt_graph_listener_ports_connected, i); + + BT_ASSERT(listener->func); + status = listener->func(upstream_comp, downstream_comp, + upstream_port, downstream_port, listener->base.data); + if (status != BT_GRAPH_LISTENER_STATUS_OK) { + goto end; + } + } + +end: + return status; +} + +enum bt_graph_status bt_graph_cancel(struct bt_graph *graph) +{ + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + graph->canceled = true; + BT_LIB_LOGV("Canceled graph: %!+i", graph); + return BT_GRAPH_STATUS_OK; +} + +bt_bool bt_graph_is_canceled(const struct bt_graph *graph) +{ + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + return graph->canceled ? BT_TRUE : BT_FALSE; +} + +BT_HIDDEN +void bt_graph_remove_connection(struct bt_graph *graph, + struct bt_connection *connection) +{ + BT_ASSERT(graph); + BT_ASSERT(connection); + BT_LIB_LOGV("Removing graph's connection: %![graph-]+g, %![conn-]+x", + graph, connection); + g_ptr_array_remove(graph->connections, connection); +} + +BT_ASSERT_PRE_FUNC +static inline +bool component_name_exists(struct bt_graph *graph, const char *name) +{ + bool exists = false; + uint64_t i; + + for (i = 0; i < graph->components->len; i++) { + struct bt_component *other_comp = graph->components->pdata[i]; + + if (strcmp(name, bt_component_get_name(other_comp)) == 0) { + BT_ASSERT_PRE_MSG("Another component with the same name already exists in the graph: " + "%![other-comp-]+c, name=\"%s\"", + other_comp, name); + exists = true; + goto end; + } + } + +end: + return exists; +} + +static +enum bt_graph_status add_component_with_init_method_data( + struct bt_graph *graph, + struct bt_component_class *comp_cls, + comp_init_method_t init_method, + const char *name, const struct bt_value *params, + void *init_method_data, struct bt_component **user_component) +{ + enum bt_graph_status graph_status = BT_GRAPH_STATUS_OK; + enum bt_self_component_status comp_status; + struct bt_component *component = NULL; + int ret; + bool init_can_consume; + struct bt_value *new_params = NULL; + + BT_ASSERT(comp_cls); + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE(!graph->canceled, "Graph is canceled: %!+g", graph); + BT_ASSERT_PRE( + graph->config_state == BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not in the \"configuring\" state: %!+g", graph); + BT_ASSERT_PRE(!component_name_exists(graph, name), + "Duplicate component name: %!+g, name=\"%s\"", graph, name); + BT_ASSERT_PRE(!params || bt_value_is_map(params), + "Parameter value is not a map value: %!+v", params); + init_can_consume = graph->can_consume; + bt_graph_set_can_consume(graph, false); + BT_LIB_LOGD("Adding component to graph: " + "%![graph-]+g, %![cc-]+C, name=\"%s\", %![params-]+v, " + "init-method-data-addr=%p", + graph, comp_cls, name, params, init_method_data); + + if (!params) { + new_params = bt_value_map_create(); + if (!new_params) { + BT_LOGE_STR("Cannot create map value object."); + graph_status = BT_GRAPH_STATUS_NOMEM; + goto end; + } + + params = new_params; + } + + ret = bt_component_create(comp_cls, name, &component); + if (ret) { + BT_LOGE("Cannot create empty component object: ret=%d", + ret); + graph_status = BT_GRAPH_STATUS_NOMEM; + goto end; + } + + /* + * The user's initialization method needs to see that this + * component is part of the graph. If the user method fails, we + * immediately remove the component from the graph's components. + */ + g_ptr_array_add(graph->components, component); + bt_component_set_graph(component, graph); + bt_value_freeze(params); + + if (init_method) { + BT_LOGD_STR("Calling user's initialization method."); + comp_status = init_method(component, params, init_method_data); + BT_LOGD("User method returned: status=%s", + bt_self_component_status_string(comp_status)); + if (comp_status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGW_STR("Initialization method failed."); + graph_status = (int) comp_status; + bt_component_set_graph(component, NULL); + g_ptr_array_remove_fast(graph->components, component); + goto end; + } + } + + /* + * Mark the component as initialized so that its finalization + * method is called when it is destroyed. + */ + component->initialized = true; + + /* + * If it's a sink component, it needs to be part of the graph's + * sink queue to be consumed by bt_graph_consume(). + */ + if (bt_component_is_sink(component)) { + graph->has_sink = true; + g_queue_push_tail(graph->sinks_to_consume, component); + } + + /* + * Freeze the component class now that it's instantiated at + * least once. + */ + BT_LOGD_STR("Freezing component class."); + bt_component_class_freeze(comp_cls); + BT_LIB_LOGD("Added component to graph: " + "%![graph-]+g, %![cc-]+C, name=\"%s\", %![params-]+v, " + "init-method-data-addr=%p, %![comp-]+c", + graph, comp_cls, name, params, init_method_data, component); + + if (user_component) { + /* Move reference to user */ + *user_component = component; + component = NULL; + } + +end: + if (graph_status != BT_GRAPH_STATUS_OK) { + bt_graph_make_faulty(graph); + } + + bt_object_put_ref(component); + bt_object_put_ref(new_params); + (void) init_can_consume; + bt_graph_set_can_consume(graph, init_can_consume); + return graph_status; +} + +enum bt_graph_status +bt_graph_add_source_component_with_init_method_data( + struct bt_graph *graph, + const struct bt_component_class_source *comp_cls, + const char *name, const struct bt_value *params, + void *init_method_data, + const struct bt_component_source **component) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return add_component_with_init_method_data(graph, + (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, + name, params, init_method_data, (void *) component); +} + +enum bt_graph_status bt_graph_add_source_component( + struct bt_graph *graph, + const struct bt_component_class_source *comp_cls, + const char *name, const struct bt_value *params, + const struct bt_component_source **component) +{ + return bt_graph_add_source_component_with_init_method_data( + graph, comp_cls, name, params, NULL, component); +} + +enum bt_graph_status +bt_graph_add_filter_component_with_init_method_data( + struct bt_graph *graph, + const struct bt_component_class_filter *comp_cls, + const char *name, const struct bt_value *params, + void *init_method_data, + const struct bt_component_filter **component) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return add_component_with_init_method_data(graph, + (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, + name, params, init_method_data, (void *) component); +} + +enum bt_graph_status bt_graph_add_filter_component( + struct bt_graph *graph, + const struct bt_component_class_filter *comp_cls, + const char *name, const struct bt_value *params, + const struct bt_component_filter **component) +{ + return bt_graph_add_filter_component_with_init_method_data( + graph, comp_cls, name, params, NULL, component); +} + +enum bt_graph_status +bt_graph_add_sink_component_with_init_method_data( + struct bt_graph *graph, + const struct bt_component_class_sink *comp_cls, + const char *name, const struct bt_value *params, + void *init_method_data, + const struct bt_component_sink **component) +{ + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + return add_component_with_init_method_data(graph, + (void *) comp_cls, (comp_init_method_t) comp_cls->methods.init, + name, params, init_method_data, (void *) component); +} + +enum bt_graph_status bt_graph_add_sink_component( + struct bt_graph *graph, + const struct bt_component_class_sink *comp_cls, + const char *name, const struct bt_value *params, + const struct bt_component_sink **component) +{ + return bt_graph_add_sink_component_with_init_method_data( + graph, comp_cls, name, params, NULL, component); +} + +BT_HIDDEN +int bt_graph_remove_unconnected_component(struct bt_graph *graph, + struct bt_component *component) +{ + bool init_can_consume; + uint64_t count; + uint64_t i; + int ret = 0; + + BT_ASSERT(graph); + BT_ASSERT(component); + BT_ASSERT(component->base.ref_count == 0); + BT_ASSERT(bt_component_borrow_graph(component) == graph); + + init_can_consume = graph->can_consume; + count = bt_component_get_input_port_count(component); + + for (i = 0; i < count; i++) { + struct bt_port *port = (void *) + bt_component_borrow_input_port_by_index(component, i); + + BT_ASSERT(port); + + if (bt_port_is_connected(port)) { + BT_LIB_LOGW("Cannot remove component from graph: " + "an input port is connected: " + "%![graph-]+g, %![comp-]+c, %![port-]+p", + graph, component, port); + goto error; + } + } + + count = bt_component_get_output_port_count(component); + + for (i = 0; i < count; i++) { + struct bt_port *port = (void *) + bt_component_borrow_output_port_by_index(component, i); + + BT_ASSERT(port); + + if (bt_port_is_connected(port)) { + BT_LIB_LOGW("Cannot remove component from graph: " + "an output port is connected: " + "%![graph-]+g, %![comp-]+c, %![port-]+p", + graph, component, port); + goto error; + } + } + + bt_graph_set_can_consume(graph, false); + + /* Possibly remove from sinks to consume */ + (void) g_queue_remove(graph->sinks_to_consume, component); + + if (graph->sinks_to_consume->length == 0) { + graph->has_sink = false; + } + + /* + * This calls bt_object_try_spec_release() on the component, and + * since its reference count is 0, its destructor is called. Its + * destructor calls the user's finalization method (if set). + */ + g_ptr_array_remove(graph->components, component); + goto end; + +error: + ret = -1; + +end: + (void) init_can_consume; + bt_graph_set_can_consume(graph, init_can_consume); + return ret; +} + +BT_HIDDEN +void bt_graph_add_message(struct bt_graph *graph, + struct bt_message *msg) +{ + BT_ASSERT(graph); + BT_ASSERT(msg); + + /* + * It's okay not to take a reference because, when a + * message's reference count drops to 0, either: + * + * * It is recycled back to one of this graph's pool. + * * It is destroyed because it doesn't have any link to any + * graph, which means the original graph is already destroyed. + */ + g_ptr_array_add(graph->messages, msg); +} + +void bt_graph_get_ref(const struct bt_graph *graph) +{ + bt_object_get_ref(graph); +} + +void bt_graph_put_ref(const struct bt_graph *graph) +{ + bt_object_put_ref(graph); +} diff --git a/src/lib/graph/graph.h b/src/lib/graph/graph.h new file mode 100644 index 00000000..c41d1316 --- /dev/null +++ b/src/lib/graph/graph.h @@ -0,0 +1,300 @@ +#ifndef BABELTRACE_GRAPH_GRAPH_INTERNAL_H +#define BABELTRACE_GRAPH_GRAPH_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include "lib/object.h" +#include "lib/object-pool.h" +#include "common/assert.h" +#include +#include + +#include "component.h" +#include "component-sink.h" +#include "connection.h" + +struct bt_component; +struct bt_port; + +enum bt_graph_configuration_state { + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED, + BT_GRAPH_CONFIGURATION_STATE_CONFIGURED, + BT_GRAPH_CONFIGURATION_STATE_FAULTY, +}; + +struct bt_graph { + /** + * A component graph contains components and point-to-point connection + * between these components. + * + * In terms of ownership: + * 1) The graph is the components' parent, + * 2) The graph is the connnections' parent, + * 3) Components share the ownership of their connections, + * 4) A connection holds weak references to its two component endpoints. + */ + struct bt_object base; + + /* Array of pointers to bt_connection. */ + GPtrArray *connections; + /* Array of pointers to bt_component. */ + GPtrArray *components; + /* Queue of pointers (weak references) to sink bt_components. */ + GQueue *sinks_to_consume; + + bool canceled; + bool in_remove_listener; + bool has_sink; + + /* + * If this is false, then the public API's consuming + * functions (bt_graph_consume() and bt_graph_run()) return + * BT_GRAPH_STATUS_CANNOT_CONSUME. The internal "no check" + * functions always work. + * + * In bt_port_output_message_iterator_create(), on success, + * this flag is cleared so that the iterator remains the only + * consumer for the graph's lifetime. + */ + bool can_consume; + + enum bt_graph_configuration_state config_state; + + struct { + GArray *source_output_port_added; + GArray *filter_output_port_added; + GArray *filter_input_port_added; + GArray *sink_input_port_added; + GArray *source_filter_ports_connected; + GArray *source_sink_ports_connected; + GArray *filter_filter_ports_connected; + GArray *filter_sink_ports_connected; + } listeners; + + /* Pool of `struct bt_message_event *` */ + struct bt_object_pool event_msg_pool; + + /* Pool of `struct bt_message_packet_beginning *` */ + struct bt_object_pool packet_begin_msg_pool; + + /* Pool of `struct bt_message_packet_end *` */ + struct bt_object_pool packet_end_msg_pool; + + /* + * Array of `struct bt_message *` (weak). + * + * This is an array of all the messages ever created from + * this graph. Some of them can be in one of the pools above, + * some of them can be at large. Because each message has a + * weak pointer to the graph containing its pool, we need to + * notify each message that the graph is gone on graph + * destruction. + * + * TODO: When we support a maximum size for object pools, + * add a way for a message to remove itself from this + * array (on destruction). + */ + GPtrArray *messages; +}; + +static inline +void _bt_graph_set_can_consume(struct bt_graph *graph, bool can_consume) +{ + BT_ASSERT(graph); + graph->can_consume = can_consume; +} + +#ifdef BT_DEV_MODE +# define bt_graph_set_can_consume _bt_graph_set_can_consume +#else +# define bt_graph_set_can_consume(_graph, _can_consume) +#endif + +BT_HIDDEN +enum bt_graph_status bt_graph_consume_sink_no_check(struct bt_graph *graph, + struct bt_component_sink *sink); + +BT_HIDDEN +enum bt_graph_listener_status bt_graph_notify_port_added(struct bt_graph *graph, + struct bt_port *port); + +BT_HIDDEN +enum bt_graph_listener_status bt_graph_notify_ports_connected( + struct bt_graph *graph, struct bt_port *upstream_port, + struct bt_port *downstream_port); + +BT_HIDDEN +void bt_graph_remove_connection(struct bt_graph *graph, + struct bt_connection *connection); + +/* + * This only works with a component which is not connected at this + * point. + * + * Also the reference count of `component` should be 0 when you call + * this function, which means only `graph` owns the component, so it + * is safe to destroy. + */ +BT_HIDDEN +int bt_graph_remove_unconnected_component(struct bt_graph *graph, + struct bt_component *component); + +BT_HIDDEN +void bt_graph_add_message(struct bt_graph *graph, + struct bt_message *msg); + +static inline +const char *bt_graph_status_string(enum bt_graph_status status) +{ + switch (status) { + case BT_GRAPH_STATUS_CANCELED: + return "BT_GRAPH_STATUS_CANCELED"; + case BT_GRAPH_STATUS_AGAIN: + return "BT_GRAPH_STATUS_AGAIN"; + case BT_GRAPH_STATUS_END: + return "BT_GRAPH_STATUS_END"; + case BT_GRAPH_STATUS_OK: + return "BT_GRAPH_STATUS_OK"; + case BT_GRAPH_STATUS_ERROR: + return "BT_GRAPH_STATUS_ERROR"; + case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; + case BT_GRAPH_STATUS_NOMEM: + return "BT_GRAPH_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_graph_configuration_state_string( + enum bt_graph_configuration_state state) +{ + switch (state) { + case BT_GRAPH_CONFIGURATION_STATE_CONFIGURING: + return "BT_GRAPH_CONFIGURATION_STATE_CONFIGURING"; + case BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED: + return "BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED"; + case BT_GRAPH_CONFIGURATION_STATE_CONFIGURED: + return "BT_GRAPH_CONFIGURATION_STATE_CONFIGURED"; + default: + return "(unknown)"; + } +} + +static inline +enum bt_graph_status bt_graph_configure(struct bt_graph *graph) +{ + enum bt_graph_status status = BT_GRAPH_STATUS_OK; + uint64_t i; + + BT_ASSERT(graph->config_state != BT_GRAPH_CONFIGURATION_STATE_FAULTY); + + if (likely(graph->config_state == + BT_GRAPH_CONFIGURATION_STATE_CONFIGURED)) { + goto end; + } + +#ifdef BT_ASSERT_PRE + BT_ASSERT_PRE(graph->has_sink, "Graph has no sink component: %!+g", graph); +#endif + + graph->config_state = BT_GRAPH_CONFIGURATION_STATE_PARTIALLY_CONFIGURED; + + for (i = 0; i < graph->components->len; i++) { + struct bt_component *comp = graph->components->pdata[i]; + struct bt_component_sink *comp_sink = (void *) comp; + struct bt_component_class_sink *comp_cls_sink = + (void *) comp->class; + + if (comp->class->type != BT_COMPONENT_CLASS_TYPE_SINK) { + continue; + } + + if (comp_sink->graph_is_configured_method_called) { + continue; + } + + if (comp_cls_sink->methods.graph_is_configured) { + enum bt_self_component_status comp_status; + +#ifdef BT_LIB_LOGD + BT_LIB_LOGD("Calling user's \"graph is configured\" method: " + "%![graph-]+g, %![comp-]+c", + graph, comp); +#endif + + comp_status = comp_cls_sink->methods.graph_is_configured( + (void *) comp_sink); + +#ifdef BT_LIB_LOGD + BT_LIB_LOGD("User method returned: status=%s", + bt_self_component_status_string(comp_status)); +#endif + +#ifdef BT_ASSERT_PRE + BT_ASSERT_PRE(comp_status == BT_SELF_COMPONENT_STATUS_OK || + comp_status == BT_SELF_COMPONENT_STATUS_ERROR || + comp_status == BT_SELF_COMPONENT_STATUS_NOMEM, + "Unexpected returned status: status=%s", + bt_self_component_status_string(comp_status)); +#endif + + if (comp_status != BT_SELF_COMPONENT_STATUS_OK) { + status = BT_GRAPH_STATUS_ERROR; +#ifdef BT_LIB_LOGW + BT_LIB_LOGW("User's \"graph is configured\" method failed: " + "%![comp-]+c, status=%s", + comp, + bt_self_component_status_string( + comp_status)); +#endif + + goto end; + } + } + + comp_sink->graph_is_configured_method_called = true; + } + + graph->config_state = BT_GRAPH_CONFIGURATION_STATE_CONFIGURED; + +end: + return status; +} + +static inline +void bt_graph_make_faulty(struct bt_graph *graph) +{ + graph->config_state = BT_GRAPH_CONFIGURATION_STATE_FAULTY; +#ifdef BT_LIB_LOGD + BT_LIB_LOGD("Set graph's state to faulty: %![graph-]+g", graph); +#endif +} + +#endif /* BABELTRACE_GRAPH_GRAPH_INTERNAL_H */ diff --git a/src/lib/graph/iterator.c b/src/lib/graph/iterator.c new file mode 100644 index 00000000..4b876998 --- /dev/null +++ b/src/lib/graph/iterator.c @@ -0,0 +1,1473 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-ITER" +#include "lib/lib-logging.h" + +#include "compat/compiler.h" +#include "lib/trace-ir/clock-class.h" +#include "lib/trace-ir/clock-snapshot.h" +#include +#include +#include "lib/trace-ir/event.h" +#include +#include "lib/trace-ir/packet.h" +#include "lib/trace-ir/stream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include +#include + +#include "component-class.h" +#include "component-class-sink-colander.h" +#include "component.h" +#include "component-sink.h" +#include "component-source.h" +#include "connection.h" +#include "graph.h" +#include "message/discarded-items.h" +#include "message/event.h" +#include "message/iterator.h" +#include "message/message.h" +#include "message/message-iterator-inactivity.h" +#include "message/stream.h" +#include "message/packet.h" +#include "message/stream-activity.h" + +/* + * TODO: Use graph's state (number of active iterators, etc.) and + * possibly system specifications to make a better guess than this. + */ +#define MSG_BATCH_SIZE 15 + +#define BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(_iter) \ + BT_ASSERT_PRE((_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE || \ + (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED || \ + (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN || \ + (_iter)->state == BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR, \ + "Message iterator is in the wrong state: %!+i", _iter) + +static inline +void set_self_comp_port_input_msg_iterator_state( + struct bt_self_component_port_input_message_iterator *iterator, + enum bt_self_component_port_input_message_iterator_state state) +{ + BT_ASSERT(iterator); + BT_LIB_LOGD("Updating message iterator's state: new-state=%s", + bt_self_component_port_input_message_iterator_state_string(state)); + iterator->state = state; +} + +static +void destroy_base_message_iterator(struct bt_object *obj) +{ + struct bt_message_iterator *iterator = (void *) obj; + + BT_ASSERT(iterator); + + if (iterator->msgs) { + g_ptr_array_free(iterator->msgs, TRUE); + iterator->msgs = NULL; + } + + g_free(iterator); +} + +static +void bt_self_component_port_input_message_iterator_destroy(struct bt_object *obj) +{ + struct bt_self_component_port_input_message_iterator *iterator; + + BT_ASSERT(obj); + + /* + * The message iterator's reference count is 0 if we're + * here. Increment it to avoid a double-destroy (possibly + * infinitely recursive). This could happen for example if the + * message iterator's finalization function does + * bt_object_get_ref() (or anything that causes + * bt_object_get_ref() to be called) on itself (ref. count goes + * from 0 to 1), and then bt_object_put_ref(): the reference + * count would go from 1 to 0 again and this function would be + * called again. + */ + obj->ref_count++; + iterator = (void *) obj; + BT_LIB_LOGD("Destroying self component input port message iterator object: " + "%!+i", iterator); + bt_self_component_port_input_message_iterator_try_finalize(iterator); + + if (iterator->connection) { + /* + * Remove ourself from the originating connection so + * that it does not try to finalize a dangling pointer + * later. + */ + bt_connection_remove_iterator(iterator->connection, iterator); + iterator->connection = NULL; + } + + if (iterator->auto_seek_msgs) { + while (!g_queue_is_empty(iterator->auto_seek_msgs)) { + bt_object_put_no_null_check( + g_queue_pop_tail(iterator->auto_seek_msgs)); + } + + g_queue_free(iterator->auto_seek_msgs); + iterator->auto_seek_msgs = NULL; + } + + destroy_base_message_iterator(obj); +} + +BT_HIDDEN +void bt_self_component_port_input_message_iterator_try_finalize( + struct bt_self_component_port_input_message_iterator *iterator) +{ + typedef void (*method_t)(void *); + + struct bt_component_class *comp_class = NULL; + method_t method = NULL; + + BT_ASSERT(iterator); + + switch (iterator->state) { + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED: + /* Skip user finalization if user initialization failed */ + BT_LIB_LOGD("Not finalizing non-initialized message iterator: " + "%!+i", iterator); + goto end; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED: + /* Already finalized */ + BT_LIB_LOGD("Not finalizing message iterator: already finalized: " + "%!+i", iterator); + goto end; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING: + /* Already finalized */ + BT_LIB_LOGF("Message iterator is already being finalized: " + "%!+i", iterator); + abort(); + default: + break; + } + + BT_LIB_LOGD("Finalizing message iterator: %!+i", iterator); + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING); + BT_ASSERT(iterator->upstream_component); + comp_class = iterator->upstream_component->class; + + /* Call user-defined destroy method */ + switch (comp_class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_comp_cls = + (void *) comp_class; + + method = (method_t) src_comp_cls->methods.msg_iter_finalize; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_comp_cls = + (void *) comp_class; + + method = (method_t) flt_comp_cls->methods.msg_iter_finalize; + break; + } + default: + /* Unreachable */ + abort(); + } + + if (method) { + BT_LIB_LOGD("Calling user's finalization method: %!+i", + iterator); + method(iterator); + } + + iterator->upstream_component = NULL; + iterator->upstream_port = NULL; + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED); + BT_LIB_LOGD("Finalized message iterator: %!+i", iterator); + +end: + return; +} + +BT_HIDDEN +void bt_self_component_port_input_message_iterator_set_connection( + struct bt_self_component_port_input_message_iterator *iterator, + struct bt_connection *connection) +{ + BT_ASSERT(iterator); + iterator->connection = connection; + BT_LIB_LOGV("Set message iterator's connection: " + "%![iter-]+i, %![conn-]+x", iterator, connection); +} + +static +int init_message_iterator(struct bt_message_iterator *iterator, + enum bt_message_iterator_type type, + bt_object_release_func destroy) +{ + int ret = 0; + + bt_object_init_shared(&iterator->base, destroy); + iterator->type = type; + iterator->msgs = g_ptr_array_new(); + if (!iterator->msgs) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + ret = -1; + goto end; + } + + g_ptr_array_set_size(iterator->msgs, MSG_BATCH_SIZE); + +end: + return ret; +} + +static +bt_bool can_seek_ns_from_origin_true( + struct bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin) +{ + return BT_TRUE; +} + +static +bt_bool can_seek_beginning_true( + struct bt_self_component_port_input_message_iterator *iterator) +{ + return BT_TRUE; +} + +static +struct bt_self_component_port_input_message_iterator * +bt_self_component_port_input_message_iterator_create_initial( + struct bt_component *upstream_comp, + struct bt_port *upstream_port) +{ + int ret; + struct bt_self_component_port_input_message_iterator *iterator = NULL; + + BT_ASSERT(upstream_comp); + BT_ASSERT(upstream_port); + BT_ASSERT(bt_port_is_connected(upstream_port)); + BT_LIB_LOGD("Creating initial message iterator on self component input port: " + "%![up-comp-]+c, %![up-port-]+p", upstream_comp, upstream_port); + BT_ASSERT(bt_component_get_class_type(upstream_comp) == + BT_COMPONENT_CLASS_TYPE_SOURCE || + bt_component_get_class_type(upstream_comp) == + BT_COMPONENT_CLASS_TYPE_FILTER); + iterator = g_new0( + struct bt_self_component_port_input_message_iterator, 1); + if (!iterator) { + BT_LOGE_STR("Failed to allocate one self component input port " + "message iterator."); + goto end; + } + + ret = init_message_iterator((void *) iterator, + BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT, + bt_self_component_port_input_message_iterator_destroy); + if (ret) { + /* init_message_iterator() logs errors */ + BT_OBJECT_PUT_REF_AND_RESET(iterator); + goto end; + } + + iterator->auto_seek_msgs = g_queue_new(); + if (!iterator->auto_seek_msgs) { + BT_LOGE_STR("Failed to allocate a GQueue."); + ret = -1; + goto end; + } + + iterator->upstream_component = upstream_comp; + iterator->upstream_port = upstream_port; + iterator->connection = iterator->upstream_port->connection; + iterator->graph = bt_component_borrow_graph(upstream_comp); + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED); + + switch (iterator->upstream_component->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_comp_cls = + (void *) iterator->upstream_component->class; + + iterator->methods.next = + (bt_self_component_port_input_message_iterator_next_method) + src_comp_cls->methods.msg_iter_next; + iterator->methods.seek_ns_from_origin = + (bt_self_component_port_input_message_iterator_seek_ns_from_origin_method) + src_comp_cls->methods.msg_iter_seek_ns_from_origin; + iterator->methods.seek_beginning = + (bt_self_component_port_input_message_iterator_seek_beginning_method) + src_comp_cls->methods.msg_iter_seek_beginning; + iterator->methods.can_seek_ns_from_origin = + (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) + src_comp_cls->methods.msg_iter_can_seek_ns_from_origin; + iterator->methods.can_seek_beginning = + (bt_self_component_port_input_message_iterator_can_seek_beginning_method) + src_comp_cls->methods.msg_iter_can_seek_beginning; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_comp_cls = + (void *) iterator->upstream_component->class; + + iterator->methods.next = + (bt_self_component_port_input_message_iterator_next_method) + flt_comp_cls->methods.msg_iter_next; + iterator->methods.seek_ns_from_origin = + (bt_self_component_port_input_message_iterator_seek_ns_from_origin_method) + flt_comp_cls->methods.msg_iter_seek_ns_from_origin; + iterator->methods.seek_beginning = + (bt_self_component_port_input_message_iterator_seek_beginning_method) + flt_comp_cls->methods.msg_iter_seek_beginning; + iterator->methods.can_seek_ns_from_origin = + (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) + flt_comp_cls->methods.msg_iter_can_seek_ns_from_origin; + iterator->methods.can_seek_beginning = + (bt_self_component_port_input_message_iterator_can_seek_beginning_method) + flt_comp_cls->methods.msg_iter_can_seek_beginning; + break; + } + default: + abort(); + } + + if (iterator->methods.seek_ns_from_origin && + !iterator->methods.can_seek_ns_from_origin) { + iterator->methods.can_seek_ns_from_origin = + (bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method) + can_seek_ns_from_origin_true; + } + + if (iterator->methods.seek_beginning && + !iterator->methods.can_seek_beginning) { + iterator->methods.can_seek_beginning = + (bt_self_component_port_input_message_iterator_seek_beginning_method) + can_seek_beginning_true; + } + + BT_LIB_LOGD("Created initial message iterator on self component input port: " + "%![up-port-]+p, %![up-comp-]+c, %![iter-]+i", + upstream_port, upstream_comp, iterator); + +end: + return iterator; +} + +struct bt_self_component_port_input_message_iterator * +bt_self_component_port_input_message_iterator_create( + struct bt_self_component_port_input *self_port) +{ + typedef enum bt_self_message_iterator_status (*init_method_t)( + void *, void *, void *); + + init_method_t init_method = NULL; + struct bt_self_component_port_input_message_iterator *iterator = + NULL; + struct bt_port *port = (void *) self_port; + struct bt_port *upstream_port; + struct bt_component *comp; + struct bt_component *upstream_comp; + struct bt_component_class *upstream_comp_cls; + + BT_ASSERT_PRE_NON_NULL(port, "Port"); + comp = bt_port_borrow_component_inline(port); + BT_ASSERT_PRE(bt_port_is_connected(port), + "Port is not connected: %![port-]+p", port); + BT_ASSERT_PRE(comp, "Port is not part of a component: %![port-]+p", + port); + BT_ASSERT_PRE(!bt_component_graph_is_canceled(comp), + "Port's component's graph is canceled: " + "%![port-]+p, %![comp-]+c", port, comp); + BT_ASSERT(port->connection); + upstream_port = port->connection->upstream_port; + BT_ASSERT(upstream_port); + upstream_comp = bt_port_borrow_component_inline(upstream_port); + BT_ASSERT(upstream_comp); + BT_ASSERT_PRE( + bt_component_borrow_graph(upstream_comp)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(upstream_comp)); + upstream_comp_cls = upstream_comp->class; + BT_ASSERT(upstream_comp->class->type == + BT_COMPONENT_CLASS_TYPE_SOURCE || + upstream_comp->class->type == + BT_COMPONENT_CLASS_TYPE_FILTER); + iterator = bt_self_component_port_input_message_iterator_create_initial( + upstream_comp, upstream_port); + if (!iterator) { + BT_LOGW_STR("Cannot create self component input port " + "message iterator."); + goto end; + } + + switch (upstream_comp_cls->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_comp_cls = + (void *) upstream_comp_cls; + + init_method = + (init_method_t) src_comp_cls->methods.msg_iter_init; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_comp_cls = + (void *) upstream_comp_cls; + + init_method = + (init_method_t) flt_comp_cls->methods.msg_iter_init; + break; + } + default: + /* Unreachable */ + abort(); + } + + if (init_method) { + int iter_status; + + BT_LIB_LOGD("Calling user's initialization method: %!+i", iterator); + iter_status = init_method(iterator, upstream_comp, + upstream_port); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(iter_status)); + if (iter_status != BT_MESSAGE_ITERATOR_STATUS_OK) { + BT_LOGW_STR("Initialization method failed."); + BT_OBJECT_PUT_REF_AND_RESET(iterator); + goto end; + } + } + + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); + g_ptr_array_add(port->connection->iterators, iterator); + BT_LIB_LOGD("Created message iterator on self component input port: " + "%![up-port-]+p, %![up-comp-]+c, %![iter-]+i", + upstream_port, upstream_comp, iterator); + +end: + return iterator; +} + +void *bt_self_message_iterator_get_data( + const struct bt_self_message_iterator *self_iterator) +{ + struct bt_self_component_port_input_message_iterator *iterator = + (void *) self_iterator; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return iterator->user_data; +} + +void bt_self_message_iterator_set_data( + struct bt_self_message_iterator *self_iterator, void *data) +{ + struct bt_self_component_port_input_message_iterator *iterator = + (void *) self_iterator; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + iterator->user_data = data; + BT_LIB_LOGV("Set message iterator's user data: " + "%!+i, user-data-addr=%p", iterator, data); +} + +enum bt_message_iterator_status +bt_self_component_port_input_message_iterator_next( + struct bt_self_component_port_input_message_iterator *iterator, + bt_message_array_const *msgs, uint64_t *user_count) +{ + int status = BT_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(msgs, "Message array (output)"); + BT_ASSERT_PRE_NON_NULL(user_count, "Message count (output)"); + BT_ASSERT_PRE(iterator->state == + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE, + "Message iterator's \"next\" called, but " + "message iterator is in the wrong state: %!+i", iterator); + BT_ASSERT(iterator->upstream_component); + BT_ASSERT(iterator->upstream_component->class); + BT_ASSERT_PRE( + bt_component_borrow_graph(iterator->upstream_component)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(iterator->upstream_component)); + BT_LIB_LOGD("Getting next self component input port " + "message iterator's messages: %!+i", iterator); + + /* + * Call the user's "next" method to get the next messages + * and status. + */ + BT_ASSERT(iterator->methods.next); + BT_LOGD_STR("Calling user's \"next\" method."); + status = iterator->methods.next(iterator, + (void *) iterator->base.msgs->pdata, MSG_BATCH_SIZE, + user_count); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(status)); + if (status < 0) { + BT_LOGW_STR("User method failed."); + goto end; + } + + /* + * There is no way that this iterator could have been finalized + * during its "next" method, as the only way to do this is to + * put the last iterator's reference, and this can only be done + * by its downstream owner. + * + * For the same reason, there is no way that this iterator could + * have seeked (cannot seek a self message iterator). + */ + BT_ASSERT(iterator->state == + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); + + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + BT_ASSERT_PRE(*user_count <= MSG_BATCH_SIZE, + "Invalid returned message count: greater than " + "batch size: count=%" PRIu64 ", batch-size=%u", + *user_count, MSG_BATCH_SIZE); + *msgs = (void *) iterator->base.msgs->pdata; + break; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + goto end; + case BT_MESSAGE_ITERATOR_STATUS_END: + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED); + goto end; + default: + /* Unknown non-error status */ + abort(); + } + +end: + return status; +} + +enum bt_message_iterator_status bt_port_output_message_iterator_next( + struct bt_port_output_message_iterator *iterator, + bt_message_array_const *msgs_to_user, + uint64_t *count_to_user) +{ + enum bt_message_iterator_status status; + enum bt_graph_status graph_status; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(msgs_to_user, "Message array (output)"); + BT_ASSERT_PRE_NON_NULL(count_to_user, "Message count (output)"); + BT_LIB_LOGD("Getting next output port message iterator's messages: " + "%!+i", iterator); + graph_status = bt_graph_consume_sink_no_check(iterator->graph, + iterator->colander); + switch (graph_status) { + case BT_GRAPH_STATUS_CANCELED: + case BT_GRAPH_STATUS_AGAIN: + case BT_GRAPH_STATUS_END: + case BT_GRAPH_STATUS_NOMEM: + status = (int) graph_status; + break; + case BT_GRAPH_STATUS_OK: + status = BT_MESSAGE_ITERATOR_STATUS_OK; + + /* + * On success, the colander sink moves the messages + * to this iterator's array and sets this iterator's + * message count: move them to the user. + */ + *msgs_to_user = (void *) iterator->base.msgs->pdata; + *count_to_user = iterator->count; + break; + default: + /* Other errors */ + status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + } + + return status; +} + +struct bt_component * +bt_self_component_port_input_message_iterator_borrow_component( + struct bt_self_component_port_input_message_iterator *iterator) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return iterator->upstream_component; +} + +const struct bt_component * +bt_self_component_port_input_message_iterator_borrow_component_const( + const struct bt_self_component_port_input_message_iterator *iterator) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return iterator->upstream_component; +} + +struct bt_self_component *bt_self_message_iterator_borrow_component( + struct bt_self_message_iterator *self_iterator) +{ + struct bt_self_component_port_input_message_iterator *iterator = + (void *) self_iterator; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return (void *) iterator->upstream_component; +} + +struct bt_self_port_output *bt_self_message_iterator_borrow_port( + struct bt_self_message_iterator *self_iterator) +{ + struct bt_self_component_port_input_message_iterator *iterator = + (void *) self_iterator; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return (void *) iterator->upstream_port; +} + +static +void bt_port_output_message_iterator_destroy(struct bt_object *obj) +{ + struct bt_port_output_message_iterator *iterator = (void *) obj; + + BT_LIB_LOGD("Destroying output port message iterator object: %!+i", + iterator); + BT_LOGD_STR("Putting graph."); + BT_OBJECT_PUT_REF_AND_RESET(iterator->graph); + BT_LOGD_STR("Putting colander sink component."); + BT_OBJECT_PUT_REF_AND_RESET(iterator->colander); + destroy_base_message_iterator(obj); +} + +struct bt_port_output_message_iterator * +bt_port_output_message_iterator_create(struct bt_graph *graph, + const struct bt_port_output *output_port) +{ + struct bt_port_output_message_iterator *iterator = NULL; + struct bt_component_class_sink *colander_comp_cls = NULL; + struct bt_component *output_port_comp = NULL; + struct bt_component_sink *colander_comp; + enum bt_graph_status graph_status; + struct bt_port_input *colander_in_port = NULL; + struct bt_component_class_sink_colander_data colander_data; + int ret; + + BT_ASSERT_PRE_NON_NULL(graph, "Graph"); + BT_ASSERT_PRE_NON_NULL(output_port, "Output port"); + output_port_comp = bt_port_borrow_component_inline( + (const void *) output_port); + BT_ASSERT_PRE(output_port_comp, + "Output port has no component: %!+p", output_port); + BT_ASSERT_PRE(bt_component_borrow_graph(output_port_comp) == + (void *) graph, + "Output port is not part of graph: %![graph-]+g, %![port-]+p", + graph, output_port); + BT_ASSERT_PRE(!graph->has_sink, + "Graph already has a sink component: %![graph-]+g"); + + /* Create message iterator */ + BT_LIB_LOGD("Creating message iterator on output port: " + "%![port-]+p, %![comp-]+c", output_port, output_port_comp); + iterator = g_new0(struct bt_port_output_message_iterator, 1); + if (!iterator) { + BT_LOGE_STR("Failed to allocate one output port message iterator."); + goto error; + } + + ret = init_message_iterator((void *) iterator, + BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT, + bt_port_output_message_iterator_destroy); + if (ret) { + /* init_message_iterator() logs errors */ + BT_OBJECT_PUT_REF_AND_RESET(iterator); + goto end; + } + + /* Create colander component */ + colander_comp_cls = bt_component_class_sink_colander_get(); + if (!colander_comp_cls) { + BT_LOGW("Cannot get colander sink component class."); + goto error; + } + + iterator->graph = graph; + bt_object_get_no_null_check(iterator->graph); + colander_data.msgs = (void *) iterator->base.msgs->pdata; + colander_data.count_addr = &iterator->count; + + /* Hope that nobody uses this very unique name */ + graph_status = + bt_graph_add_sink_component_with_init_method_data( + (void *) graph, colander_comp_cls, + "colander-36ac3409-b1a8-4d60-ab1f-4fdf341a8fb1", + NULL, &colander_data, (void *) &iterator->colander); + if (graph_status != BT_GRAPH_STATUS_OK) { + BT_LIB_LOGW("Cannot add colander sink component to graph: " + "%1[graph-]+g, status=%s", graph, + bt_graph_status_string(graph_status)); + goto error; + } + + /* + * Connect provided output port to the colander component's + * input port. + */ + colander_in_port = + (void *) bt_component_sink_borrow_input_port_by_index_const( + (void *) iterator->colander, 0); + BT_ASSERT(colander_in_port); + graph_status = bt_graph_connect_ports(graph, + output_port, colander_in_port, NULL); + if (graph_status != BT_GRAPH_STATUS_OK) { + BT_LIB_LOGW("Cannot add colander sink component to graph: " + "%![graph-]+g, %![comp-]+c, status=%s", graph, + iterator->colander, + bt_graph_status_string(graph_status)); + goto error; + } + + /* + * At this point everything went fine. Make the graph + * nonconsumable forever so that only this message iterator + * can consume (thanks to bt_graph_consume_sink_no_check()). + * This avoids leaking the message created by the colander + * sink and moved to the message iterator's message + * member. + */ + bt_graph_set_can_consume(iterator->graph, false); + + /* Also set the graph as being configured. */ + graph_status = bt_graph_configure(graph); + if (graph_status != BT_GRAPH_STATUS_OK) { + BT_LIB_LOGW("Cannot configure graph after having added colander: " + "%![graph-]+g, status=%s", graph, + bt_graph_status_string(graph_status)); + goto error; + } + goto end; + +error: + if (iterator && iterator->graph && iterator->colander) { + int ret; + + /* Remove created colander component from graph if any */ + colander_comp = iterator->colander; + BT_OBJECT_PUT_REF_AND_RESET(iterator->colander); + + /* + * At this point the colander component's reference + * count is 0 because iterator->colander was the only + * owner. We also know that it is not connected because + * this is the last operation before this function + * succeeds. + * + * Since we honor the preconditions here, + * bt_graph_remove_unconnected_component() always + * succeeds. + */ + ret = bt_graph_remove_unconnected_component(iterator->graph, + (void *) colander_comp); + BT_ASSERT(ret == 0); + } + + BT_OBJECT_PUT_REF_AND_RESET(iterator); + +end: + bt_object_put_ref(colander_comp_cls); + return (void *) iterator; +} + +bt_bool bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( + struct bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin) +{ + bt_bool can = BT_FALSE; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); + BT_ASSERT_PRE( + bt_component_borrow_graph(iterator->upstream_component)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(iterator->upstream_component)); + + if (iterator->methods.can_seek_ns_from_origin) { + can = iterator->methods.can_seek_ns_from_origin(iterator, + ns_from_origin); + goto end; + } + + /* + * Automatic seeking fall back: if we can seek to the beginning, + * then we can automatically seek to any message. + */ + if (iterator->methods.can_seek_beginning) { + can = iterator->methods.can_seek_beginning(iterator); + } + +end: + return can; +} + +bt_bool bt_self_component_port_input_message_iterator_can_seek_beginning( + struct bt_self_component_port_input_message_iterator *iterator) +{ + bt_bool can = BT_FALSE; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); + BT_ASSERT_PRE( + bt_component_borrow_graph(iterator->upstream_component)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(iterator->upstream_component)); + + if (iterator->methods.can_seek_beginning) { + can = iterator->methods.can_seek_beginning(iterator); + } + + return can; +} + +static inline +void set_iterator_state_after_seeking( + struct bt_self_component_port_input_message_iterator *iterator, + enum bt_message_iterator_status status) +{ + enum bt_self_component_port_input_message_iterator_state new_state = 0; + + /* Set iterator's state depending on seeking status */ + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE; + break; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN; + break; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR; + break; + case BT_MESSAGE_ITERATOR_STATUS_END: + new_state = BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED; + break; + default: + abort(); + } + + set_self_comp_port_input_msg_iterator_state(iterator, new_state); +} + +enum bt_message_iterator_status +bt_self_component_port_input_message_iterator_seek_beginning( + struct bt_self_component_port_input_message_iterator *iterator) +{ + int status; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); + BT_ASSERT_PRE( + bt_component_borrow_graph(iterator->upstream_component)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(iterator->upstream_component)); + BT_ASSERT_PRE( + bt_self_component_port_input_message_iterator_can_seek_beginning( + iterator), + "Message iterator cannot seek beginning: %!+i", iterator); + BT_LIB_LOGD("Calling user's \"seek beginning\" method: %!+i", iterator); + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING); + status = iterator->methods.seek_beginning(iterator); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(status)); + BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || + status == BT_MESSAGE_ITERATOR_STATUS_ERROR || + status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || + status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, + "Unexpected status: %![iter-]+i, status=%s", + iterator, bt_common_self_message_iterator_status_string(status)); + set_iterator_state_after_seeking(iterator, status); + return status; +} + +static inline +enum bt_message_iterator_status auto_seek_handle_message( + struct bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin, const struct bt_message *msg, + bool *got_first) +{ + enum bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; + int64_t msg_ns_from_origin; + const struct bt_clock_snapshot *clk_snapshot = NULL; + int ret; + + BT_ASSERT(msg); + BT_ASSERT(got_first); + + switch (msg->type) { + case BT_MESSAGE_TYPE_EVENT: + { + const struct bt_message_event *event_msg = + (const void *) msg; + + clk_snapshot = event_msg->default_cs; + BT_ASSERT_PRE(clk_snapshot, + "Event message has no default clock snapshot: %!+n", + event_msg); + break; + } + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + { + const struct bt_message_message_iterator_inactivity *inactivity_msg = + (const void *) msg; + + clk_snapshot = inactivity_msg->default_cs; + BT_ASSERT(clk_snapshot); + break; + } + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + case BT_MESSAGE_TYPE_PACKET_END: + { + const struct bt_message_packet *packet_msg = + (const void *) msg; + + clk_snapshot = packet_msg->default_cs; + BT_ASSERT_PRE(clk_snapshot, + "Packet message has no default clock snapshot: %!+n", + packet_msg); + break; + } + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + { + struct bt_message_discarded_items *msg_disc_items = + (void *) msg; + + BT_ASSERT_PRE(msg_disc_items->default_begin_cs && + msg_disc_items->default_end_cs, + "Discarded events/packets message has no default clock snapshots: %!+n", + msg_disc_items); + ret = bt_clock_snapshot_get_ns_from_origin( + msg_disc_items->default_begin_cs, + &msg_ns_from_origin); + if (ret) { + status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (msg_ns_from_origin >= ns_from_origin) { + *got_first = true; + goto push_msg; + } + + ret = bt_clock_snapshot_get_ns_from_origin( + msg_disc_items->default_end_cs, + &msg_ns_from_origin); + if (ret) { + status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (msg_ns_from_origin >= ns_from_origin) { + /* + * The discarded items message's beginning time + * is before the requested seeking time, but its + * end time is after. Modify the message so as + * to set its beginning time to the requested + * seeking time, and make its item count unknown + * as we don't know if items were really + * discarded within the new time range. + */ + uint64_t new_begin_raw_value; + + ret = bt_clock_class_clock_value_from_ns_from_origin( + msg_disc_items->default_end_cs->clock_class, + ns_from_origin, &new_begin_raw_value); + if (ret) { + status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + bt_clock_snapshot_set_raw_value( + msg_disc_items->default_begin_cs, + new_begin_raw_value); + msg_disc_items->count.base.avail = + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE; + + /* + * It is safe to push it because its beginning + * time is exactly the requested seeking time. + */ + goto push_msg; + } else { + goto skip_msg; + } + } + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + { + const struct bt_message_stream_activity *stream_act_msg = + (const void *) msg; + + switch (stream_act_msg->default_cs_state) { + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: + /* + * -inf is always less than any requested time, + * and we can't assume any specific time for an + * unknown clock snapshot, so skip this. + */ + goto skip_msg; + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: + clk_snapshot = stream_act_msg->default_cs; + BT_ASSERT(clk_snapshot); + break; + default: + abort(); + } + + break; + } + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + { + const struct bt_message_stream_activity *stream_act_msg = + (const void *) msg; + + switch (stream_act_msg->default_cs_state) { + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: + /* + * We can't assume any specific time for an + * unknown clock snapshot, so skip this. + */ + goto skip_msg; + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: + /* + * +inf is always greater than any requested + * time. + */ + *got_first = true; + goto push_msg; + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: + clk_snapshot = stream_act_msg->default_cs; + BT_ASSERT(clk_snapshot); + break; + default: + abort(); + } + + break; + } + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + case BT_MESSAGE_TYPE_STREAM_END: + /* Ignore */ + goto skip_msg; + default: + abort(); + } + + BT_ASSERT(clk_snapshot); + ret = bt_clock_snapshot_get_ns_from_origin(clk_snapshot, + &msg_ns_from_origin); + if (ret) { + status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (msg_ns_from_origin >= ns_from_origin) { + *got_first = true; + goto push_msg; + } + +skip_msg: + bt_object_put_no_null_check(msg); + msg = NULL; + goto end; + +push_msg: + g_queue_push_head(iterator->auto_seek_msgs, (void *) msg); + msg = NULL; + +end: + BT_ASSERT(!msg || status != BT_MESSAGE_ITERATOR_STATUS_OK); + return status; +} + +static +enum bt_message_iterator_status find_message_ge_ns_from_origin( + struct bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin) +{ + int status; + enum bt_self_component_port_input_message_iterator_state init_state = + iterator->state; + const struct bt_message *messages[MSG_BATCH_SIZE]; + uint64_t user_count = 0; + uint64_t i; + bool got_first = false; + + BT_ASSERT(iterator); + memset(&messages[0], 0, sizeof(messages[0]) * MSG_BATCH_SIZE); + + /* + * Make this iterator temporarily active (not seeking) to call + * the "next" method. + */ + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); + + BT_ASSERT(iterator->methods.next); + + while (!got_first) { + /* + * Call the user's "next" method to get the next + * messages and status. + */ + BT_LOGD_STR("Calling user's \"next\" method."); + status = iterator->methods.next(iterator, + &messages[0], MSG_BATCH_SIZE, &user_count); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(status)); + + /* + * The user's "next" method must not do any action which + * would change the iterator's state. + */ + BT_ASSERT(iterator->state == + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE); + + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + BT_ASSERT_PRE(user_count <= MSG_BATCH_SIZE, + "Invalid returned message count: greater than " + "batch size: count=%" PRIu64 ", batch-size=%u", + user_count, MSG_BATCH_SIZE); + break; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + case BT_MESSAGE_ITERATOR_STATUS_END: + goto end; + default: + abort(); + } + + for (i = 0; i < user_count; i++) { + if (got_first) { + g_queue_push_head(iterator->auto_seek_msgs, + (void *) messages[i]); + messages[i] = NULL; + continue; + } + + status = auto_seek_handle_message(iterator, + ns_from_origin, messages[i], &got_first); + if (status == BT_MESSAGE_ITERATOR_STATUS_OK) { + /* Message was either pushed or moved */ + messages[i] = NULL; + } else { + goto end; + } + } + } + +end: + for (i = 0; i < user_count; i++) { + if (messages[i]) { + bt_object_put_no_null_check(messages[i]); + } + } + + set_self_comp_port_input_msg_iterator_state(iterator, init_state); + return status; +} + +static +enum bt_self_message_iterator_status post_auto_seek_next( + struct bt_self_component_port_input_message_iterator *iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + BT_ASSERT(!g_queue_is_empty(iterator->auto_seek_msgs)); + *count = 0; + + /* + * Move auto-seek messages to the output array (which is this + * iterator's base message array). + */ + while (capacity > 0 && !g_queue_is_empty(iterator->auto_seek_msgs)) { + msgs[*count] = g_queue_pop_tail(iterator->auto_seek_msgs); + capacity--; + (*count)++; + } + + BT_ASSERT(*count > 0); + + if (g_queue_is_empty(iterator->auto_seek_msgs)) { + /* No more auto-seek messages */ + switch (iterator->upstream_component->class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_comp_cls = + (void *) iterator->upstream_component->class; + + iterator->methods.next = + (bt_self_component_port_input_message_iterator_next_method) + src_comp_cls->methods.msg_iter_next; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_comp_cls = + (void *) iterator->upstream_component->class; + + iterator->methods.next = + (bt_self_component_port_input_message_iterator_next_method) + flt_comp_cls->methods.msg_iter_next; + break; + } + default: + abort(); + } + } + + return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; +} + +enum bt_message_iterator_status +bt_self_component_port_input_message_iterator_seek_ns_from_origin( + struct bt_self_component_port_input_message_iterator *iterator, + int64_t ns_from_origin) +{ + int status; + + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + BT_ASSERT_PRE_ITER_HAS_STATE_TO_SEEK(iterator); + BT_ASSERT_PRE( + bt_component_borrow_graph(iterator->upstream_component)->config_state != + BT_GRAPH_CONFIGURATION_STATE_CONFIGURING, + "Graph is not configured: %!+g", + bt_component_borrow_graph(iterator->upstream_component)); + BT_ASSERT_PRE( + bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( + iterator, ns_from_origin), + "Message iterator cannot seek nanoseconds from origin: %!+i, " + "ns-from-origin=%" PRId64, iterator, ns_from_origin); + set_self_comp_port_input_msg_iterator_state(iterator, + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING); + + if (iterator->methods.seek_ns_from_origin) { + BT_LIB_LOGD("Calling user's \"seek nanoseconds from origin\" method: " + "%![iter-]+i, ns=%" PRId64, iterator, ns_from_origin); + status = iterator->methods.seek_ns_from_origin(iterator, + ns_from_origin); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(status)); + BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || + status == BT_MESSAGE_ITERATOR_STATUS_ERROR || + status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || + status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, + "Unexpected status: %![iter-]+i, status=%s", + iterator, + bt_common_self_message_iterator_status_string(status)); + } else { + /* Start automatic seeking: seek beginning first */ + BT_ASSERT(iterator->methods.can_seek_beginning(iterator)); + BT_ASSERT(iterator->methods.seek_beginning); + BT_LIB_LOGD("Calling user's \"seek beginning\" method: %!+i", + iterator); + status = iterator->methods.seek_beginning(iterator); + BT_LOGD("User method returned: status=%s", + bt_message_iterator_status_string(status)); + BT_ASSERT_PRE(status == BT_MESSAGE_ITERATOR_STATUS_OK || + status == BT_MESSAGE_ITERATOR_STATUS_ERROR || + status == BT_MESSAGE_ITERATOR_STATUS_NOMEM || + status == BT_MESSAGE_ITERATOR_STATUS_AGAIN, + "Unexpected status: %![iter-]+i, status=%s", + iterator, + bt_common_self_message_iterator_status_string(status)); + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + break; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + goto end; + default: + abort(); + } + + /* + * Find the first message which has a default clock + * snapshot greater than or equal to the requested + * seeking time, and move the received messages from + * this point in the batch to this iterator's auto-seek + * message queue. + */ + while (!g_queue_is_empty(iterator->auto_seek_msgs)) { + bt_object_put_no_null_check( + g_queue_pop_tail(iterator->auto_seek_msgs)); + } + + status = find_message_ge_ns_from_origin(iterator, + ns_from_origin); + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + case BT_MESSAGE_ITERATOR_STATUS_END: + /* + * If there are messages in the auto-seek + * message queue, replace the user's "next" + * method with a custom, temporary "next" method + * which returns them. + */ + if (!g_queue_is_empty(iterator->auto_seek_msgs)) { + iterator->methods.next = + (bt_self_component_port_input_message_iterator_next_method) + post_auto_seek_next; + } + + /* + * `BT_MESSAGE_ITERATOR_STATUS_END` becomes + * `BT_MESSAGE_ITERATOR_STATUS_OK`: the next + * time this iterator's "next" method is called, + * it will return + * `BT_MESSAGE_ITERATOR_STATUS_END`. + */ + status = BT_MESSAGE_ITERATOR_STATUS_OK; + break; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + goto end; + default: + abort(); + } + } + +end: + set_iterator_state_after_seeking(iterator, status); + return status; +} + +static inline +bt_self_component_port_input_message_iterator * +borrow_output_port_message_iterator_upstream_iterator( + struct bt_port_output_message_iterator *iterator) +{ + struct bt_component_class_sink_colander_priv_data *colander_data; + + BT_ASSERT(iterator); + colander_data = (void *) iterator->colander->parent.user_data; + BT_ASSERT(colander_data); + BT_ASSERT(colander_data->msg_iter); + return colander_data->msg_iter; +} + +bt_bool bt_port_output_message_iterator_can_seek_ns_from_origin( + struct bt_port_output_message_iterator *iterator, + int64_t ns_from_origin) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( + borrow_output_port_message_iterator_upstream_iterator( + iterator), ns_from_origin); +} + +bt_bool bt_port_output_message_iterator_can_seek_beginning( + struct bt_port_output_message_iterator *iterator) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return bt_self_component_port_input_message_iterator_can_seek_beginning( + borrow_output_port_message_iterator_upstream_iterator( + iterator)); +} + +enum bt_message_iterator_status bt_port_output_message_iterator_seek_ns_from_origin( + struct bt_port_output_message_iterator *iterator, + int64_t ns_from_origin) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return bt_self_component_port_input_message_iterator_seek_ns_from_origin( + borrow_output_port_message_iterator_upstream_iterator(iterator), + ns_from_origin); +} + +enum bt_message_iterator_status bt_port_output_message_iterator_seek_beginning( + struct bt_port_output_message_iterator *iterator) +{ + BT_ASSERT_PRE_NON_NULL(iterator, "Message iterator"); + return bt_self_component_port_input_message_iterator_seek_beginning( + borrow_output_port_message_iterator_upstream_iterator( + iterator)); +} + +void bt_port_output_message_iterator_get_ref( + const struct bt_port_output_message_iterator *iterator) +{ + bt_object_get_ref(iterator); +} + +void bt_port_output_message_iterator_put_ref( + const struct bt_port_output_message_iterator *iterator) +{ + bt_object_put_ref(iterator); +} + +void bt_self_component_port_input_message_iterator_get_ref( + const struct bt_self_component_port_input_message_iterator *iterator) +{ + bt_object_get_ref(iterator); +} + +void bt_self_component_port_input_message_iterator_put_ref( + const struct bt_self_component_port_input_message_iterator *iterator) +{ + bt_object_put_ref(iterator); +} diff --git a/src/lib/graph/message/Makefile.am b/src/lib/graph/message/Makefile.am new file mode 100644 index 00000000..b0053ceb --- /dev/null +++ b/src/lib/graph/message/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libgraph-message.la + +libgraph_message_la_SOURCES = \ + discarded-items.c \ + discarded-items.h \ + event.c \ + event.h \ + iterator.h \ + message.c \ + message.h \ + message-iterator-inactivity.c \ + message-iterator-inactivity.h \ + packet.c \ + packet.h \ + stream-activity.c \ + stream-activity.h \ + stream.c \ + stream.h diff --git a/src/lib/graph/message/discarded-items.c b/src/lib/graph/message/discarded-items.c new file mode 100644 index 00000000..646a7e4f --- /dev/null +++ b/src/lib/graph/message/discarded-items.c @@ -0,0 +1,390 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-DISCARDED-ITEMS" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/trace-ir/stream-class.h" +#include "lib/trace-ir/stream.h" +#include "lib/property.h" +#include "lib/graph/message/message.h" +#include +#include +#include +#include + +#include "discarded-items.h" + +static +void destroy_discarded_items_message(struct bt_object *obj) +{ + struct bt_message_discarded_items *message = (void *) obj; + + BT_LIB_LOGD("Destroying discarded items message: %!+n", message); + BT_LIB_LOGD("Putting stream: %!+s", message->stream); + BT_OBJECT_PUT_REF_AND_RESET(message->stream); + + if (message->default_begin_cs) { + bt_clock_snapshot_recycle(message->default_begin_cs); + message->default_begin_cs = NULL; + } + + if (message->default_end_cs) { + bt_clock_snapshot_recycle(message->default_end_cs); + message->default_end_cs = NULL; + } + + g_free(message); +} + +static inline +struct bt_message *create_discarded_items_message( + struct bt_self_message_iterator *self_msg_iter, + enum bt_message_type type, struct bt_stream *stream, + bool with_cs, + uint64_t beginning_raw_value, uint64_t end_raw_value) +{ + struct bt_message_discarded_items *message; + struct bt_stream_class *stream_class; + bool has_support; + bool need_cs; + + BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + stream_class = bt_stream_borrow_class(stream); + BT_ASSERT(stream_class); + + if (type == BT_MESSAGE_TYPE_DISCARDED_EVENTS) { + has_support = stream_class->supports_discarded_events; + need_cs = stream_class->discarded_events_have_default_clock_snapshots; + } else { + has_support = stream_class->supports_discarded_packets; + need_cs = stream_class->discarded_packets_have_default_clock_snapshots; + } + + BT_ASSERT_PRE(has_support, + "Stream class does not support discarded events or packets: " + "type=%s, %![stream-]+s, %![sc-]+S", + bt_message_type_string(type), stream, stream_class); + BT_ASSERT_PRE(need_cs ? with_cs : true, + "Unexpected stream class configuration when creating " + "a discarded events or discarded packets message: " + "default clock snapshots are needed, but none was provided: " + "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " + "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, + bt_message_type_string(type), stream, stream_class, + with_cs, beginning_raw_value, end_raw_value); + BT_ASSERT_PRE(!need_cs ? !with_cs : true, + "Unexpected stream class configuration when creating " + "a discarded events or discarded packets message: " + "no default clock snapshots are needed, but two were provided: " + "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " + "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, + bt_message_type_string(type), stream, stream_class, + with_cs, beginning_raw_value, end_raw_value); + BT_LIB_LOGD("Creating discarded items message object: " + "type=%s, %![stream-]+s, %![sc-]+S, with-cs=%d, " + "cs-begin-val=%" PRIu64 ", cs-end-val=%" PRIu64, + bt_message_type_string(type), stream, stream_class, + with_cs, beginning_raw_value, end_raw_value); + message = g_new0(struct bt_message_discarded_items, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one discarded items message."); + goto error; + } + + bt_message_init(&message->parent, type, + destroy_discarded_items_message, NULL); + message->stream = stream; + bt_object_get_no_null_check(message->stream); + + if (with_cs) { + BT_ASSERT(stream_class->default_clock_class); + message->default_begin_cs = bt_clock_snapshot_create( + stream_class->default_clock_class); + if (!message->default_begin_cs) { + goto error; + } + + bt_clock_snapshot_set_raw_value(message->default_begin_cs, + beginning_raw_value); + + message->default_end_cs = bt_clock_snapshot_create( + stream_class->default_clock_class); + if (!message->default_end_cs) { + goto error; + } + + bt_clock_snapshot_set_raw_value(message->default_end_cs, + end_raw_value); + } + + bt_property_uint_init(&message->count, + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, 0); + BT_LIB_LOGD("Created discarded items message object: " + "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, + stream, stream_class); + + return (void *) &message->parent; + +error: + return NULL; +} + +static inline +struct bt_stream *borrow_discarded_items_message_stream( + struct bt_message *message) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) message; + + BT_ASSERT(message); + return disc_items_msg->stream; +} + +static inline +void set_discarded_items_message_count(struct bt_message *message, + uint64_t count) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) message; + + BT_ASSERT(message); + BT_ASSERT_PRE_HOT(message, "Message", ": %!+n", message); + bt_property_uint_set(&disc_items_msg->count, count); +} + +static inline +enum bt_property_availability get_discarded_items_message_count( + const struct bt_message *message, uint64_t *count) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) message; + + BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); + BT_ASSERT(message); + *count = disc_items_msg->count.value; + return disc_items_msg->count.base.avail; +} + +static inline +const struct bt_clock_snapshot * +borrow_discarded_items_message_beginning_default_clock_snapshot_const( + const struct bt_message *message) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) message; + + BT_ASSERT(message); + BT_ASSERT_PRE(disc_items_msg->stream->class->default_clock_class, + "Message's stream's class has no default clock class: " + "%![msg-]+n, %![sc-]+S", + message, disc_items_msg->stream->class); + return disc_items_msg->default_begin_cs; +} + +static inline +const struct bt_clock_snapshot * +borrow_discarded_items_message_end_default_clock_snapshot_const( + const struct bt_message *message) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) message; + + BT_ASSERT(message); + BT_ASSERT_PRE(disc_items_msg->stream->class->default_clock_class, + "Message's stream's class has no default clock class: " + "%![msg-]+n, %![sc-]+S", + message, disc_items_msg->stream->class); + return disc_items_msg->default_end_cs; +} + +struct bt_message *bt_message_discarded_events_create( + struct bt_self_message_iterator *message_iterator, + const struct bt_stream *stream) +{ + return create_discarded_items_message(message_iterator, + BT_MESSAGE_TYPE_DISCARDED_EVENTS, (void *) stream, + false, 0, 0); +} + +struct bt_message *bt_message_discarded_events_create_with_default_clock_snapshots( + struct bt_self_message_iterator *message_iterator, + const struct bt_stream *stream, uint64_t beginning_raw_value, + uint64_t end_raw_value) +{ + return create_discarded_items_message(message_iterator, + BT_MESSAGE_TYPE_DISCARDED_EVENTS, (void *) stream, + true, beginning_raw_value, end_raw_value); +} + +struct bt_stream *bt_message_discarded_events_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + return borrow_discarded_items_message_stream(message); +} + +void bt_message_discarded_events_set_count(struct bt_message *message, + uint64_t count) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + set_discarded_items_message_count(message, count); +} + +const struct bt_clock_snapshot * +bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + return borrow_discarded_items_message_beginning_default_clock_snapshot_const( + msg); +} + +const struct bt_clock_snapshot * +bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + return borrow_discarded_items_message_end_default_clock_snapshot_const( + msg); +} + +const struct bt_stream * +bt_message_discarded_events_borrow_stream_const(const struct bt_message *message) +{ + return (void *) bt_message_discarded_events_borrow_stream( + (void *) message); +} + +enum bt_property_availability bt_message_discarded_events_get_count( + const struct bt_message *message, uint64_t *count) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + return get_discarded_items_message_count(message, count); +} + +struct bt_message *bt_message_discarded_packets_create( + struct bt_self_message_iterator *message_iterator, + const struct bt_stream *stream) +{ + return create_discarded_items_message(message_iterator, + BT_MESSAGE_TYPE_DISCARDED_PACKETS, (void *) stream, + false, 0, 0); +} + +struct bt_message *bt_message_discarded_packets_create_with_default_clock_snapshots( + struct bt_self_message_iterator *message_iterator, + const struct bt_stream *stream, uint64_t beginning_raw_value, + uint64_t end_raw_value) +{ + return create_discarded_items_message(message_iterator, + BT_MESSAGE_TYPE_DISCARDED_PACKETS, (void *) stream, + true, beginning_raw_value, end_raw_value); +} + +struct bt_stream *bt_message_discarded_packets_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + return borrow_discarded_items_message_stream(message); +} + +void bt_message_discarded_packets_set_count(struct bt_message *message, + uint64_t count) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + set_discarded_items_message_count(message, count); +} + +const struct bt_clock_snapshot * +bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + return borrow_discarded_items_message_beginning_default_clock_snapshot_const( + msg); +} + +const struct bt_clock_snapshot * +bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + return borrow_discarded_items_message_end_default_clock_snapshot_const( + msg); +} + +const struct bt_stream * +bt_message_discarded_packets_borrow_stream_const(const struct bt_message *message) +{ + return (void *) bt_message_discarded_packets_borrow_stream( + (void *) message); +} + +enum bt_property_availability bt_message_discarded_packets_get_count( + const struct bt_message *message, uint64_t *count) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + return get_discarded_items_message_count(message, count); +} + +static inline +const struct bt_clock_class * +borrow_discarded_items_message_stream_class_default_clock_class( + const struct bt_message *msg) +{ + struct bt_message_discarded_items *disc_items_msg = (void *) msg; + + BT_ASSERT(msg); + return disc_items_msg->stream->class->default_clock_class; +} + +const struct bt_clock_class * +bt_message_discarded_events_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_EVENTS); + return borrow_discarded_items_message_stream_class_default_clock_class( + msg); +} + +const struct bt_clock_class * +bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_DISCARDED_PACKETS); + return borrow_discarded_items_message_stream_class_default_clock_class( + msg); +} diff --git a/src/lib/graph/message/discarded-items.h b/src/lib/graph/message/discarded-items.h new file mode 100644 index 00000000..d9f3fb60 --- /dev/null +++ b/src/lib/graph/message/discarded-items.h @@ -0,0 +1,42 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/trace-ir/stream.h" +#include "lib/property.h" +#include + +#include "message.h" + +struct bt_message_discarded_items { + struct bt_message parent; + struct bt_stream *stream; + struct bt_clock_snapshot *default_begin_cs; + struct bt_clock_snapshot *default_end_cs; + struct bt_property_uint count; +}; + +#endif /* BABELTRACE_GRAPH_MESSAGE_DISCARDED_ITEMS_INTERNAL_H */ diff --git a/src/lib/graph/message/event.c b/src/lib/graph/message/event.c new file mode 100644 index 00000000..00b428c2 --- /dev/null +++ b/src/lib/graph/message/event.c @@ -0,0 +1,290 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-EVENT" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include "lib/object.h" +#include +#include "lib/trace-ir/event.h" +#include "lib/trace-ir/event-class.h" +#include "lib/trace-ir/stream-class.h" +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/graph/graph.h" +#include +#include +#include +#include +#include + +#include "event.h" + +BT_ASSERT_PRE_FUNC +static inline bool event_class_has_trace(struct bt_event_class *event_class) +{ + struct bt_stream_class *stream_class; + + stream_class = bt_event_class_borrow_stream_class_inline(event_class); + BT_ASSERT(stream_class); + return bt_stream_class_borrow_trace_class(stream_class) != NULL; +} + +BT_HIDDEN +struct bt_message *bt_message_event_new( + struct bt_graph *graph) +{ + struct bt_message_event *message = NULL; + + message = g_new0(struct bt_message_event, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one event message."); + goto error; + } + + bt_message_init(&message->parent, BT_MESSAGE_TYPE_EVENT, + (bt_object_release_func) bt_message_event_recycle, graph); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(message); + +end: + return (void *) message; +} + +static inline +struct bt_message *create_event_message( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_event_class *c_event_class, + const struct bt_packet *c_packet, bool with_cs, + uint64_t raw_value) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + struct bt_message_event *message = NULL; + struct bt_event_class *event_class = (void *) c_event_class; + struct bt_stream_class *stream_class; + struct bt_packet *packet = (void *) c_packet; + struct bt_event *event; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + BT_ASSERT_PRE(event_class_has_trace(event_class), + "Event class is not part of a trace: %!+E", event_class); + stream_class = bt_event_class_borrow_stream_class_inline(event_class); + BT_ASSERT(stream_class); + BT_ASSERT_PRE((with_cs && stream_class->default_clock_class) || + (!with_cs && !stream_class->default_clock_class), + "Creating an event message with a default clock snapshot, but without " + "a default clock class, or without a default clock snapshot, " + "but with a default clock class: ", + "%![ec-]+E, %![sc-]+S, with-cs=%d, " + "cs-val=%" PRIu64, + event_class, stream_class, with_cs, raw_value); + BT_LIB_LOGD("Creating event message object: %![ec-]+E", event_class); + event = bt_event_create(event_class, packet); + if (unlikely(!event)) { + BT_LIB_LOGE("Cannot create event from event class: " + "%![ec-]+E", event_class); + goto error; + } + + /* + * Create message from pool _after_ we have everything + * (in this case, a valid event object) so that we never have an + * error condition with a non-NULL message object. + * Otherwise: + * + * * We cannot recycle the message on error because + * bt_message_event_recycle() expects a complete + * message (and the event or clock class priority map + * object could be unset). + * + * * We cannot destroy the message because we would need + * to notify the graph (pool owner) so that it removes the + * message from its message array. + */ + message = (void *) bt_message_create_from_pool( + &msg_iter->graph->event_msg_pool, msg_iter->graph); + if (unlikely(!message)) { + /* bt_message_create_from_pool() logs errors */ + goto error; + } + + if (with_cs) { + BT_ASSERT(stream_class->default_clock_class); + message->default_cs = bt_clock_snapshot_create( + stream_class->default_clock_class); + if (!message->default_cs) { + goto error; + } + + bt_clock_snapshot_set_raw_value(message->default_cs, raw_value); + } + + BT_ASSERT(!message->event); + message->event = event; + bt_packet_set_is_frozen(packet, true); + bt_event_class_freeze(event_class); + BT_LIB_LOGD("Created event message object: " + "%![msg-]+n, %![event-]+e", message, event); + goto end; + +error: + BT_ASSERT(!message); + bt_event_destroy(event); + +end: + return (void *) message; +} + +struct bt_message *bt_message_event_create( + struct bt_self_message_iterator *msg_iter, + const struct bt_event_class *event_class, + const struct bt_packet *packet) +{ + return create_event_message(msg_iter, event_class, packet, false, 0); +} + +struct bt_message *bt_message_event_create_with_default_clock_snapshot( + struct bt_self_message_iterator *msg_iter, + const struct bt_event_class *event_class, + const struct bt_packet *packet, + uint64_t raw_value) +{ + return create_event_message(msg_iter, event_class, packet, + true, raw_value); +} + +BT_HIDDEN +void bt_message_event_destroy(struct bt_message *msg) +{ + struct bt_message_event *event_msg = (void *) msg; + + BT_LIB_LOGD("Destroying event message: %!+n", msg); + + if (event_msg->event) { + BT_LIB_LOGD("Recycling event: %!+e", event_msg->event); + bt_event_recycle(event_msg->event); + event_msg->event = NULL; + } + + if (event_msg->default_cs) { + bt_clock_snapshot_recycle(event_msg->default_cs); + event_msg->default_cs = NULL; + } + + g_free(msg); +} + +BT_HIDDEN +void bt_message_event_recycle(struct bt_message *msg) +{ + struct bt_message_event *event_msg = (void *) msg; + struct bt_graph *graph; + + BT_ASSERT(event_msg); + + if (unlikely(!msg->graph)) { + bt_message_event_destroy(msg); + return; + } + + BT_LIB_LOGD("Recycling event message: %![msg-]+n, %![event-]+e", + msg, event_msg->event); + bt_message_reset(msg); + BT_ASSERT(event_msg->event); + bt_event_recycle(event_msg->event); + event_msg->event = NULL; + + if (event_msg->default_cs) { + bt_clock_snapshot_recycle(event_msg->default_cs); + event_msg->default_cs = NULL; + } + + graph = msg->graph; + msg->graph = NULL; + bt_object_pool_recycle_object(&graph->event_msg_pool, msg); +} + +static inline +struct bt_event *borrow_event(struct bt_message *message) +{ + struct bt_message_event *event_message; + + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_EVENT); + event_message = container_of(message, + struct bt_message_event, parent); + return event_message->event; +} + +struct bt_event *bt_message_event_borrow_event( + struct bt_message *message) +{ + return borrow_event(message); +} + +const struct bt_event *bt_message_event_borrow_event_const( + const struct bt_message *message) +{ + return borrow_event((void *) message); +} + +const struct bt_clock_snapshot * +bt_message_event_borrow_default_clock_snapshot_const( + const struct bt_message *msg) +{ + struct bt_message_event *event_msg = (void *) msg; + struct bt_stream_class *stream_class; + + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_EVENT); + stream_class = bt_event_class_borrow_stream_class_inline( + event_msg->event->class); + BT_ASSERT(stream_class); + BT_ASSERT_PRE(stream_class->default_clock_class, + "Message's stream's class has no default clock class: " + "%![msg-]+n, %![sc-]+S", msg, stream_class); + return event_msg->default_cs; +} + +const bt_clock_class * +bt_message_event_borrow_stream_class_default_clock_class_const( + const bt_message *msg) +{ + struct bt_message_event *event_msg = (void *) msg; + struct bt_stream_class *stream_class; + + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_EVENT); + stream_class = bt_event_class_borrow_stream_class_inline( + event_msg->event->class); + BT_ASSERT(stream_class); + return stream_class->default_clock_class; +} diff --git a/src/lib/graph/message/event.h b/src/lib/graph/message/event.h new file mode 100644 index 00000000..b53b6e2f --- /dev/null +++ b/src/lib/graph/message/event.h @@ -0,0 +1,57 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compat/compiler.h" +#include +#include +#include "common/assert.h" + +#include "message.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_message_event { + struct bt_message parent; + struct bt_event *event; + struct bt_clock_snapshot *default_cs; +}; + +BT_HIDDEN +struct bt_message *bt_message_event_new(struct bt_graph *graph); + +BT_HIDDEN +void bt_message_event_recycle(struct bt_message *msg); + +BT_HIDDEN +void bt_message_event_destroy(struct bt_message *msg); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_GRAPH_MESSAGE_EVENT_INTERNAL_H */ diff --git a/src/lib/graph/message/iterator.h b/src/lib/graph/message/iterator.h new file mode 100644 index 00000000..bae41b19 --- /dev/null +++ b/src/lib/graph/message/iterator.h @@ -0,0 +1,184 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "lib/object.h" +#include +#include +#include +#include +#include "common/assert.h" +#include + +struct bt_port; +struct bt_graph; + +enum bt_message_iterator_type { + BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT, + BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT, +}; + +enum bt_self_component_port_input_message_iterator_state { + /* Iterator is not initialized */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_NON_INITIALIZED, + + /* Iterator is active, not at the end yet, and not finalized */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE, + + /* + * Iterator is ended, not finalized yet: the "next" method + * returns BT_MESSAGE_ITERATOR_STATUS_END. + */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED, + + /* Iterator is currently being finalized */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING, + + /* Iterator is finalized */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED, + + /* Iterator is seeking */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING, + + /* Iterator did seek, but returned `BT_MESSAGE_ITERATOR_STATUS_AGAIN` */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN, + + /* Iterator did seek, but returned error status */ + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR, +}; + +struct bt_message_iterator { + struct bt_object base; + enum bt_message_iterator_type type; + GPtrArray *msgs; +}; + +typedef enum bt_self_message_iterator_status +(*bt_self_component_port_input_message_iterator_next_method)( + void *, bt_message_array_const, uint64_t, uint64_t *); + +typedef enum bt_self_message_iterator_status +(*bt_self_component_port_input_message_iterator_seek_ns_from_origin_method)( + void *, int64_t); + +typedef enum bt_self_message_iterator_status +(*bt_self_component_port_input_message_iterator_seek_beginning_method)( + void *); + +typedef bt_bool +(*bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method)( + void *, int64_t); + +typedef bt_bool +(*bt_self_component_port_input_message_iterator_can_seek_beginning_method)( + void *); + +struct bt_self_component_port_input_message_iterator { + struct bt_message_iterator base; + struct bt_component *upstream_component; /* Weak */ + struct bt_port *upstream_port; /* Weak */ + struct bt_connection *connection; /* Weak */ + struct bt_graph *graph; /* Weak */ + + struct { + bt_self_component_port_input_message_iterator_next_method next; + bt_self_component_port_input_message_iterator_seek_ns_from_origin_method seek_ns_from_origin; + bt_self_component_port_input_message_iterator_seek_beginning_method seek_beginning; + bt_self_component_port_input_message_iterator_can_seek_ns_from_origin_method can_seek_ns_from_origin; + bt_self_component_port_input_message_iterator_can_seek_beginning_method can_seek_beginning; + } methods; + + enum bt_self_component_port_input_message_iterator_state state; + GQueue *auto_seek_msgs; + void *user_data; +}; + +struct bt_port_output_message_iterator { + struct bt_message_iterator base; + struct bt_graph *graph; /* Owned by this */ + struct bt_component_sink *colander; /* Owned by this */ + + /* + * Only used temporarily as a bridge between a colander sink and + * the user. + */ + uint64_t count; +}; + +BT_HIDDEN +void bt_self_component_port_input_message_iterator_try_finalize( + struct bt_self_component_port_input_message_iterator *iterator); + +BT_HIDDEN +void bt_self_component_port_input_message_iterator_set_connection( + struct bt_self_component_port_input_message_iterator *iterator, + struct bt_connection *connection); + +static inline +const char *bt_message_iterator_status_string( + enum bt_message_iterator_status status) +{ + switch (status) { + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + return "BT_MESSAGE_ITERATOR_STATUS_AGAIN"; + case BT_MESSAGE_ITERATOR_STATUS_END: + return "BT_MESSAGE_ITERATOR_STATUS_END"; + case BT_MESSAGE_ITERATOR_STATUS_OK: + return "BT_MESSAGE_ITERATOR_STATUS_OK"; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + return "BT_MESSAGE_ITERATOR_STATUS_ERROR"; + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + return "BT_MESSAGE_ITERATOR_STATUS_NOMEM"; + default: + return "(unknown)"; + } +}; + +static inline +const char *bt_self_component_port_input_message_iterator_state_string( + enum bt_self_component_port_input_message_iterator_state state) +{ + switch (state) { + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ACTIVE"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_ENDED"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZING"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_FINALIZED"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_SEEKING"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_AGAIN"; + case BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR: + return "BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_STATE_LAST_SEEKING_RETURNED_ERROR"; + default: + return "(unknown)"; + } +}; + +#endif /* BABELTRACE_GRAPH_MESSAGE_ITERATOR_INTERNAL_H */ diff --git a/src/lib/graph/message/message-iterator-inactivity.c b/src/lib/graph/message/message-iterator-inactivity.c new file mode 100644 index 00000000..cfec17ed --- /dev/null +++ b/src/lib/graph/message/message-iterator-inactivity.c @@ -0,0 +1,106 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-MESSAGE-ITERATOR-INACTIVITY" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/graph/message/message.h" +#include +#include + +#include "message-iterator-inactivity.h" + +static +void bt_message_message_iterator_inactivity_destroy(struct bt_object *obj) +{ + struct bt_message_message_iterator_inactivity *message = + (struct bt_message_message_iterator_inactivity *) obj; + + BT_LIB_LOGD("Destroying message iterator inactivity message: %!+n", + message); + + if (message->default_cs) { + bt_clock_snapshot_recycle(message->default_cs); + message->default_cs = NULL; + } + + g_free(message); +} + +struct bt_message *bt_message_message_iterator_inactivity_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_clock_class *default_clock_class, + uint64_t value_cycles) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + struct bt_message_message_iterator_inactivity *message; + struct bt_message *ret_msg = NULL; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(default_clock_class, "Default clock class"); + BT_LIB_LOGD("Creating message iterator inactivity message object: " + "%![iter-]+i, %![default-cc-]+K, value=%" PRIu64, msg_iter, + default_clock_class, value_cycles); + message = g_new0(struct bt_message_message_iterator_inactivity, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one message iterator " + "inactivity message."); + goto error; + } + bt_message_init(&message->parent, + BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY, + bt_message_message_iterator_inactivity_destroy, NULL); + ret_msg = &message->parent; + message->default_cs = bt_clock_snapshot_create( + (void *) default_clock_class); + if (!message->default_cs) { + goto error; + } + bt_clock_snapshot_set_raw_value(message->default_cs, value_cycles); + + BT_LIB_LOGD("Created message iterator inactivity message object: %!+n", + ret_msg); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(ret_msg); + +end: + return (void *) ret_msg; +} + +extern const struct bt_clock_snapshot * +bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + const bt_message *msg) +{ + struct bt_message_message_iterator_inactivity *inactivity = (void *) msg; + + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY); + return inactivity->default_cs; +} diff --git a/src/lib/graph/message/message-iterator-inactivity.h b/src/lib/graph/message/message-iterator-inactivity.h new file mode 100644 index 00000000..12806bfb --- /dev/null +++ b/src/lib/graph/message/message-iterator-inactivity.h @@ -0,0 +1,35 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/trace-ir/clock-snapshot.h" +#include + +struct bt_message_message_iterator_inactivity { + struct bt_message parent; + struct bt_clock_snapshot *default_cs; +}; + +#endif /* BABELTRACE_GRAPH_MESSAGE_MESSAGE_ITERATOR_INACTIVITY_INTERNAL_H */ diff --git a/src/lib/graph/message/message.c b/src/lib/graph/message/message.c new file mode 100644 index 00000000..0a65b3be --- /dev/null +++ b/src/lib/graph/message/message.c @@ -0,0 +1,71 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include "lib/graph/message/message.h" +#include "lib/graph/graph.h" + +BT_HIDDEN +void bt_message_init(struct bt_message *message, + enum bt_message_type type, + bt_object_release_func release, + struct bt_graph *graph) +{ + BT_ASSERT(type >= 0 && type <= BT_MESSAGE_TYPE_DISCARDED_PACKETS); + message->type = type; + bt_object_init_shared(&message->base, release); + message->graph = graph; + + if (graph) { + bt_graph_add_message(graph, message); + } +} + +enum bt_message_type bt_message_get_type( + const struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + return message->type; +} + +BT_HIDDEN +void bt_message_unlink_graph(struct bt_message *msg) +{ + BT_ASSERT(msg); + msg->graph = NULL; +} + +void bt_message_get_ref(const struct bt_message *message) +{ + bt_object_get_ref(message); +} + +void bt_message_put_ref(const struct bt_message *message) +{ + bt_object_put_ref(message); +} diff --git a/src/lib/graph/message/message.h b/src/lib/graph/message/message.h new file mode 100644 index 00000000..98437707 --- /dev/null +++ b/src/lib/graph/message/message.h @@ -0,0 +1,138 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "lib/object.h" +#include "common/assert.h" +#include +#include +#include +#include "lib/object-pool.h" +#include + +typedef struct bt_stream *(*get_stream_func)( + struct bt_message *message); + +struct bt_message { + struct bt_object base; + enum bt_message_type type; + bt_bool frozen; + + /* Owned by this; keeps the graph alive while the msg. is alive */ + struct bt_graph *graph; +}; + +#define BT_ASSERT_PRE_MSG_IS_TYPE(_msg, _type) \ + BT_ASSERT_PRE(((struct bt_message *) (_msg))->type == (_type), \ + "Message has the wrong type: expected-type=%s, " \ + "%![msg-]+n", bt_message_type_string(_type), \ + (_msg)) + +BT_HIDDEN +void bt_message_init(struct bt_message *message, + enum bt_message_type type, + bt_object_release_func release, + struct bt_graph *graph); + +static inline +void bt_message_reset(struct bt_message *message) +{ + BT_ASSERT(message); + +#ifdef BT_DEV_MODE + message->frozen = BT_FALSE; +#endif +} + +static inline +struct bt_message *bt_message_create_from_pool( + struct bt_object_pool *pool, struct bt_graph *graph) +{ + struct bt_message *msg = bt_object_pool_create_object(pool); + + if (unlikely(!msg)) { +#ifdef BT_LIB_LOGE + BT_LIB_LOGE("Cannot allocate one message from message pool: " + "%![pool-]+o, %![graph-]+g", pool, graph); +#endif + goto error; + } + + if (likely(!msg->graph)) { + msg->graph = graph; + } + + goto end; + +error: + BT_ASSERT(!msg); + +end: + return msg; +} + +static inline void _bt_message_freeze(struct bt_message *message) +{ + message->frozen = BT_TRUE; +} + +BT_HIDDEN +void bt_message_unlink_graph(struct bt_message *msg); + +#ifdef BT_DEV_MODE +# define bt_message_freeze _bt_message_freeze +#else +# define bt_message_freeze(_x) +#endif /* BT_DEV_MODE */ + +static inline +const char *bt_message_type_string(enum bt_message_type type) +{ + switch (type) { + case BT_MESSAGE_TYPE_EVENT: + return "BT_MESSAGE_TYPE_EVENT"; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + return "BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY"; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + return "BT_MESSAGE_TYPE_STREAM_BEGINNING"; + case BT_MESSAGE_TYPE_STREAM_END: + return "BT_MESSAGE_TYPE_STREAM_END"; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + return "BT_MESSAGE_TYPE_PACKET_BEGINNING"; + case BT_MESSAGE_TYPE_PACKET_END: + return "BT_MESSAGE_TYPE_PACKET_END"; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + return "BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING"; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + return "BT_MESSAGE_TYPE_STREAM_ACTIVITY_END"; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + return "BT_MESSAGE_TYPE_DISCARDED_EVENTS"; + default: + return "(unknown)"; + } +} + +#endif /* BABELTRACE_GRAPH_MESSAGE_MESSAGE_INTERNAL_H */ diff --git a/src/lib/graph/message/packet.c b/src/lib/graph/message/packet.c new file mode 100644 index 00000000..6bce7129 --- /dev/null +++ b/src/lib/graph/message/packet.c @@ -0,0 +1,366 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-PACKET" +#include "lib/lib-logging.h" + +#include "compat/compiler.h" +#include +#include "lib/trace-ir/packet.h" +#include +#include +#include "lib/trace-ir/stream.h" +#include "lib/trace-ir/stream-class.h" +#include "lib/graph/graph.h" +#include +#include +#include +#include +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "lib/object.h" +#include + +#include "packet.h" + +static inline +struct bt_message *new_packet_message(struct bt_graph *graph, + enum bt_message_type type, bt_object_release_func recycle_func) +{ + struct bt_message_packet *message; + + message = g_new0(struct bt_message_packet, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one packet message."); + goto error; + } + + bt_message_init(&message->parent, type, recycle_func, graph); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(message); + +end: + return (void *) message; +} + +BT_HIDDEN +struct bt_message *bt_message_packet_beginning_new(struct bt_graph *graph) +{ + return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_BEGINNING, + (bt_object_release_func) bt_message_packet_beginning_recycle); +} + +BT_HIDDEN +struct bt_message *bt_message_packet_end_new(struct bt_graph *graph) +{ + return new_packet_message(graph, BT_MESSAGE_TYPE_PACKET_END, + (bt_object_release_func) bt_message_packet_end_recycle); +} + +static inline +struct bt_message *create_packet_message( + struct bt_self_component_port_input_message_iterator *msg_iter, + struct bt_packet *packet, struct bt_object_pool *pool, + bool with_cs, uint64_t raw_value) +{ + struct bt_message_packet *message = NULL; + struct bt_stream *stream; + struct bt_stream_class *stream_class; + bool need_cs; + + BT_ASSERT(msg_iter); + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + stream = bt_packet_borrow_stream(packet); + BT_ASSERT(stream); + stream_class = bt_stream_borrow_class(stream); + BT_ASSERT(stream_class); + + if (pool == &msg_iter->graph->packet_begin_msg_pool) { + need_cs = stream_class->packets_have_beginning_default_clock_snapshot; + } else { + need_cs = stream_class->packets_have_end_default_clock_snapshot; + } + + /* + * `packet_has_default_clock_snapshot` implies that the stream + * class has a default clock class (precondition). + */ + BT_ASSERT_PRE(need_cs ? with_cs : true, + "Unexpected stream class configuration when creating " + "a packet beginning or end message: " + "a default clock snapshot is needed, but none was provided: " + "%![stream-]+s, %![sc-]+S, with-cs=%d, " + "cs-val=%" PRIu64, + stream, stream_class, with_cs, raw_value); + BT_ASSERT_PRE(!need_cs ? !with_cs : true, + "Unexpected stream class configuration when creating " + "a packet beginning or end message: " + "no default clock snapshot is needed, but one was provided: " + "%![stream-]+s, %![sc-]+S, with-cs=%d, " + "cs-val=%" PRIu64, + stream, stream_class, with_cs, raw_value); + BT_LIB_LOGD("Creating packet message object: " + "%![packet-]+a, %![stream-]+s, %![sc-]+S", + packet, stream, stream_class); + message = (void *) bt_message_create_from_pool(pool, msg_iter->graph); + if (!message) { + /* bt_message_create_from_pool() logs errors */ + goto end; + } + + if (with_cs) { + BT_ASSERT(stream_class->default_clock_class); + message->default_cs = bt_clock_snapshot_create( + stream_class->default_clock_class); + if (!message->default_cs) { + bt_object_put_no_null_check(message); + message = NULL; + goto end; + } + + bt_clock_snapshot_set_raw_value(message->default_cs, raw_value); + } + + BT_ASSERT(!message->packet); + message->packet = packet; + bt_object_get_no_null_check_no_parent_check( + &message->packet->base); + bt_packet_set_is_frozen(packet, true); + BT_LIB_LOGD("Created packet message object: " + "%![msg-]+n, %![packet-]+a, %![stream-]+s, %![sc-]+S", + message, packet, stream, stream_class); + goto end; + +end: + return (void *) message; +} + +struct bt_message *bt_message_packet_beginning_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_packet *packet) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + return create_packet_message(msg_iter, (void *) packet, + &msg_iter->graph->packet_begin_msg_pool, false, 0); +} + +struct bt_message *bt_message_packet_beginning_create_with_default_clock_snapshot( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_packet *packet, uint64_t raw_value) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + return create_packet_message(msg_iter, (void *) packet, + &msg_iter->graph->packet_begin_msg_pool, true, raw_value); +} + +struct bt_message *bt_message_packet_end_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_packet *packet) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + return create_packet_message(msg_iter, (void *) packet, + &msg_iter->graph->packet_end_msg_pool, false, 0); +} + +struct bt_message *bt_message_packet_end_create_with_default_clock_snapshot( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_packet *packet, uint64_t raw_value) +{ + struct bt_self_component_port_input_message_iterator *msg_iter = + (void *) self_msg_iter; + + BT_ASSERT_PRE_NON_NULL(msg_iter, "Message iterator"); + return create_packet_message(msg_iter, (void *) packet, + &msg_iter->graph->packet_end_msg_pool, true, raw_value); +} + +BT_HIDDEN +void bt_message_packet_destroy(struct bt_message *msg) +{ + struct bt_message_packet *packet_msg = (void *) msg; + + BT_LIB_LOGD("Destroying packet message: %!+n", msg); + BT_LIB_LOGD("Putting packet: %!+a", packet_msg->packet); + BT_OBJECT_PUT_REF_AND_RESET(packet_msg->packet); + + if (packet_msg->default_cs) { + bt_clock_snapshot_recycle(packet_msg->default_cs); + packet_msg->default_cs = NULL; + } + + g_free(msg); +} + +static inline +void recycle_packet_message(struct bt_message *msg, struct bt_object_pool *pool) +{ + struct bt_message_packet *packet_msg = (void *) msg; + + BT_LIB_LOGD("Recycling packet message: %!+n", msg); + bt_message_reset(msg); + bt_object_put_no_null_check(&packet_msg->packet->base); + + if (packet_msg->default_cs) { + bt_clock_snapshot_recycle(packet_msg->default_cs); + packet_msg->default_cs = NULL; + } + + packet_msg->packet = NULL; + msg->graph = NULL; + bt_object_pool_recycle_object(pool, msg); +} + +BT_HIDDEN +void bt_message_packet_beginning_recycle(struct bt_message *msg) +{ + BT_ASSERT(msg); + + if (unlikely(!msg->graph)) { + bt_message_packet_destroy(msg); + return; + } + + recycle_packet_message(msg, &msg->graph->packet_begin_msg_pool); +} + +BT_HIDDEN +void bt_message_packet_end_recycle(struct bt_message *msg) +{ + BT_ASSERT(msg); + + if (unlikely(!msg->graph)) { + bt_message_packet_destroy(msg); + return; + } + + recycle_packet_message(msg, &msg->graph->packet_end_msg_pool); +} + +struct bt_packet *bt_message_packet_beginning_borrow_packet( + struct bt_message *message) +{ + struct bt_message_packet *packet_msg = (void *) message; + + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, + BT_MESSAGE_TYPE_PACKET_BEGINNING); + return packet_msg->packet; +} + +const struct bt_packet *bt_message_packet_beginning_borrow_packet_const( + const struct bt_message *message) +{ + return bt_message_packet_beginning_borrow_packet( + (void *) message); +} + +struct bt_packet *bt_message_packet_end_borrow_packet( + struct bt_message *message) +{ + struct bt_message_packet *packet_msg = (void *) message; + + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, + BT_MESSAGE_TYPE_PACKET_END); + return packet_msg->packet; +} + +const struct bt_packet *bt_message_packet_end_borrow_packet_const( + const struct bt_message *message) +{ + return bt_message_packet_end_borrow_packet( + (void *) message); +} + +static inline +const struct bt_clock_snapshot * +borrow_packet_message_default_clock_snapshot_const( + const struct bt_message *message) +{ + struct bt_message_packet *packet_msg = (void *) message; + + BT_ASSERT(message); + BT_ASSERT_PRE(packet_msg->packet->stream->class->default_clock_class, + "Message's stream's class has no default clock class: " + "%![msg-]+n, %![sc-]+S", + message, packet_msg->packet->stream->class); + return packet_msg->default_cs; +} + +const struct bt_clock_snapshot * +bt_message_packet_beginning_borrow_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_BEGINNING); + return borrow_packet_message_default_clock_snapshot_const(msg); +} + +const struct bt_clock_snapshot * +bt_message_packet_end_borrow_default_clock_snapshot_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_END); + return borrow_packet_message_default_clock_snapshot_const(msg); +} + +static inline +const struct bt_clock_class * +borrow_packet_message_stream_class_default_clock_class( + const struct bt_message *msg) +{ + struct bt_message_packet *packet_msg = (void *) msg; + + BT_ASSERT(msg); + return packet_msg->packet->stream->class->default_clock_class; +} + +const struct bt_clock_class * +bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_BEGINNING); + return borrow_packet_message_stream_class_default_clock_class(msg); +} + +const struct bt_clock_class * +bt_message_packet_end_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_PACKET_END); + return borrow_packet_message_stream_class_default_clock_class(msg); +} diff --git a/src/lib/graph/message/packet.h b/src/lib/graph/message/packet.h new file mode 100644 index 00000000..7738788c --- /dev/null +++ b/src/lib/graph/message/packet.h @@ -0,0 +1,55 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compat/compiler.h" +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "common/assert.h" + +#include "message.h" + +struct bt_message_packet { + struct bt_message parent; + struct bt_packet *packet; + struct bt_clock_snapshot *default_cs; +}; + +BT_HIDDEN +void bt_message_packet_destroy(struct bt_message *msg); + +BT_HIDDEN +struct bt_message *bt_message_packet_beginning_new( + struct bt_graph *graph); +BT_HIDDEN +void bt_message_packet_beginning_recycle(struct bt_message *msg); + +BT_HIDDEN +struct bt_message *bt_message_packet_end_new(struct bt_graph *graph); + +BT_HIDDEN +void bt_message_packet_end_recycle(struct bt_message *msg); + +#endif /* BABELTRACE_GRAPH_MESSAGE_PACKET_INTERNAL_H */ diff --git a/src/lib/graph/message/stream-activity.c b/src/lib/graph/message/stream-activity.c new file mode 100644 index 00000000..dc840bea --- /dev/null +++ b/src/lib/graph/message/stream-activity.c @@ -0,0 +1,304 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-STREAM-ACTIVITY" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "lib/object.h" +#include "compat/compiler.h" +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/trace-ir/stream-class.h" +#include "lib/trace-ir/stream.h" +#include "lib/graph/message/message.h" +#include +#include +#include +#include + +#include "stream-activity.h" + +static +void destroy_stream_activity_message(struct bt_object *obj) +{ + struct bt_message_stream_activity *message = (void *) obj; + + BT_LIB_LOGD("Destroying stream activity message: %!+n", message); + BT_LIB_LOGD("Putting stream: %!+s", message->stream); + BT_OBJECT_PUT_REF_AND_RESET(message->stream); + + if (message->default_cs) { + bt_clock_snapshot_recycle(message->default_cs); + message->default_cs = NULL; + } + + g_free(message); +} + +static inline +struct bt_message *create_stream_activity_message( + struct bt_self_message_iterator *self_msg_iter, + struct bt_stream *stream, enum bt_message_type type) +{ + struct bt_message_stream_activity *message; + struct bt_stream_class *stream_class; + + BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + stream_class = bt_stream_borrow_class(stream); + BT_ASSERT(stream_class); + BT_LIB_LOGD("Creating stream activity message object: " + "type=%s, %![stream-]+s, %![sc-]+S", + bt_message_type_string(type), stream, stream_class); + message = g_new0(struct bt_message_stream_activity, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one stream activity message."); + goto error; + } + + bt_message_init(&message->parent, type, + destroy_stream_activity_message, NULL); + message->stream = stream; + bt_object_get_no_null_check(message->stream); + + if (stream_class->default_clock_class) { + message->default_cs = bt_clock_snapshot_create( + stream_class->default_clock_class); + if (!message->default_cs) { + goto error; + } + } + + message->default_cs_state = + BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN; + BT_LIB_LOGD("Created stream activity message object: " + "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, + stream, stream_class); + + return (void *) &message->parent; + +error: + return NULL; +} + +struct bt_message *bt_message_stream_activity_beginning_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_stream *stream) +{ + return create_stream_activity_message(self_msg_iter, (void *) stream, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); +} + +struct bt_message *bt_message_stream_activity_end_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_stream *stream) +{ + return create_stream_activity_message(self_msg_iter, (void *) stream, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); +} + +static inline +struct bt_stream *borrow_stream_activity_message_stream( + struct bt_message *message) +{ + struct bt_message_stream_activity *stream_act_msg = (void *) message; + + BT_ASSERT(message); + return stream_act_msg->stream; +} + +struct bt_stream *bt_message_stream_activity_beginning_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); + return borrow_stream_activity_message_stream(message); +} + +struct bt_stream *bt_message_stream_activity_end_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); + return borrow_stream_activity_message_stream(message); +} + +const struct bt_stream *bt_message_stream_activity_beginning_borrow_stream_const( + const struct bt_message *message) +{ + return bt_message_stream_activity_beginning_borrow_stream( + (void *) message); +} + +const struct bt_stream *bt_message_stream_activity_end_borrow_stream_const( + const struct bt_message *message) +{ + return bt_message_stream_activity_end_borrow_stream((void *) message); +} + +static inline +void set_stream_activity_message_default_clock_snapshot( + struct bt_message *msg, uint64_t value_cycles) +{ + struct bt_message_stream_activity *stream_act_msg = (void *) msg; + struct bt_stream_class *sc; + + BT_ASSERT(msg); + BT_ASSERT_PRE_HOT(msg, "Message", ": %!+n", msg); + sc = stream_act_msg->stream->class; + BT_ASSERT(sc); + BT_ASSERT_PRE(sc->default_clock_class, + "Message's stream's class has no default clock class: " + "%![msg-]+n, %![sc-]+S", msg, sc); + bt_clock_snapshot_set_raw_value(stream_act_msg->default_cs, + value_cycles); + stream_act_msg->default_cs_state = + BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN; + BT_LIB_LOGV("Set stream activity message's default clock snapshot: " + "%![msg-]+n, value=%" PRIu64, msg, value_cycles); +} + +void bt_message_stream_activity_beginning_set_default_clock_snapshot( + struct bt_message *msg, uint64_t raw_value) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); + set_stream_activity_message_default_clock_snapshot(msg, raw_value); +} + +void bt_message_stream_activity_end_set_default_clock_snapshot( + struct bt_message *msg, uint64_t raw_value) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); + set_stream_activity_message_default_clock_snapshot(msg, raw_value); +} + +static inline +enum bt_message_stream_activity_clock_snapshot_state +borrow_stream_activity_message_default_clock_snapshot_const( + const bt_message *msg, const bt_clock_snapshot **snapshot) +{ + const struct bt_message_stream_activity *stream_act_msg = + (const void *) msg; + + BT_ASSERT_PRE_NON_NULL(snapshot, "Clock snapshot (output)"); + *snapshot = stream_act_msg->default_cs; + return stream_act_msg->default_cs_state; +} + +enum bt_message_stream_activity_clock_snapshot_state +bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + const bt_message *msg, const bt_clock_snapshot **snapshot) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); + return borrow_stream_activity_message_default_clock_snapshot_const(msg, + snapshot); +} + +enum bt_message_stream_activity_clock_snapshot_state +bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + const bt_message *msg, const bt_clock_snapshot **snapshot) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); + return borrow_stream_activity_message_default_clock_snapshot_const(msg, + snapshot); +} + +static inline +void set_stream_activity_message_default_clock_snapshot_state( + struct bt_message *msg, + enum bt_message_stream_activity_clock_snapshot_state state) +{ + struct bt_message_stream_activity *stream_act_msg = (void *) msg; + + BT_ASSERT(msg); + BT_ASSERT_PRE_HOT(msg, "Message", ": %!+n", msg); + BT_ASSERT_PRE(state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN, + "Invalid clock snapshot state: %![msg-]+n, state=%s", + msg, + bt_message_stream_activity_clock_snapshot_state_string(state)); + stream_act_msg->default_cs_state = state; + BT_LIB_LOGV("Set stream activity message's default clock snapshot state: " + "%![msg-]+n, state=%s", msg, + bt_message_stream_activity_clock_snapshot_state_string(state)); +} + +void bt_message_stream_activity_beginning_set_default_clock_snapshot_state( + struct bt_message *msg, + enum bt_message_stream_activity_clock_snapshot_state state) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); + set_stream_activity_message_default_clock_snapshot_state(msg, state); +} + +void bt_message_stream_activity_end_set_default_clock_snapshot_state( + struct bt_message *msg, + enum bt_message_stream_activity_clock_snapshot_state state) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); + set_stream_activity_message_default_clock_snapshot_state(msg, state); +} + +static inline +const struct bt_clock_class * +borrow_stream_activity_message_stream_class_default_clock_class( + const struct bt_message *msg) +{ + struct bt_message_stream_activity *stream_act_msg = (void *) msg; + + BT_ASSERT(msg); + return stream_act_msg->stream->class->default_clock_class; +} + +const struct bt_clock_class * +bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, + BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING); + return borrow_stream_activity_message_stream_class_default_clock_class( + msg); +} + +const struct bt_clock_class * +bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( + const struct bt_message *msg) +{ + BT_ASSERT_PRE_NON_NULL(msg, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(msg, BT_MESSAGE_TYPE_STREAM_ACTIVITY_END); + return borrow_stream_activity_message_stream_class_default_clock_class( + msg); +} diff --git a/src/lib/graph/message/stream-activity.h b/src/lib/graph/message/stream-activity.h new file mode 100644 index 00000000..9199d43b --- /dev/null +++ b/src/lib/graph/message/stream-activity.h @@ -0,0 +1,55 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/trace-ir/clock-snapshot.h" +#include "lib/trace-ir/stream.h" +#include +#include + +struct bt_message_stream_activity { + struct bt_message parent; + struct bt_stream *stream; + struct bt_clock_snapshot *default_cs; + enum bt_message_stream_activity_clock_snapshot_state default_cs_state; +}; + +static inline +const char *bt_message_stream_activity_clock_snapshot_state_string( + enum bt_message_stream_activity_clock_snapshot_state state) +{ + switch (state) { + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN: + return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN"; + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN: + return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN"; + case BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE: + return "BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE"; + default: + return "(unknown)"; + } +} + +#endif /* BABELTRACE_GRAPH_MESSAGE_STREAM_ACTIVITY_INTERNAL_H */ diff --git a/src/lib/graph/message/stream.c b/src/lib/graph/message/stream.c new file mode 100644 index 00000000..f691d72b --- /dev/null +++ b/src/lib/graph/message/stream.c @@ -0,0 +1,142 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "MSG-STREAM" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include "lib/trace-ir/stream.h" +#include +#include "lib/trace-ir/stream-class.h" +#include +#include +#include +#include +#include "common/assert.h" +#include + +#include "stream.h" + +static +void destroy_stream_message(struct bt_object *obj) +{ + struct bt_message_stream *message = (void *) obj; + + BT_LIB_LOGD("Destroying stream message: %!+n", message); + BT_LIB_LOGD("Putting stream: %!+s", message->stream); + BT_OBJECT_PUT_REF_AND_RESET(message->stream); + g_free(message); +} + +static inline +struct bt_message *create_stream_message( + struct bt_self_message_iterator *self_msg_iter, + struct bt_stream *stream, enum bt_message_type type) +{ + struct bt_message_stream *message; + struct bt_stream_class *stream_class; + + BT_ASSERT_PRE_NON_NULL(self_msg_iter, "Message iterator"); + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + stream_class = bt_stream_borrow_class(stream); + BT_ASSERT(stream_class); + BT_LIB_LOGD("Creating stream message object: " + "type=%s, %![stream-]+s, %![sc-]+S", + bt_message_type_string(type), stream, stream_class); + message = g_new0(struct bt_message_stream, 1); + if (!message) { + BT_LOGE_STR("Failed to allocate one stream message."); + goto error; + } + + bt_message_init(&message->parent, type, + destroy_stream_message, NULL); + message->stream = stream; + bt_object_get_no_null_check(message->stream); + BT_LIB_LOGD("Created stream message object: " + "%![msg-]+n, %![stream-]+s, %![sc-]+S", message, + stream, stream_class); + + return (void *) &message->parent; + +error: + return NULL; +} + +struct bt_message *bt_message_stream_beginning_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_stream *stream) +{ + return create_stream_message(self_msg_iter, (void *) stream, + BT_MESSAGE_TYPE_STREAM_BEGINNING); +} + +struct bt_message *bt_message_stream_end_create( + struct bt_self_message_iterator *self_msg_iter, + const struct bt_stream *stream) +{ + return create_stream_message(self_msg_iter, (void *) stream, + BT_MESSAGE_TYPE_STREAM_END); +} + +static inline +struct bt_stream *borrow_stream_message_stream(struct bt_message *message) +{ + struct bt_message_stream *stream_msg; + + BT_ASSERT(message); + stream_msg = (void *) message; + return stream_msg->stream; +} + +struct bt_stream *bt_message_stream_beginning_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_STREAM_BEGINNING); + return borrow_stream_message_stream(message); +} + +struct bt_stream *bt_message_stream_end_borrow_stream( + struct bt_message *message) +{ + BT_ASSERT_PRE_NON_NULL(message, "Message"); + BT_ASSERT_PRE_MSG_IS_TYPE(message, BT_MESSAGE_TYPE_STREAM_END); + return borrow_stream_message_stream(message); +} + +const struct bt_stream *bt_message_stream_beginning_borrow_stream_const( + const struct bt_message *message) +{ + return bt_message_stream_beginning_borrow_stream( + (void *) message); +} + +const struct bt_stream *bt_message_stream_end_borrow_stream_const( + const struct bt_message *message) +{ + return bt_message_stream_end_borrow_stream( + (void *) message); +} diff --git a/src/lib/graph/message/stream.h b/src/lib/graph/message/stream.h new file mode 100644 index 00000000..7cca4bc2 --- /dev/null +++ b/src/lib/graph/message/stream.h @@ -0,0 +1,39 @@ +#ifndef BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H +#define BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compat/compiler.h" +#include "lib/trace-ir/stream.h" +#include "lib/trace-ir/clock-snapshot.h" +#include "common/assert.h" + +#include "message.h" + +struct bt_message_stream { + struct bt_message parent; + struct bt_stream *stream; +}; + +#endif /* BABELTRACE_GRAPH_MESSAGE_STREAM_INTERNAL_H */ diff --git a/src/lib/graph/port.c b/src/lib/graph/port.c new file mode 100644 index 00000000..ff4e4430 --- /dev/null +++ b/src/lib/graph/port.c @@ -0,0 +1,181 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PORT" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" + +#include "component.h" +#include "connection.h" +#include "port.h" + +static +void destroy_port(struct bt_object *obj) +{ + struct bt_port *port = (void *) obj; + + BT_LIB_LOGD("Destroying port: %!+p", port); + + if (port->name) { + g_string_free(port->name, TRUE); + port->name = NULL; + } + + g_free(port); +} + +BT_HIDDEN +struct bt_port *bt_port_create(struct bt_component *parent_component, + enum bt_port_type type, const char *name, void *user_data) +{ + struct bt_port *port = NULL; + + BT_ASSERT(name); + BT_ASSERT(parent_component); + BT_ASSERT(type == BT_PORT_TYPE_INPUT || type == BT_PORT_TYPE_OUTPUT); + BT_ASSERT(strlen(name) > 0); + port = g_new0(struct bt_port, 1); + if (!port) { + BT_LOGE_STR("Failed to allocate one port."); + goto end; + } + + BT_LIB_LOGD("Creating port for component: %![comp-]+c, port-type=%s, " + "port-name=\"%s\"", parent_component, bt_port_type_string(type), + name); + bt_object_init_shared_with_parent(&port->base, destroy_port); + port->name = g_string_new(name); + if (!port->name) { + BT_LOGE_STR("Failed to allocate one GString."); + BT_OBJECT_PUT_REF_AND_RESET(port); + goto end; + } + + port->type = type; + port->user_data = user_data; + bt_object_set_parent(&port->base, &parent_component->base); + BT_LIB_LOGD("Created port for component: " + "%![comp-]+c, %![port-]+p", parent_component, port); + +end: + return port; +} + +const char *bt_port_get_name(const struct bt_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return port->name->str; +} + +enum bt_port_type bt_port_get_type(const struct bt_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return port->type; +} + +const struct bt_connection *bt_port_borrow_connection_const( + const struct bt_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return port->connection; +} + +const struct bt_component *bt_port_borrow_component_const( + const struct bt_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return bt_port_borrow_component_inline(port); +} + +struct bt_self_component *bt_self_component_port_borrow_component( + struct bt_self_component_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return (void *) bt_object_borrow_parent((void *) port); +} + +BT_HIDDEN +void bt_port_set_connection(struct bt_port *port, + struct bt_connection *connection) +{ + /* + * Don't take a reference on connection as its existence is + * guaranteed by the existence of the graph in which the + * connection exists. + */ + port->connection = connection; + BT_LIB_LOGV("Set port's connection: %![port-]+p, %![conn-]+x", port, + connection); +} + +bt_bool bt_port_is_connected(const struct bt_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return port->connection ? BT_TRUE : BT_FALSE; +} + +void *bt_self_component_port_get_data(const struct bt_self_component_port *port) +{ + BT_ASSERT_PRE_NON_NULL(port, "Port"); + return ((struct bt_port *) port)->user_data; +} + +void bt_port_get_ref(const struct bt_port *port) +{ + bt_object_get_ref(port); +} + +void bt_port_put_ref(const struct bt_port *port) +{ + bt_object_put_ref(port); +} + +void bt_port_input_get_ref(const struct bt_port_input *port_input) +{ + bt_object_get_ref(port_input); +} + +void bt_port_input_put_ref(const struct bt_port_input *port_input) +{ + bt_object_put_ref(port_input); +} + +void bt_port_output_get_ref(const struct bt_port_output *port_output) +{ + bt_object_get_ref(port_output); +} + +void bt_port_output_put_ref(const struct bt_port_output *port_output) +{ + bt_object_put_ref(port_output); +} diff --git a/src/lib/graph/port.h b/src/lib/graph/port.h new file mode 100644 index 00000000..c1be31ad --- /dev/null +++ b/src/lib/graph/port.h @@ -0,0 +1,67 @@ +#ifndef BABELTRACE_GRAPH_PORT_INTERNAL_H +#define BABELTRACE_GRAPH_PORT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +struct bt_port { + struct bt_object base; + enum bt_port_type type; + GString *name; + struct bt_connection *connection; + void *user_data; +}; + +struct bt_component; + +BT_HIDDEN +struct bt_port *bt_port_create(struct bt_component *parent_component, + enum bt_port_type type, const char *name, void *user_data); + +BT_HIDDEN +void bt_port_set_connection(struct bt_port *port, + struct bt_connection *connection); + +static inline +struct bt_component *bt_port_borrow_component_inline(const struct bt_port *port) +{ + BT_ASSERT(port); + return (void *) bt_object_borrow_parent(&port->base); +} + +static inline +const char *bt_port_type_string(enum bt_port_type port_type) +{ + switch (port_type) { + case BT_PORT_TYPE_INPUT: + return "BT_PORT_TYPE_INPUT"; + case BT_PORT_TYPE_OUTPUT: + return "BT_PORT_TYPE_OUTPUT"; + default: + return "(unknown)"; + } +} + +#endif /* BABELTRACE_GRAPH_PORT_INTERNAL_H */ diff --git a/src/lib/graph/query-executor.c b/src/lib/graph/query-executor.c new file mode 100644 index 00000000..b26455d4 --- /dev/null +++ b/src/lib/graph/query-executor.c @@ -0,0 +1,169 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "QUERY-EXECUTOR" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" + +#include "component-class.h" +#include "query-executor.h" + +static +void bt_query_executor_destroy(struct bt_object *obj) +{ + struct bt_query_executor *query_exec = + container_of(obj, struct bt_query_executor, base); + + BT_LOGD("Destroying query executor: addr=%p", query_exec); + g_free(query_exec); +} + +struct bt_query_executor *bt_query_executor_create(void) +{ + struct bt_query_executor *query_exec; + + BT_LOGD_STR("Creating query executor."); + query_exec = g_new0(struct bt_query_executor, 1); + if (!query_exec) { + BT_LOGE_STR("Failed to allocate one query executor."); + goto end; + } + + bt_object_init_shared(&query_exec->base, + bt_query_executor_destroy); + BT_LOGD("Created query executor: addr=%p", query_exec); + +end: + return (void *) query_exec; +} + +enum bt_query_executor_status bt_query_executor_query( + struct bt_query_executor *query_exec, + const struct bt_component_class *comp_cls, + const char *object, const struct bt_value *params, + const struct bt_value **user_result) +{ + typedef enum bt_query_status (*method_t)(void *, const void *, + const void *, const void *, const void *); + + enum bt_query_status status; + enum bt_query_executor_status exec_status; + method_t method = NULL; + + BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); + BT_ASSERT_PRE_NON_NULL(comp_cls, "Component class"); + BT_ASSERT_PRE_NON_NULL(object, "Object"); + BT_ASSERT_PRE_NON_NULL(user_result, "Result (output)"); + BT_ASSERT_PRE(!query_exec->canceled, "Query executor is canceled."); + + if (!params) { + params = bt_value_null; + } + + switch (comp_cls->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + { + struct bt_component_class_source *src_cc = (void *) comp_cls; + + method = (method_t) src_cc->methods.query; + break; + } + case BT_COMPONENT_CLASS_TYPE_FILTER: + { + struct bt_component_class_filter *flt_cc = (void *) comp_cls; + + method = (method_t) flt_cc->methods.query; + break; + } + case BT_COMPONENT_CLASS_TYPE_SINK: + { + struct bt_component_class_sink *sink_cc = (void *) comp_cls; + + method = (method_t) sink_cc->methods.query; + break; + } + default: + abort(); + } + + if (!method) { + /* Not an error: nothing to query */ + BT_LIB_LOGD("Component class has no registered query method: " + "%!+C", comp_cls); + exec_status = BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED; + goto end; + } + + BT_LIB_LOGD("Calling user's query method: " + "query-exec-addr=%p, %![cc-]+C, object=\"%s\", %![params-]+v", + query_exec, comp_cls, object, params); + *user_result = NULL; + status = method((void *) comp_cls, query_exec, object, params, + user_result); + BT_LIB_LOGD("User method returned: status=%s, %![res-]+v", + bt_query_status_string(status), *user_result); + BT_ASSERT_PRE(status != BT_QUERY_STATUS_OK || *user_result, + "User method returned `BT_QUERY_STATUS_OK` without a result."); + exec_status = (int) status; + if (query_exec->canceled) { + BT_OBJECT_PUT_REF_AND_RESET(*user_result); + exec_status = BT_QUERY_EXECUTOR_STATUS_CANCELED; + goto end; + } + +end: + return exec_status; +} + +enum bt_query_executor_status bt_query_executor_cancel( + struct bt_query_executor *query_exec) +{ + BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); + query_exec->canceled = BT_TRUE; + BT_LOGV("Canceled query executor: addr=%p", query_exec); + return BT_QUERY_EXECUTOR_STATUS_OK; +} + +bt_bool bt_query_executor_is_canceled(const struct bt_query_executor *query_exec) +{ + BT_ASSERT_PRE_NON_NULL(query_exec, "Query executor"); + return query_exec->canceled; +} + +void bt_query_executor_get_ref(const struct bt_query_executor *query_executor) +{ + bt_object_get_ref(query_executor); +} + +void bt_query_executor_put_ref(const struct bt_query_executor *query_executor) +{ + bt_object_put_ref(query_executor); +} diff --git a/src/lib/graph/query-executor.h b/src/lib/graph/query-executor.h new file mode 100644 index 00000000..57951b72 --- /dev/null +++ b/src/lib/graph/query-executor.h @@ -0,0 +1,81 @@ +#ifndef BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H +#define BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/object.h" +#include +#include + +struct bt_query_executor { + struct bt_object base; + bool canceled; +}; + +static inline const char *bt_query_status_string(enum bt_query_status status) +{ + switch (status) { + case BT_QUERY_STATUS_OK: + return "BT_QUERY_STATUS_OK"; + case BT_QUERY_STATUS_AGAIN: + return "BT_QUERY_STATUS_AGAIN"; + case BT_QUERY_STATUS_ERROR: + return "BT_QUERY_STATUS_ERROR"; + case BT_QUERY_STATUS_INVALID_OBJECT: + return "BT_QUERY_STATUS_INVALID_OBJECT"; + case BT_QUERY_STATUS_INVALID_PARAMS: + return "BT_QUERY_STATUS_INVALID_PARAMS"; + case BT_QUERY_STATUS_NOMEM: + return "BT_QUERY_STATUS_NOMEM"; + default: + return "(unknown)"; + } +}; + +static inline const char *bt_query_executor_status_string( + enum bt_query_executor_status status) +{ + switch (status) { + case BT_QUERY_EXECUTOR_STATUS_OK: + return "BT_QUERY_EXECUTOR_STATUS_OK"; + case BT_QUERY_EXECUTOR_STATUS_AGAIN: + return "BT_QUERY_EXECUTOR_STATUS_AGAIN"; + case BT_QUERY_EXECUTOR_STATUS_CANCELED: + return "BT_QUERY_EXECUTOR_STATUS_CANCELED"; + case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: + return "BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED"; + case BT_QUERY_EXECUTOR_STATUS_ERROR: + return "BT_QUERY_EXECUTOR_STATUS_ERROR"; + case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: + return "BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT"; + case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: + return "BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS"; + case BT_QUERY_EXECUTOR_STATUS_NOMEM: + return "BT_QUERY_EXECUTOR_STATUS_NOMEM"; + default: + return "(unknown)"; + } +}; + +#endif /* BABELTRACE_GRAPH_QUERY_EXECUTOR_INTERNAL_H */ diff --git a/src/lib/lib-logging.c b/src/lib/lib-logging.c new file mode 100644 index 00000000..d6d75e05 --- /dev/null +++ b/src/lib/lib-logging.c @@ -0,0 +1,1420 @@ +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "LIB-LOGGING" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/common.h" +#include "lib/value.h" +#include "lib/value.h" +#include "lib/object-pool.h" +#include +#include +#include + +#include "graph/component-class.h" +#include "graph/component-class-sink-colander.h" +#include "graph/component-filter.h" +#include "graph/component.h" +#include "graph/component-sink.h" +#include "graph/component-source.h" +#include "graph/connection.h" +#include "graph/graph.h" +#include "graph/message/discarded-items.h" +#include "graph/message/event.h" +#include "graph/message/iterator.h" +#include "graph/message/message.h" +#include "graph/message/message-iterator-inactivity.h" +#include "graph/message/packet.h" +#include "graph/message/stream-activity.h" +#include "graph/message/stream.h" +#include "graph/port.h" +#include "lib-logging.h" +#include "plugin/plugin.h" +#include "plugin/plugin-so.h" +#include "trace-ir/clock-class.h" +#include "trace-ir/clock-snapshot.h" +#include "trace-ir/event-class.h" +#include "trace-ir/event.h" +#include "trace-ir/field-class.h" +#include "trace-ir/field.h" +#include "trace-ir/field-path.h" +#include "trace-ir/packet.h" +#include "trace-ir/stream-class.h" +#include "trace-ir/stream.h" +#include "trace-ir/trace-class.h" +#include "trace-ir/trace.h" +#include "trace-ir/utils.h" +#include "assert-pre.h" + +#define LIB_LOGGING_BUF_SIZE (4096 * 4) + +static __thread char lib_logging_buf[LIB_LOGGING_BUF_SIZE]; + +#define BUF_APPEND(_fmt, ...) \ + do { \ + int _count; \ + size_t _size = LIB_LOGGING_BUF_SIZE - \ + (size_t) (*buf_ch - lib_logging_buf); \ + _count = snprintf(*buf_ch, _size, (_fmt), __VA_ARGS__); \ + BT_ASSERT(_count >= 0); \ + *buf_ch += MIN(_count, _size); \ + if (*buf_ch >= lib_logging_buf + LIB_LOGGING_BUF_SIZE - 1) { \ + return; \ + } \ + } while (0) + +#define BUF_APPEND_UUID(_uuid) \ + do { \ + BUF_APPEND(", %suuid=", prefix); \ + format_uuid(buf_ch, (_uuid)); \ + } while (0) + +#define PRFIELD(_expr) prefix, (_expr) + +#define PRFIELD_GSTRING(_expr) PRFIELD((_expr) ? (_expr)->str : NULL) + +#define TMP_PREFIX_LEN 64 +#define SET_TMP_PREFIX(_prefix2) \ + do { \ + snprintf(tmp_prefix, TMP_PREFIX_LEN - 1, "%s%s", \ + prefix, (_prefix2)); \ + tmp_prefix[TMP_PREFIX_LEN - 1] = '\0'; \ + } while (0) + +static inline void format_component(char **buf_ch, bool extended, + const char *prefix, const struct bt_component *component); + +static inline void format_port(char **buf_ch, bool extended, + const char *prefix, const struct bt_port *port); + +static inline void format_connection(char **buf_ch, bool extended, + const char *prefix, const struct bt_connection *connection); + +static inline void format_clock_snapshot(char **buf_ch, bool extended, + const char *prefix, const struct bt_clock_snapshot *clock_snapshot); + +static inline void format_field_path(char **buf_ch, bool extended, + const char *prefix, const struct bt_field_path *field_path); + +static inline void format_object(char **buf_ch, bool extended, + const char *prefix, const struct bt_object *obj) +{ + BUF_APPEND(", %sref-count=%llu", prefix, obj->ref_count); +} + +static inline void format_uuid(char **buf_ch, bt_uuid uuid) +{ + BUF_APPEND("\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + (unsigned int) uuid[0], + (unsigned int) uuid[1], + (unsigned int) uuid[2], + (unsigned int) uuid[3], + (unsigned int) uuid[4], + (unsigned int) uuid[5], + (unsigned int) uuid[6], + (unsigned int) uuid[7], + (unsigned int) uuid[8], + (unsigned int) uuid[9], + (unsigned int) uuid[10], + (unsigned int) uuid[11], + (unsigned int) uuid[12], + (unsigned int) uuid[13], + (unsigned int) uuid[14], + (unsigned int) uuid[15]); +} + +static inline void format_object_pool(char **buf_ch, bool extended, + const char *prefix, const struct bt_object_pool *pool) +{ + BUF_APPEND(", %ssize=%zu", PRFIELD(pool->size)); + + if (pool->objects) { + BUF_APPEND(", %scap=%u", PRFIELD(pool->objects->len)); + } +} + +static inline void format_integer_field_class(char **buf_ch, + bool extended, const char *prefix, + const struct bt_field_class *field_class) +{ + const struct bt_field_class_integer *int_fc = + (const void *) field_class; + + BUF_APPEND(", %srange-size=%" PRIu64 ", %sbase=%s", + PRFIELD(int_fc->range), + PRFIELD(bt_common_field_class_integer_preferred_display_base_string(int_fc->base))); +} + +static inline void format_array_field_class(char **buf_ch, + bool extended, const char *prefix, + const struct bt_field_class *field_class) +{ + const struct bt_field_class_array *array_fc = + (const void *) field_class; + + BUF_APPEND(", %selement-fc-addr=%p, %selement-fc-type=%s", + PRFIELD(array_fc->element_fc), + PRFIELD(bt_common_field_class_type_string(array_fc->element_fc->type))); +} + +static inline void format_field_class(char **buf_ch, bool extended, + const char *prefix, const struct bt_field_class *field_class) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %stype=%s", + PRFIELD(bt_common_field_class_type_string(field_class->type))); + + if (extended) { + BUF_APPEND(", %sis-frozen=%d", PRFIELD(field_class->frozen)); + BUF_APPEND(", %sis-part-of-trace-class=%d", + PRFIELD(field_class->part_of_trace_class)); + } else { + return; + } + + switch (field_class->type) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + { + format_integer_field_class(buf_ch, extended, prefix, field_class); + break; + } + case BT_FIELD_CLASS_TYPE_REAL: + { + const struct bt_field_class_real *real_fc = (void *) field_class; + + BUF_APPEND(", %sis-single-precision=%d", + PRFIELD(real_fc->is_single_precision)); + break; + } + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + { + const struct bt_field_class_enumeration *enum_fc = + (const void *) field_class; + + format_integer_field_class(buf_ch, extended, prefix, field_class); + BUF_APPEND(", %smapping-count=%u", + PRFIELD(enum_fc->mappings->len)); + break; + } + case BT_FIELD_CLASS_TYPE_STRUCTURE: + { + const struct bt_field_class_structure *struct_fc = + (const void *) field_class; + + if (struct_fc->common.named_fcs) { + BUF_APPEND(", %smember-count=%u", + PRFIELD(struct_fc->common.named_fcs->len)); + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + { + const struct bt_field_class_static_array *array_fc = + (const void *) field_class; + + format_array_field_class(buf_ch, extended, prefix, field_class); + BUF_APPEND(", %slength=%" PRIu64, PRFIELD(array_fc->length)); + break; + } + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + const struct bt_field_class_dynamic_array *array_fc = + (const void *) field_class; + + format_array_field_class(buf_ch, extended, prefix, field_class); + + if (array_fc->length_fc) { + SET_TMP_PREFIX("length-fc-"); + format_field_class(buf_ch, extended, tmp_prefix, + array_fc->length_fc); + } + + if (array_fc->length_field_path) { + SET_TMP_PREFIX("length-field-path-"); + format_field_path(buf_ch, extended, tmp_prefix, + array_fc->length_field_path); + } + + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + { + const struct bt_field_class_variant *var_fc = + (const void *) field_class; + + if (var_fc->common.named_fcs) { + BUF_APPEND(", %soption-count=%u", + PRFIELD(var_fc->common.named_fcs->len)); + } + + if (var_fc->selector_fc) { + SET_TMP_PREFIX("selector-fc-"); + format_field_class(buf_ch, extended, tmp_prefix, + var_fc->selector_fc); + } + + if (var_fc->selector_field_path) { + SET_TMP_PREFIX("selector-field-path-"); + format_field_path(buf_ch, extended, tmp_prefix, + var_fc->selector_field_path); + } + + break; + } + default: + break; + } +} + +static inline void format_field_integer_extended(char **buf_ch, + const char *prefix, const struct bt_field *field) +{ + const struct bt_field_integer *integer = (void *) field; + const struct bt_field_class_integer *field_class = + (void *) field->class; + const char *fmt = NULL; + + BT_ASSERT(field_class); + + if (field_class->base == BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL) { + fmt = ", %svalue=%" PRIo64; + } else if (field_class->base == BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL) { + fmt = ", %svalue=%" PRIx64; + } + + if (field_class->common.type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || + field_class->common.type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { + if (!fmt) { + fmt = ", %svalue=%" PRId64; + } + + BUF_APPEND(fmt, PRFIELD(integer->value.i)); + } else { + if (!fmt) { + fmt = ", %svalue=%" PRIu64; + } + + BUF_APPEND(fmt, PRFIELD(integer->value.u)); + } +} + +static inline void format_field(char **buf_ch, bool extended, + const char *prefix, const struct bt_field *field) +{ + BUF_APPEND(", %sis-set=%d", PRFIELD(field->is_set)); + + if (extended) { + BUF_APPEND(", %sis-frozen=%d", PRFIELD(field->frozen)); + } + + BUF_APPEND(", %sclass-addr=%p", PRFIELD(field->class)); + + if (!field->class) { + return; + } + + BUF_APPEND(", %sclass-type=%s", + PRFIELD(bt_common_field_class_type_string(field->class->type))); + + if (!extended || !field->is_set) { + return; + } + + switch (field->class->type) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + { + format_field_integer_extended(buf_ch, prefix, field); + break; + } + case BT_FIELD_CLASS_TYPE_REAL: + { + const struct bt_field_real *real_field = (const void *) field; + + BUF_APPEND(", %svalue=%f", PRFIELD(real_field->value)); + break; + } + case BT_FIELD_CLASS_TYPE_STRING: + { + const struct bt_field_string *str = (const void *) field; + + if (str->buf) { + BT_ASSERT(str->buf->data); + BUF_APPEND(", %spartial-value=\"%.32s\"", + PRFIELD(str->buf->data)); + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + const struct bt_field_array *array_field = (const void *) field; + + BUF_APPEND(", %slength=%" PRIu64, PRFIELD(array_field->length)); + + if (array_field->fields) { + BUF_APPEND(", %sallocated-length=%u", + PRFIELD(array_field->fields->len)); + } + + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + { + const struct bt_field_variant *var_field = (const void *) field; + + BUF_APPEND(", %sselected-field-index=%" PRIu64, + PRFIELD(var_field->selected_index)); + break; + } + default: + break; + } +} + +static inline void format_field_path(char **buf_ch, bool extended, + const char *prefix, const struct bt_field_path *field_path) +{ + uint64_t i; + + if (field_path->items) { + BT_ASSERT(field_path->items); + BUF_APPEND(", %sitem-count=%u", + PRFIELD(field_path->items->len)); + } + + if (!extended || !field_path->items) { + return; + } + + BUF_APPEND(", %spath=[%s", + PRFIELD(bt_common_scope_string(field_path->root))); + + for (i = 0; i < bt_field_path_get_item_count(field_path); i++) { + const struct bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_const(field_path, i); + + switch (bt_field_path_item_get_type(fp_item)) { + case BT_FIELD_PATH_ITEM_TYPE_INDEX: + BUF_APPEND(", %" PRIu64, + bt_field_path_item_index_get_index(fp_item)); + break; + case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: + BUF_APPEND("%s", ", "); + break; + default: + abort(); + } + } + + BUF_APPEND("%s", "]"); +} + +static inline void format_trace_class(char **buf_ch, bool extended, + const char *prefix, const struct bt_trace_class *trace_class) +{ + if (trace_class->name.value) { + BUF_APPEND(", %sname=\"%s\"", + PRFIELD(trace_class->name.value)); + } + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d", PRFIELD(trace_class->frozen)); + + if (trace_class->uuid.value) { + BUF_APPEND_UUID(trace_class->uuid.value); + } + + if (trace_class->stream_classes) { + BUF_APPEND(", %sstream-class-count=%u", + PRFIELD(trace_class->stream_classes->len)); + } + + BUF_APPEND(", %sassigns-auto-sc-id=%d", + PRFIELD(trace_class->assigns_automatic_stream_class_id)); +} + +static inline void format_trace(char **buf_ch, bool extended, + const char *prefix, const struct bt_trace *trace) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + if (trace->name.value) { + BUF_APPEND(", %sname=\"%s\"", PRFIELD(trace->name.value)); + } + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d", PRFIELD(trace->frozen)); + + if (trace->streams) { + BUF_APPEND(", %sstream-count=%u", + PRFIELD(trace->streams->len)); + } + + if (!trace->class) { + return; + } + + BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace->class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, trace->class); +} + +static inline void format_stream_class(char **buf_ch, bool extended, + const char *prefix, + const struct bt_stream_class *stream_class) +{ + const struct bt_trace_class *trace_class; + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %sid=%" PRIu64, PRFIELD(stream_class->id)); + + if (stream_class->name.value) { + BUF_APPEND(", %sname=\"%s\"", + PRFIELD(stream_class->name.value)); + } + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d", PRFIELD(stream_class->frozen)); + + if (stream_class->event_classes) { + BUF_APPEND(", %sevent-class-count=%u", + PRFIELD(stream_class->event_classes->len)); + } + + BUF_APPEND(", %spacket-context-fc-addr=%p, " + "%sevent-common-context-fc-addr=%p", + PRFIELD(stream_class->packet_context_fc), + PRFIELD(stream_class->event_common_context_fc)); + trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); + if (!trace_class) { + return; + } + + BUF_APPEND(", %sassigns-auto-ec-id=%d, %sassigns-auto-stream-id=%d, " + "%spackets-have-default-beginning-cs=%d, " + "%spackets-have-default-end-cs=%d, " + "%ssupports-discarded-events=%d, " + "%sdiscarded-events-have-default-cs=%d, " + "%ssupports-discarded-packets=%d, " + "%sdiscarded-packets-have-default-cs=%d", + PRFIELD(stream_class->assigns_automatic_event_class_id), + PRFIELD(stream_class->assigns_automatic_stream_id), + PRFIELD(stream_class->packets_have_beginning_default_clock_snapshot), + PRFIELD(stream_class->packets_have_end_default_clock_snapshot), + PRFIELD(stream_class->supports_discarded_events), + PRFIELD(stream_class->discarded_events_have_default_clock_snapshots), + PRFIELD(stream_class->supports_discarded_packets), + PRFIELD(stream_class->discarded_packets_have_default_clock_snapshots)); + BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, trace_class); + SET_TMP_PREFIX("pcf-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &stream_class->packet_context_field_pool); +} + +static inline void format_event_class(char **buf_ch, bool extended, + const char *prefix, const struct bt_event_class *event_class) +{ + const struct bt_stream_class *stream_class; + const struct bt_trace_class *trace_class; + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %sid=%" PRIu64, PRFIELD(event_class->id)); + + if (event_class->name.value) { + BUF_APPEND(", %sname=\"%s\"", + PRFIELD(event_class->name.value)); + } + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d", PRFIELD(event_class->frozen)); + + if (event_class->log_level.base.avail) { + BUF_APPEND(", %slog-level=%s", + PRFIELD(bt_common_event_class_log_level_string( + (int) event_class->log_level.value))); + } + + if (event_class->emf_uri.value) { + BUF_APPEND(", %semf-uri=\"%s\"", + PRFIELD(event_class->emf_uri.value)); + } + + BUF_APPEND(", %sspecific-context-fc-addr=%p, %spayload-fc-addr=%p", + PRFIELD(event_class->specific_context_fc), + PRFIELD(event_class->payload_fc)); + + stream_class = bt_event_class_borrow_stream_class_const(event_class); + if (!stream_class) { + return; + } + + BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); + SET_TMP_PREFIX("stream-class-"); + format_stream_class(buf_ch, false, tmp_prefix, stream_class); + trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); + if (!trace_class) { + return; + } + + BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, trace_class); + SET_TMP_PREFIX("event-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &event_class->event_pool); +} + +static inline void format_stream(char **buf_ch, bool extended, + const char *prefix, const struct bt_stream *stream) +{ + const struct bt_stream_class *stream_class; + const struct bt_trace_class *trace_class = NULL; + const struct bt_trace *trace = NULL; + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %sid=%" PRIu64, PRFIELD(stream->id)); + + if (stream->name.value) { + BUF_APPEND(", %sname=\"%s\"", PRFIELD(stream->name.value)); + } + + if (!extended) { + return; + } + + stream_class = bt_stream_borrow_class_const(stream); + if (stream_class) { + BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); + SET_TMP_PREFIX("stream-class-"); + format_stream_class(buf_ch, false, tmp_prefix, stream_class); + trace_class = bt_stream_class_borrow_trace_class_inline(stream_class); + } + + if (trace_class) { + BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, trace_class); + } + + trace = bt_stream_borrow_trace_inline(stream); + if (trace) { + BUF_APPEND(", %strace-addr=%p", PRFIELD(trace)); + SET_TMP_PREFIX("trace-"); + format_trace(buf_ch, false, tmp_prefix, trace); + } + + SET_TMP_PREFIX("packet-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, &stream->packet_pool); +} + +static inline void format_packet(char **buf_ch, bool extended, + const char *prefix, const struct bt_packet *packet) +{ + const struct bt_stream *stream; + const struct bt_trace_class *trace_class; + char tmp_prefix[TMP_PREFIX_LEN]; + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d, %scontext-field-addr=%p", + PRFIELD(packet->frozen), + PRFIELD(packet->context_field ? packet->context_field->field : NULL)); + stream = bt_packet_borrow_stream_const(packet); + if (!stream) { + return; + } + + BUF_APPEND(", %sstream-addr=%p", PRFIELD(stream)); + SET_TMP_PREFIX("stream-"); + format_stream(buf_ch, false, tmp_prefix, stream); + trace_class = (const struct bt_trace_class *) bt_object_borrow_parent(&stream->base); + if (!trace_class) { + return; + } + + BUF_APPEND(", %strace-class-addr=%p", PRFIELD(trace_class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, trace_class); +} + +static inline void format_event(char **buf_ch, bool extended, + const char *prefix, const struct bt_event *event) +{ + const struct bt_packet *packet; + const struct bt_stream *stream; + const struct bt_trace_class *trace_class; + const struct bt_stream_class *stream_class; + char tmp_prefix[TMP_PREFIX_LEN]; + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d, " + "%scommon-context-field-addr=%p, " + "%sspecific-context-field-addr=%p, " + "%spayload-field-addr=%p, ", + PRFIELD(event->frozen), + PRFIELD(event->common_context_field), + PRFIELD(event->specific_context_field), + PRFIELD(event->payload_field)); + BUF_APPEND(", %sevent-class-addr=%p", PRFIELD(event->class)); + + if (!event->class) { + return; + } + + SET_TMP_PREFIX("event-class-"); + format_event_class(buf_ch, false, tmp_prefix, event->class); + stream_class = bt_event_class_borrow_stream_class(event->class); + if (stream_class) { + BUF_APPEND(", %sstream-class-addr=%p", PRFIELD(stream_class)); + SET_TMP_PREFIX("stream-class-"); + format_stream_class(buf_ch, false, tmp_prefix, + stream_class); + + trace_class = bt_stream_class_borrow_trace_class_inline( + stream_class); + if (trace_class) { + BUF_APPEND(", %strace-class-addr=%p", + PRFIELD(trace_class)); + SET_TMP_PREFIX("trace-class-"); + format_trace_class(buf_ch, false, tmp_prefix, + trace_class); + } + } + + packet = bt_event_borrow_packet_const(event); + if (!packet) { + return; + } + + BUF_APPEND(", %spacket-addr=%p", PRFIELD(packet)); + SET_TMP_PREFIX("packet-"); + format_packet(buf_ch, false, tmp_prefix, packet); + stream = bt_packet_borrow_stream_const(packet); + if (!stream) { + return; + } + + BUF_APPEND(", %sstream-addr=%p", PRFIELD(stream)); + SET_TMP_PREFIX("stream-"); + format_stream(buf_ch, false, tmp_prefix, stream); +} + +static inline void format_clock_class(char **buf_ch, bool extended, + const char *prefix, const struct bt_clock_class *clock_class) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + if (clock_class->name.value) { + BUF_APPEND(", %sname=\"%s\"", PRFIELD(clock_class->name.value)); + } + + BUF_APPEND(", %sfreq=%" PRIu64, PRFIELD(clock_class->frequency)); + + if (!extended) { + return; + } + + if (clock_class->description.value) { + BUF_APPEND(", %spartial-descr=\"%.32s\"", + PRFIELD(clock_class->description.value)); + } + + if (clock_class->uuid.value) { + BUF_APPEND_UUID(clock_class->uuid.value); + } + + BUF_APPEND(", %sis-frozen=%d, %sprecision=%" PRIu64 ", " + "%soffset-s=%" PRId64 ", " + "%soffset-cycles=%" PRIu64 ", %sorigin-is-unix-epoch=%d, " + "%sbase-offset-ns=%" PRId64, + PRFIELD(clock_class->frozen), PRFIELD(clock_class->precision), + PRFIELD(clock_class->offset_seconds), + PRFIELD(clock_class->offset_cycles), + PRFIELD(clock_class->origin_is_unix_epoch), + PRFIELD(clock_class->base_offset.value_ns)); + + SET_TMP_PREFIX("cs-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &clock_class->cs_pool); +} + +static inline void format_clock_snapshot(char **buf_ch, bool extended, + const char *prefix, const struct bt_clock_snapshot *clock_snapshot) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + BUF_APPEND(", %svalue=%" PRIu64 ", %sns-from-origin=%" PRId64, + PRFIELD(clock_snapshot->value_cycles), + PRFIELD(clock_snapshot->ns_from_origin)); + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-set=%d", PRFIELD(clock_snapshot->is_set)); + + if (clock_snapshot->clock_class) { + BUF_APPEND(", %sclock-class-addr=%p", + PRFIELD(clock_snapshot->clock_class)); + SET_TMP_PREFIX("clock-class-"); + format_clock_class(buf_ch, false, tmp_prefix, + clock_snapshot->clock_class); + } +} + +static inline void format_value(char **buf_ch, bool extended, + const char *prefix, const struct bt_value *value) +{ + BUF_APPEND(", %stype=%s", + PRFIELD(bt_common_value_type_string(bt_value_get_type(value)))); + + if (!extended) { + return; + } + + switch (bt_value_get_type(value)) { + case BT_VALUE_TYPE_BOOL: + { + bt_bool val = bt_value_bool_get(value); + + BUF_APPEND(", %svalue=%d", PRFIELD(val)); + break; + } + case BT_VALUE_TYPE_UNSIGNED_INTEGER: + { + BUF_APPEND(", %svalue=%" PRIu64, + PRFIELD(bt_value_unsigned_integer_get(value))); + break; + } + case BT_VALUE_TYPE_SIGNED_INTEGER: + { + BUF_APPEND(", %svalue=%" PRId64, + PRFIELD(bt_value_signed_integer_get(value))); + break; + } + case BT_VALUE_TYPE_REAL: + { + double val = bt_value_real_get(value); + + BUF_APPEND(", %svalue=%f", PRFIELD(val)); + break; + } + case BT_VALUE_TYPE_STRING: + { + const char *val = bt_value_string_get(value); + + BUF_APPEND(", %spartial-value=\"%.32s\"", PRFIELD(val)); + break; + } + case BT_VALUE_TYPE_ARRAY: + { + int64_t count = bt_value_array_get_size(value); + + BT_ASSERT(count >= 0); + BUF_APPEND(", %selement-count=%" PRId64, PRFIELD(count)); + break; + } + case BT_VALUE_TYPE_MAP: + { + int64_t count = bt_value_map_get_size(value); + + BT_ASSERT(count >= 0); + BUF_APPEND(", %selement-count=%" PRId64, PRFIELD(count)); + break; + } + default: + break; + } +} + +static inline void format_message(char **buf_ch, bool extended, + const char *prefix, const struct bt_message *msg) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %stype=%s", + PRFIELD(bt_message_type_string(msg->type))); + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d, %sgraph-addr=%p", + PRFIELD(msg->frozen), PRFIELD(msg->graph)); + + switch (msg->type) { + case BT_MESSAGE_TYPE_EVENT: + { + const struct bt_message_event *msg_event = + (const void *) msg; + + if (msg_event->event) { + SET_TMP_PREFIX("event-"); + format_event(buf_ch, true, tmp_prefix, + msg_event->event); + } + + if (msg_event->default_cs) { + SET_TMP_PREFIX("default-cs-"); + format_clock_snapshot(buf_ch, true, tmp_prefix, + msg_event->default_cs); + } + + break; + } + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + case BT_MESSAGE_TYPE_STREAM_END: + { + const struct bt_message_stream *msg_stream = (const void *) msg; + + if (msg_stream->stream) { + SET_TMP_PREFIX("stream-"); + format_stream(buf_ch, true, tmp_prefix, + msg_stream->stream); + } + + break; + } + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + { + const struct bt_message_stream_activity *msg_stream_activity = + (const void *) msg; + + if (msg_stream_activity->stream) { + SET_TMP_PREFIX("stream-"); + format_stream(buf_ch, true, tmp_prefix, + msg_stream_activity->stream); + } + + BUF_APPEND(", %sdefault-cs-state=%s", + PRFIELD(bt_message_stream_activity_clock_snapshot_state_string( + msg_stream_activity->default_cs_state))); + + if (msg_stream_activity->default_cs) { + SET_TMP_PREFIX("default-cs-"); + format_clock_snapshot(buf_ch, true, tmp_prefix, + msg_stream_activity->default_cs); + } + + break; + } + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + case BT_MESSAGE_TYPE_PACKET_END: + { + const struct bt_message_packet *msg_packet = (const void *) msg; + + if (msg_packet->packet) { + SET_TMP_PREFIX("packet-"); + format_packet(buf_ch, true, tmp_prefix, + msg_packet->packet); + } + + if (msg_packet->default_cs) { + SET_TMP_PREFIX("default-cs-"); + format_clock_snapshot(buf_ch, true, tmp_prefix, + msg_packet->default_cs); + } + + break; + } + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + { + const struct bt_message_discarded_items *msg_disc_items = + (const void *) msg; + + if (msg_disc_items->stream) { + SET_TMP_PREFIX("stream-"); + format_stream(buf_ch, true, tmp_prefix, + msg_disc_items->stream); + } + + if (msg_disc_items->default_begin_cs) { + SET_TMP_PREFIX("default-begin-cs-"); + format_clock_snapshot(buf_ch, true, tmp_prefix, + msg_disc_items->default_begin_cs); + } + + if (msg_disc_items->default_end_cs) { + SET_TMP_PREFIX("default-end-cs-"); + format_clock_snapshot(buf_ch, true, tmp_prefix, + msg_disc_items->default_end_cs); + } + + if (msg_disc_items->count.base.avail) { + BUF_APPEND(", %scount=%" PRIu64, + PRFIELD(msg_disc_items->count.value)); + } + + break; + } + default: + break; + } +} + +static inline void format_plugin_so_shared_lib_handle(char **buf_ch, + const char *prefix, + const struct bt_plugin_so_shared_lib_handle *handle) +{ + BUF_APPEND(", %saddr=%p", PRFIELD(handle)); + + if (handle->path) { + BUF_APPEND(", %spath=\"%s\"", PRFIELD_GSTRING(handle->path)); + } +} + +static inline void format_component_class(char **buf_ch, bool extended, + const char *prefix, + const struct bt_component_class *comp_class) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %stype=%s, %sname=\"%s\"", + PRFIELD(bt_component_class_type_string(comp_class->type)), + PRFIELD_GSTRING(comp_class->name)); + + if (comp_class->description) { + BUF_APPEND(", %spartial-descr=\"%.32s\"", + PRFIELD_GSTRING(comp_class->description)); + } + + if (!extended) { + return; + } + + BUF_APPEND(", %sis-frozen=%d", PRFIELD(comp_class->frozen)); + + if (comp_class->so_handle) { + SET_TMP_PREFIX("so-handle-"); + format_plugin_so_shared_lib_handle(buf_ch, tmp_prefix, + comp_class->so_handle); + } +} + +static inline void format_component(char **buf_ch, bool extended, + const char *prefix, const struct bt_component *component) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %sname=\"%s\"", + PRFIELD_GSTRING(component->name)); + + if (component->class) { + SET_TMP_PREFIX("class-"); + format_component_class(buf_ch, extended, tmp_prefix, + component->class); + } + + if (!extended) { + return; + } + + if (component->input_ports) { + BUF_APPEND(", %sinput-port-count=%u", + PRFIELD(component->input_ports->len)); + } + + if (component->output_ports) { + BUF_APPEND(", %soutput-port-count=%u", + PRFIELD(component->output_ports->len)); + } +} + +static inline void format_port(char **buf_ch, bool extended, + const char *prefix, const struct bt_port *port) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %stype=%s, %sname=\"%s\"", + PRFIELD(bt_port_type_string(port->type)), + PRFIELD_GSTRING(port->name)); + + if (!extended) { + return; + } + + if (port->connection) { + SET_TMP_PREFIX("conn-"); + format_connection(buf_ch, false, tmp_prefix, port->connection); + } +} + +static inline void format_connection(char **buf_ch, bool extended, + const char *prefix, const struct bt_connection *connection) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + if (!extended) { + return; + } + + if (connection->upstream_port) { + SET_TMP_PREFIX("upstream-port-"); + format_port(buf_ch, false, tmp_prefix, + connection->upstream_port); + } + + if (connection->downstream_port) { + SET_TMP_PREFIX("downstream-port-"); + format_port(buf_ch, false, tmp_prefix, + connection->downstream_port); + } +} + +static inline void format_graph(char **buf_ch, bool extended, + const char *prefix, const struct bt_graph *graph) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %sis-canceled=%d, %scan-consume=%d, " + "%sconfig-state=%s", + PRFIELD(graph->canceled), + PRFIELD(graph->can_consume), + PRFIELD(bt_graph_configuration_state_string(graph->config_state))); + + if (!extended) { + return; + } + + if (graph->components) { + BUF_APPEND(", %scomp-count=%u", + PRFIELD(graph->components->len)); + } + + if (graph->connections) { + BUF_APPEND(", %sconn-count=%u", + PRFIELD(graph->connections->len)); + } + + SET_TMP_PREFIX("en-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &graph->event_msg_pool); + SET_TMP_PREFIX("pbn-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &graph->packet_begin_msg_pool); + SET_TMP_PREFIX("pen-pool-"); + format_object_pool(buf_ch, extended, tmp_prefix, + &graph->packet_end_msg_pool); +} + +static inline void format_message_iterator(char **buf_ch, + bool extended, const char *prefix, + const struct bt_message_iterator *iterator) +{ + const char *type; + char tmp_prefix[TMP_PREFIX_LEN]; + + if (iterator->type == BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT) { + type = "BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT"; + } else if (iterator->type == BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT) { + type = "BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT"; + } else { + type = "(unknown)"; + } + + BUF_APPEND(", %stype=%s", PRFIELD(type)); + + switch (iterator->type) { + case BT_MESSAGE_ITERATOR_TYPE_SELF_COMPONENT_PORT_INPUT: + { + const struct bt_self_component_port_input_message_iterator * + port_in_iter = (const void *) iterator; + + if (port_in_iter->upstream_component) { + SET_TMP_PREFIX("upstream-comp-"); + format_component(buf_ch, false, tmp_prefix, + port_in_iter->upstream_component); + } + + if (port_in_iter->upstream_port) { + SET_TMP_PREFIX("upstream-port-"); + format_port(buf_ch, false, tmp_prefix, + port_in_iter->upstream_port); + } + + if (port_in_iter->connection) { + SET_TMP_PREFIX("upstream-conn-"); + format_connection(buf_ch, false, tmp_prefix, + port_in_iter->connection); + } + break; + } + case BT_MESSAGE_ITERATOR_TYPE_PORT_OUTPUT: + { + const struct bt_port_output_message_iterator *port_out_iter = + (const void *) iterator; + + if (port_out_iter->graph) { + SET_TMP_PREFIX("graph-"); + format_graph(buf_ch, false, tmp_prefix, + port_out_iter->graph); + } + + if (port_out_iter->colander) { + SET_TMP_PREFIX("colander-comp-"); + format_component(buf_ch, false, tmp_prefix, + (void *) port_out_iter->colander); + } + + break; + } + default: + break; + } +} + +static inline void format_plugin(char **buf_ch, bool extended, + const char *prefix, const struct bt_plugin *plugin) +{ + char tmp_prefix[TMP_PREFIX_LEN]; + + BUF_APPEND(", %stype=%s", PRFIELD(bt_plugin_type_string(plugin->type))); + + if (plugin->info.path_set) { + BUF_APPEND(", %spath=\"%s\"", + PRFIELD_GSTRING(plugin->info.path)); + } + + if (plugin->info.name_set) { + BUF_APPEND(", %sname=\"%s\"", + PRFIELD_GSTRING(plugin->info.name)); + } + + if (!extended) { + return; + } + + if (plugin->info.author_set) { + BUF_APPEND(", %sauthor=\"%s\"", + PRFIELD_GSTRING(plugin->info.author)); + } + + if (plugin->info.license_set) { + BUF_APPEND(", %slicense=\"%s\"", + PRFIELD_GSTRING(plugin->info.license)); + } + + if (plugin->info.version_set) { + BUF_APPEND(", %sversion=%u.%u.%u%s", + PRFIELD(plugin->info.version.major), + plugin->info.version.minor, + plugin->info.version.patch, + plugin->info.version.extra ? + plugin->info.version.extra->str : ""); + } + + BUF_APPEND(", %ssrc-comp-class-count=%u, %sflt-comp-class-count=%u, " + "%ssink-comp-class-count=%u", + PRFIELD(plugin->src_comp_classes->len), + PRFIELD(plugin->flt_comp_classes->len), + PRFIELD(plugin->sink_comp_classes->len)); + + if (plugin->spec_data) { + const struct bt_plugin_so_spec_data *spec_data = + (const void *) plugin->spec_data; + + if (spec_data->shared_lib_handle) { + SET_TMP_PREFIX("so-handle-"); + format_plugin_so_shared_lib_handle(buf_ch, tmp_prefix, + spec_data->shared_lib_handle); + } + } +} + +static inline void handle_conversion_specifier_bt(void *priv_data, + char **buf_ch, size_t avail_size, + const char **out_fmt_ch, va_list *args) +{ + const char *fmt_ch = *out_fmt_ch; + bool extended = false; + char prefix[64]; + char *prefix_ch = prefix; + const void *obj; + + /* skip "%!" */ + fmt_ch += 2; + + if (*fmt_ch == 'u') { + /* UUID */ + obj = va_arg(*args, void *); + format_uuid(buf_ch, obj); + goto update_fmt; + } + + if (*fmt_ch == '[') { + /* local prefix */ + fmt_ch++; + + while (true) { + if (*fmt_ch == ']') { + *prefix_ch = '\0'; + fmt_ch++; + break; + } + + *prefix_ch = *fmt_ch; + prefix_ch++; + fmt_ch++; + } + } + + *prefix_ch = '\0'; + + if (*fmt_ch == '+') { + extended = true; + fmt_ch++; + } + + obj = va_arg(*args, void *); + BUF_APPEND("%saddr=%p", prefix, obj); + + if (!obj) { + goto update_fmt; + } + + switch (*fmt_ch) { + case 'F': + format_field_class(buf_ch, extended, prefix, obj); + break; + case 'f': + format_field(buf_ch, extended, prefix, obj); + break; + case 'P': + format_field_path(buf_ch, extended, prefix, obj); + break; + case 'E': + format_event_class(buf_ch, extended, prefix, obj); + break; + case 'e': + format_event(buf_ch, extended, prefix, obj); + break; + case 'S': + format_stream_class(buf_ch, extended, prefix, obj); + break; + case 's': + format_stream(buf_ch, extended, prefix, obj); + break; + case 'a': + format_packet(buf_ch, extended, prefix, obj); + break; + case 't': + format_trace(buf_ch, extended, prefix, obj); + break; + case 'T': + format_trace_class(buf_ch, extended, prefix, obj); + break; + case 'K': + format_clock_class(buf_ch, extended, prefix, obj); + break; + case 'k': + format_clock_snapshot(buf_ch, extended, prefix, obj); + break; + case 'v': + format_value(buf_ch, extended, prefix, obj); + break; + case 'n': + format_message(buf_ch, extended, prefix, obj); + break; + case 'i': + format_message_iterator(buf_ch, extended, prefix, obj); + break; + case 'C': + format_component_class(buf_ch, extended, prefix, obj); + break; + case 'c': + format_component(buf_ch, extended, prefix, obj); + break; + case 'p': + format_port(buf_ch, extended, prefix, obj); + break; + case 'x': + format_connection(buf_ch, extended, prefix, obj); + break; + case 'l': + format_plugin(buf_ch, extended, prefix, obj); + break; + case 'g': + format_graph(buf_ch, extended, prefix, obj); + break; + case 'o': + format_object_pool(buf_ch, extended, prefix, obj); + break; + case 'O': + format_object(buf_ch, extended, prefix, obj); + break; + default: + abort(); + } + +update_fmt: + fmt_ch++; + *out_fmt_ch = fmt_ch; +} + +void bt_lib_log(const char *func, const char *file, unsigned line, + int lvl, const char *tag, const char *fmt, ...) +{ + va_list args; + + BT_ASSERT(fmt); + va_start(args, fmt); + bt_common_custom_vsnprintf(lib_logging_buf, LIB_LOGGING_BUF_SIZE, '!', + handle_conversion_specifier_bt, NULL, fmt, &args); + va_end(args); + _bt_log_write_d(func, file, line, lvl, tag, "%s", lib_logging_buf); +} diff --git a/src/lib/lib-logging.h b/src/lib/lib-logging.h new file mode 100644 index 00000000..59e85591 --- /dev/null +++ b/src/lib/lib-logging.h @@ -0,0 +1,193 @@ +#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H +#define BABELTRACE_LIB_LOGGING_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include + +#ifndef BT_LOG_TAG +# error Please define a tag with BT_LOG_TAG before including this file. +#endif + +#define BT_LOG_OUTPUT_LEVEL bt_lib_log_level + +#include "logging/log.h" + +extern +int bt_lib_log_level; + +#define BT_LIB_LOG(_lvl, _fmt, ...) \ + do { \ + if (BT_LOG_ON(_lvl)) { \ + bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__, \ + __LINE__, _lvl, _BT_LOG_TAG, \ + (_fmt), ##__VA_ARGS__); \ + } \ + } while (0) + +/* + * The six macros below are logging statements which are specialized + * for the Babeltrace library. + * + * `_fmt` is a typical printf()-style format string, with the following + * limitations: + * + * * The `*` width specifier is not accepted. + * * The `*` precision specifier is not accepted. + * * The `j` and `t` length modifiers are not accepted. + * * The `n` format specifier is not accepted. + * * The format specifiers defined in are not accepted + * except for `PRId64`, `PRIu64`, `PRIx64`, `PRIX64`, `PRIo64`, and + * `PRIi64`. + * + * The Babeltrace extension conversion specifier is accepted. Its syntax + * is either `%!u` to format a UUID (`bt_uuid` type) or: + * + * 1. Introductory `%!` sequence. + * + * 2. Optional: `[` followed by a custom prefix for the printed fields + * of this specifier, followed by `]`. The standard form is to end + * this prefix with `-` so that, for example, with the prefix + * `prefix-`, the complete field name is `prefix-addr`. + * + * 3. Optional: `+` to print extended fields. This depends on the + * provided format specifier. + * + * 4. Format specifier (see below). + * + * The available format specifiers are: + * + * `F`: + * Trace IR field class. The parameter type is + * `struct bt_field_class *`. + * + * `f`: + * Trace IR field. The parameter type is `struct bt_field *`. + * + * `P`: + * Field path. The parameter type is `struct bt_field_path *`. + * + * `E`: + * Trace IR event class. The parameter type is + * `struct bt_event_class *`. + * + * `e`: + * Trace IR event. The parameter type is `struct bt_event *`. + * + * `S`: + * Trace IR stream class. The parameter type is + * `struct bt_stream_class *`. + * + * `s`: + * Trace IR stream. The parameter type is `struct bt_stream *`. + * + * `a`: + * Packet. The parameter type is `struct bt_packet *`. + * + * `T`: + * Trace IR trace class. The parameter type is `struct bt_trace_class *`. + * + * `t`: + * Trace IR trace. The parameter type is `struct bt_trace *`. + * + * `K`: + * Clock class. The parameter type is `struct bt_clock_class *`. + * + * `k`: + * Clock snapshot. The parameter type is `struct bt_clock_snapshot *`. + * + * `v`: + * Value. The parameter type is `struct bt_value *`. + * + * `n`: + * Message. The parameter type is `struct bt_message *`. + * + * `i`: + * Message iterator. The parameter type is + * `struct bt_message_iterator *`. + * + * `C`: + * Component class. The parameter type is + * `struct bt_component_class *`. + * + * `c`: + * Component. The parameter type is `struct bt_component *`. + * + * `p`: + * Port. The parameter type is `struct bt_port *`. + * + * `x`: + * Connection. The parameter type is `struct bt_connection *`. + * + * `g`: + * Graph. The parameter type is `struct bt_graph *`. + * + * `l`: + * Plugin. The parameter type is `const struct bt_plugin *`. + * + * `o`: + * Object pool. The parameter type is `struct bt_object_pool *`. + * + * `O`: + * Object. The parameter type is `struct bt_object *`. + * + * Conversion specifier examples: + * + * %!f + * %![my-event-]+e + * %!t + * %!+F + * + * The string `, ` is printed between individual fields, but not after + * the last one. Therefore you must put this separator in the format + * string between two conversion specifiers, e.g.: + * + * BT_LIB_LOGW("Message: count=%u, %!E, %!+K", count, event_class, + * clock_class); + * + * Example with a custom prefix: + * + * BT_LIB_LOGI("Some message: %![ec-a-]e, %![ec-b-]+e", ec_a, ec_b); + * + * It is safe to pass NULL as any Babeltrace object parameter: the + * macros only print its null address. + */ +#define BT_LIB_LOGF(_fmt, ...) BT_LIB_LOG(BT_LOG_FATAL, _fmt, ##__VA_ARGS__) +#define BT_LIB_LOGE(_fmt, ...) BT_LIB_LOG(BT_LOG_ERROR, _fmt, ##__VA_ARGS__) +#define BT_LIB_LOGW(_fmt, ...) BT_LIB_LOG(BT_LOG_WARN, _fmt, ##__VA_ARGS__) +#define BT_LIB_LOGI(_fmt, ...) BT_LIB_LOG(BT_LOG_INFO, _fmt, ##__VA_ARGS__) +#define BT_LIB_LOGD(_fmt, ...) BT_LIB_LOG(BT_LOG_DEBUG, _fmt, ##__VA_ARGS__) +#define BT_LIB_LOGV(_fmt, ...) BT_LIB_LOG(BT_LOG_VERBOSE, _fmt, ##__VA_ARGS__) + +/* + * Log statement, specialized for the Babeltrace library. + * + * Use one of the BT_LIB_LOGF*() macros above instead of calling this + * function directly. + */ + +void bt_lib_log(const char *func, const char *file, unsigned line, + int lvl, const char *tag, const char *fmt, ...); + +#endif /* BABELTRACE_LIB_LOGGING_INTERNAL_H */ diff --git a/src/lib/logging.c b/src/lib/logging.c new file mode 100644 index 00000000..3c0178c2 --- /dev/null +++ b/src/lib/logging.c @@ -0,0 +1,89 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#define BT_LOG_TAG "LIB" +#include "lib/lib-logging.h" + +#ifdef BT_DEV_MODE +/* + * Default log level is FATAL in developer mode because fatal logging is + * our way to communicate an unsatisfied precondition and the details. + */ +# define DEFAULT_LOG_LEVEL BT_LOG_FATAL +#else +/* + * In non-developer mode, use NONE by default: we don't to print logging + * statements for any executable which links with the library. The + * executable should call bt_logging_set_global_level() or the + * executable's user should set the BABELTRACE_LOGGING_GLOBAL_LEVEL + * environment variable. + */ +# define DEFAULT_LOG_LEVEL BT_LOG_NONE +#endif /* BT_DEV_MODE */ + +int bt_lib_log_level = DEFAULT_LOG_LEVEL; + +enum bt_logging_level bt_logging_get_minimal_level(void) +{ + return BT_LOG_LEVEL; +} + +enum bt_logging_level bt_logging_get_global_level(void) +{ + return bt_lib_log_level; +} + +void bt_logging_set_global_level(enum bt_logging_level log_level) +{ +#ifdef BT_DEV_MODE + /* + * Do not allow the library's log level to fall to NONE when in + * developer mode because fatal logging is our way to + * communicate an unsatisfied precondition and the details. + */ + if (log_level == BT_LOG_NONE) { + log_level = BT_LOG_FATAL; + } +#endif + + bt_lib_log_level = log_level; +} + +static +void __attribute__((constructor)) bt_logging_ctor(void) +{ + const char *v_extra = bt_version_get_extra() ? bt_version_get_extra() : + ""; + + bt_logging_set_global_level( + bt_log_get_level_from_env("BABELTRACE_LOGGING_GLOBAL_LEVEL")); + BT_LOGI("Babeltrace %d.%d.%d%s library loaded: " + "major=%d, minor=%d, patch=%d, extra=\"%s\"", + bt_version_get_major(), bt_version_get_minor(), + bt_version_get_patch(), v_extra, + bt_version_get_major(), bt_version_get_minor(), + bt_version_get_patch(), v_extra); +} diff --git a/src/lib/object-pool.c b/src/lib/object-pool.c new file mode 100644 index 00000000..2542047a --- /dev/null +++ b/src/lib/object-pool.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "OBJECT-POOL" +#include "lib/lib-logging.h" + +#include +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "lib/object-pool.h" + +int bt_object_pool_initialize(struct bt_object_pool *pool, + bt_object_pool_new_object_func new_object_func, + bt_object_pool_destroy_object_func destroy_object_func, + void *data) +{ + int ret = 0; + + BT_ASSERT(new_object_func); + BT_ASSERT(destroy_object_func); + BT_LOGD("Initializing object pool: addr=%p, data-addr=%p", + pool, data); + pool->objects = g_ptr_array_new(); + if (!pool->objects) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + pool->funcs.new_object = new_object_func; + pool->funcs.destroy_object = destroy_object_func; + pool->data = data; + pool->size = 0; + BT_LIB_LOGD("Initialized object pool: %!+o", pool); + goto end; + +error: + if (pool) { + bt_object_pool_finalize(pool); + } + + ret = -1; + +end: + return ret; +} + +void bt_object_pool_finalize(struct bt_object_pool *pool) +{ + uint64_t i; + + BT_ASSERT(pool); + BT_LIB_LOGD("Finalizing object pool: %!+o", pool); + + if (pool->objects) { + for (i = 0; i < pool->size; i++) { + void *obj = pool->objects->pdata[i]; + + if (obj) { + pool->funcs.destroy_object(obj, pool->data); + } + } + + g_ptr_array_free(pool->objects, TRUE); + pool->objects = NULL; + } +} diff --git a/src/lib/object-pool.h b/src/lib/object-pool.h new file mode 100644 index 00000000..eb7833fd --- /dev/null +++ b/src/lib/object-pool.h @@ -0,0 +1,182 @@ +#ifndef BABELTRACE_OBJECT_POOL_INTERNAL_H +#define BABELTRACE_OBJECT_POOL_INTERNAL_H + +/* + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This is a generic object pool to avoid memory allocation/deallocation + * for objects of which the lifespan is typically short, but which are + * created a lot. + * + * The object pool, thanks to two user functions, knows how to allocate + * a brand new object in memory when the pool is empty and how to + * destroy an object when we destroy the pool. + * + * The object pool's user is responsible for: + * + * * Setting whatever references the object needs to keep and reset some + * properties _after_ calling bt_object_pool_create_object(). This is + * typically done in the bt_*_create() function which calls + * bt_object_pool_create_object() (which could call the user-provided + * allocation function if the pool is empty) and then sets the + * appropriate properties on the possibly recycled object. + * + * * Releasing whatever references the object keeps _before_ calling + * bt_object_pool_recycle_object(). This is typically done in a custom + * bt_*_recycle() function which does the necessary before calling + * bt_object_pool_recycle_object() with an object ready to be reused + * at any time. + */ + +#include +#include "lib/object.h" + +typedef void *(*bt_object_pool_new_object_func)(void *data); +typedef void *(*bt_object_pool_destroy_object_func)(void *obj, void *data); + +struct bt_object_pool { + /* + * Container of recycled objects, owned by this. The array's size + * is the pool's capacity. + */ + GPtrArray *objects; + + /* + * Pool's size, that is, number of elements in the array above, + * starting at index 0, which exist as recycled objects. + */ + size_t size; + + /* User functions */ + struct { + /* Allocate a new object in memory */ + bt_object_pool_new_object_func new_object; + + /* Free direct and indirect memory occupied by object */ + bt_object_pool_destroy_object_func destroy_object; + } funcs; + + /* User data passed to user functions */ + void *data; +}; + +/* + * Initializes an object pool which is already allocated. + */ +int bt_object_pool_initialize(struct bt_object_pool *pool, + bt_object_pool_new_object_func new_object_func, + bt_object_pool_destroy_object_func destroy_object_func, + void *data); + +/* + * Finalizes an object pool without deallocating it. + */ +void bt_object_pool_finalize(struct bt_object_pool *pool); + +/* + * Creates an object from an object pool. If the pool is empty, this + * function calls the "new" user function to allocate a new object + * before returning it. Otherwise this function returns a recycled + * object, removing it from the pool. + * + * The returned object is owned by the caller. + */ +static inline +void *bt_object_pool_create_object(struct bt_object_pool *pool) +{ + struct bt_object *obj; + + BT_ASSERT(pool); + +#ifdef BT_LOGV + BT_LOGV("Creating object from pool: pool-addr=%p, pool-size=%zu, pool-cap=%u", + pool, pool->size, pool->objects->len); +#endif + + if (pool->size > 0) { + /* Pick one from the pool */ + pool->size--; + obj = pool->objects->pdata[pool->size]; + pool->objects->pdata[pool->size] = NULL; + goto end; + } + + /* Pool is empty: create a brand new object */ +#ifdef BT_LOGV + BT_LOGV("Pool is empty: allocating new object: pool-addr=%p", + pool); +#endif + + obj = pool->funcs.new_object(pool->data); + +end: +#ifdef BT_LOGV + BT_LOGV("Created one object from pool: pool-addr=%p, obj-addr=%p", + pool, obj); +#endif + + return obj; +} + +/* + * Recycles an object, that is, puts it back into the pool. + * + * The pool becomes the sole owner of the object to recycle. + */ +static inline +void bt_object_pool_recycle_object(struct bt_object_pool *pool, void *obj) +{ + struct bt_object *bt_obj = obj; + + BT_ASSERT(pool); + BT_ASSERT(obj); + +#ifdef BT_LOGV + BT_LOGV("Recycling object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", + pool, pool->size, pool->objects->len, obj); +#endif + + if (pool->size == pool->objects->len) { + /* Backing array is full: make place for recycled object */ +#ifdef BT_LOGV + BT_LOGV("Object pool is full: increasing object pool capacity: " + "pool-addr=%p, old-pool-cap=%u, new-pool-cap=%u", + pool, pool->objects->len, pool->objects->len + 1); +#endif + g_ptr_array_set_size(pool->objects, pool->size + 1); + } + + /* Reset reference count to 1 since it could be 0 now */ + bt_obj->ref_count = 1; + + /* Back to the pool */ + pool->objects->pdata[pool->size] = obj; + pool->size++; + +#ifdef BT_LOGV + BT_LOGV("Recycled object: pool-addr=%p, pool-size=%zu, pool-cap=%u, obj-addr=%p", + pool, pool->size, pool->objects->len, obj); +#endif +} + +#endif /* BABELTRACE_OBJECT_POOL_INTERNAL_H */ diff --git a/src/lib/object.h b/src/lib/object.h new file mode 100644 index 00000000..c3afd73f --- /dev/null +++ b/src/lib/object.h @@ -0,0 +1,374 @@ +#ifndef BABELTRACE_OBJECT_INTERNAL_H +#define BABELTRACE_OBJECT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "common/assert.h" +#include + +struct bt_object; + +typedef void (*bt_object_release_func)(struct bt_object *); +typedef void (*bt_object_parent_is_owner_listener_func)( + struct bt_object *); + +static inline +void bt_object_get_no_null_check(const void *obj); + +static inline +void bt_object_put_no_null_check(const void *obj); + +/* + * Babeltrace object base. + * + * All objects publicly exposed by Babeltrace APIs must contain this + * object as their first member. + */ +struct bt_object { + /* + * True if this object is shared, that is, it has a reference + * count. + */ + bool is_shared; + + /* + * Current reference count. + */ + unsigned long long ref_count; + + /* + * Release function called when the object's reference count + * falls to zero. For an object with a parent, this function is + * bt_object_with_parent_release_func(), which calls + * `spec_release_func` below if there's no current parent. + */ + bt_object_release_func release_func; + + /* + * Specific release function called by + * bt_object_with_parent_release_func() or directly by a + * parent object. + */ + bt_object_release_func spec_release_func; + + /* + * Optional callback for an object with a parent, called by + * bt_object_with_parent_release_func() to indicate to the + * object that its parent is its owner. + */ + bt_object_parent_is_owner_listener_func + parent_is_owner_listener_func; + + /* + * Optional parent object. + */ + struct bt_object *parent; +}; + +static inline +unsigned long long bt_object_get_ref_count(const struct bt_object *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + return obj->ref_count; +} + +static inline +struct bt_object *bt_object_borrow_parent(const struct bt_object *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + return obj->parent; +} + +static inline +struct bt_object *bt_object_get_parent(const struct bt_object *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + struct bt_object *parent = bt_object_borrow_parent(obj); + + if (parent) { + bt_object_get_no_null_check(parent); + } + + return parent; +} + +static inline +void bt_object_set_parent(struct bt_object *child, struct bt_object *parent) +{ + BT_ASSERT(child); + BT_ASSERT(child->is_shared); + +#ifdef BT_LOGV + BT_LOGV("Setting object's parent: addr=%p, parent-addr=%p", + child, parent); +#endif + + /* + * It is assumed that a "child" having a parent is publicly + * reachable. Therefore, a reference to its parent must be + * taken. The reference to the parent will be released once the + * object's reference count falls to zero. + */ + if (parent) { + BT_ASSERT(!child->parent); + child->parent = parent; + bt_object_get_no_null_check(parent); + } else { + if (child->parent) { + bt_object_put_no_null_check(child->parent); + } + + child->parent = NULL; + } +} + +static inline +void bt_object_try_spec_release(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->spec_release_func); + + if (bt_object_get_ref_count(obj) == 0) { + obj->spec_release_func(obj); + } +} + +static inline +void bt_object_with_parent_release_func(struct bt_object *obj) +{ + if (obj->parent) { + /* + * Keep our own copy of the parent address because `obj` + * could be destroyed in + * obj->parent_is_owner_listener_func(). + */ + struct bt_object *parent = obj->parent; + +#ifdef BT_LOGV + BT_LOGV("Releasing parented object: addr=%p, ref-count=%llu, " + "parent-addr=%p, parent-ref-count=%llu", + obj, obj->ref_count, + parent, parent->ref_count); +#endif + + if (obj->parent_is_owner_listener_func) { + /* + * Object has a chance to destroy itself here + * under certain conditions and notify its + * parent. At this point the parent is + * guaranteed to exist because it's not put yet. + */ + obj->parent_is_owner_listener_func(obj); + } + + /* The release function will be invoked by the parent. */ + bt_object_put_no_null_check(parent); + } else { + bt_object_try_spec_release(obj); + } +} + +static inline +void bt_object_init(struct bt_object *obj, bool is_shared, + bt_object_release_func release_func) +{ + BT_ASSERT(obj); + BT_ASSERT(!is_shared || release_func); + obj->is_shared = is_shared; + obj->release_func = release_func; + obj->parent_is_owner_listener_func = NULL; + obj->spec_release_func = NULL; + obj->parent = NULL; + obj->ref_count = 1; +} + +static inline +void bt_object_init_shared(struct bt_object *obj, + bt_object_release_func release_func) +{ + bt_object_init(obj, true, release_func); +} + +static inline +void bt_object_init_unique(struct bt_object *obj) +{ + bt_object_init(obj, false, NULL); +} + +static inline +void bt_object_init_shared_with_parent(struct bt_object *obj, + bt_object_release_func spec_release_func) +{ + BT_ASSERT(obj); + BT_ASSERT(spec_release_func); + bt_object_init_shared(obj, bt_object_with_parent_release_func); + obj->spec_release_func = spec_release_func; +} + +static inline +void bt_object_set_parent_is_owner_listener_func(struct bt_object *obj, + bt_object_parent_is_owner_listener_func func) +{ + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->spec_release_func); + ((struct bt_object *) obj)->parent_is_owner_listener_func = func; +} + +static inline +void bt_object_inc_ref_count(const struct bt_object *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + obj->ref_count++; + BT_ASSERT(obj->ref_count != 0); +} + +static inline +void bt_object_get_no_null_check_no_parent_check(const struct bt_object *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + +#ifdef BT_LOGV + BT_LOGV("Incrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count + 1, + obj, obj->ref_count, obj->ref_count + 1); +#endif + + bt_object_inc_ref_count(obj); +} + +static inline +void bt_object_get_no_null_check(const void *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + + if (unlikely(obj->parent && bt_object_get_ref_count(obj) == 0)) { +#ifdef BT_LOGV + BT_LOGV("Incrementing object's parent's reference count: " + "addr=%p, parent-addr=%p", obj, obj->parent); +#endif + + bt_object_get_no_null_check(obj->parent); + } + +#ifdef BT_LOGV + BT_LOGV("Incrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count + 1, + obj, obj->ref_count, obj->ref_count + 1); +#endif + + bt_object_inc_ref_count(obj); +} + +static inline +void bt_object_put_no_null_check(const void *c_obj) +{ + struct bt_object *obj = (void *) c_obj; + + BT_ASSERT(obj); + BT_ASSERT(obj->is_shared); + BT_ASSERT(obj->ref_count > 0); + +#ifdef BT_LOGV + BT_LOGV("Decrementing object's reference count: %llu -> %llu: " + "addr=%p, cur-count=%llu, new-count=%llu", + obj->ref_count, obj->ref_count - 1, + obj, obj->ref_count, obj->ref_count - 1); +#endif + + obj->ref_count--; + + if (obj->ref_count == 0) { + BT_ASSERT(obj->release_func); + obj->release_func(obj); + } +} + +static inline +void bt_object_get_ref(const void *ptr) +{ + struct bt_object *obj = (void *) ptr; + + if (unlikely(!obj)) { + return; + } + +#ifdef BT_ASSERT_PRE + BT_ASSERT_PRE(obj->is_shared, "Object is not shared: %!+O", obj); +#endif + + bt_object_get_no_null_check(obj); +} + +static inline +void bt_object_put_ref(const void *ptr) +{ + struct bt_object *obj = (void *) ptr; + + if (unlikely(!obj)) { + return; + } + +#ifdef BT_ASSERT_PRE + BT_ASSERT_PRE(obj->is_shared, "Object is not shared: %!+O", obj); + BT_ASSERT_PRE(bt_object_get_ref_count(obj) > 0, + "Decrementing a reference count set to 0: %!+O", ptr); +#endif + + bt_object_put_no_null_check(obj); +} + +#define BT_OBJECT_PUT_REF_AND_RESET(_var) \ + do { \ + bt_object_put_ref(_var); \ + (_var) = NULL; \ + } while (0) + +#define BT_OBJECT_MOVE_REF(_var_dst, _var_src) \ + do { \ + bt_object_put_ref(_var_dst); \ + (_var_dst) = (_var_src); \ + (_var_src) = NULL; \ + } while (0) + +#endif /* BABELTRACE_OBJECT_INTERNAL_H */ diff --git a/src/lib/plugin/Makefile.am b/src/lib/plugin/Makefile.am new file mode 100644 index 00000000..d3133dd8 --- /dev/null +++ b/src/lib/plugin/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libplugin.la + +# Plug-in system library +libplugin_la_SOURCES = \ + plugin.c \ + plugin.h \ + plugin-so.c \ + plugin-so.h diff --git a/src/lib/plugin/plugin-so.c b/src/lib/plugin/plugin-so.c new file mode 100644 index 00000000..621353c0 --- /dev/null +++ b/src/lib/plugin/plugin-so.c @@ -0,0 +1,1620 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-SO" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "compat/compiler.h" +#include +#include "lib/graph/component-class.h" +#include +#include +#include +#include +#include +#include "common/list.h" +#include +#include +#include +#include + +#include "plugin.h" +#include "plugin-so.h" + +#define NATIVE_PLUGIN_SUFFIX "." G_MODULE_SUFFIX +#define NATIVE_PLUGIN_SUFFIX_LEN sizeof(NATIVE_PLUGIN_SUFFIX) +#define LIBTOOL_PLUGIN_SUFFIX ".la" +#define LIBTOOL_PLUGIN_SUFFIX_LEN sizeof(LIBTOOL_PLUGIN_SUFFIX) + +#define PLUGIN_SUFFIX_LEN max_t(size_t, sizeof(NATIVE_PLUGIN_SUFFIX), \ + sizeof(LIBTOOL_PLUGIN_SUFFIX)) + +BT_PLUGIN_MODULE(); + +/* + * This list, global to the library, keeps all component classes that + * have a reference to their shared library handles. It allows iteration + * on all component classes still present when the destructor executes + * to release the shared library handle references they might still have. + * + * The list items are the component classes created with + * bt_plugin_add_component_class(). They keep the shared library handle + * object created by their plugin alive so that the plugin's code is + * not discarded when it could still be in use by living components + * created from those component classes: + * + * [component] --ref-> [component class]-> [shlib handle] + * + * It allows this use-case: + * + * my_plugins = bt_plugin_find_all_from_file("/path/to/my-plugin.so"); + * // instantiate components from a plugin's component classes + * // put plugins and free my_plugins here + * // user code of instantiated components still exists + * + * An entry is removed from this list when a component class is + * destroyed thanks to a custom destroy listener. When the entry is + * removed, the entry is removed from the list, and we release the + * reference on the shlib handle. Assuming the original plugin object + * which contained some component classes is put first, when the last + * component class is removed from this list, the shared library handle + * object's reference count falls to zero and the shared library is + * finally closed. + */ + +static +BT_LIST_HEAD(component_class_list); + +__attribute__((destructor)) static +void fini_comp_class_list(void) +{ + struct bt_component_class *comp_class, *tmp; + + bt_list_for_each_entry_safe(comp_class, tmp, &component_class_list, node) { + bt_list_del(&comp_class->node); + BT_OBJECT_PUT_REF_AND_RESET(comp_class->so_handle); + } + + BT_LOGD_STR("Released references from all component classes to shared library handles."); +} + +static inline +const char *bt_self_plugin_status_string(enum bt_self_plugin_status status) +{ + switch (status) { + case BT_SELF_PLUGIN_STATUS_OK: + return "BT_SELF_PLUGIN_STATUS_OK"; + case BT_SELF_PLUGIN_STATUS_ERROR: + return "BT_SELF_PLUGIN_STATUS_ERROR"; + case BT_SELF_PLUGIN_STATUS_NOMEM: + return "BT_SELF_PLUGIN_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +static +void bt_plugin_so_shared_lib_handle_destroy(struct bt_object *obj) +{ + struct bt_plugin_so_shared_lib_handle *shared_lib_handle; + + BT_ASSERT(obj); + shared_lib_handle = container_of(obj, + struct bt_plugin_so_shared_lib_handle, base); + const char *path = shared_lib_handle->path ? + shared_lib_handle->path->str : NULL; + + BT_LOGD("Destroying shared library handle: addr=%p, path=\"%s\"", + shared_lib_handle, path); + + if (shared_lib_handle->init_called && shared_lib_handle->exit) { + BT_LOGD_STR("Calling user's plugin exit function."); + shared_lib_handle->exit(); + BT_LOGD_STR("User function returned."); + } + + if (shared_lib_handle->module) { +#ifndef NDEBUG + /* + * Valgrind shows incomplete stack traces when + * dynamically loaded libraries are closed before it + * finishes. Use the BABELTRACE_NO_DLCLOSE in a debug + * build to avoid this. + */ + const char *var = getenv("BABELTRACE_NO_DLCLOSE"); + + if (!var || strcmp(var, "1") != 0) { +#endif + BT_LOGD("Closing GModule: path=\"%s\"", path); + + if (!g_module_close(shared_lib_handle->module)) { + BT_LOGE("Cannot close GModule: %s: path=\"%s\"", + g_module_error(), path); + } + + shared_lib_handle->module = NULL; +#ifndef NDEBUG + } else { + BT_LOGD("Not closing GModule because `BABELTRACE_NO_DLCLOSE=1`: " + "path=\"%s\"", path); + } +#endif + } + + if (shared_lib_handle->path) { + g_string_free(shared_lib_handle->path, TRUE); + shared_lib_handle->path = NULL; + } + + g_free(shared_lib_handle); +} + +static +struct bt_plugin_so_shared_lib_handle *bt_plugin_so_shared_lib_handle_create( + const char *path) +{ + struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL; + + BT_LOGD("Creating shared library handle: path=\"%s\"", path); + shared_lib_handle = g_new0(struct bt_plugin_so_shared_lib_handle, 1); + if (!shared_lib_handle) { + BT_LOGE_STR("Failed to allocate one shared library handle."); + goto error; + } + + bt_object_init_shared(&shared_lib_handle->base, + bt_plugin_so_shared_lib_handle_destroy); + + if (!path) { + goto end; + } + + shared_lib_handle->path = g_string_new(path); + if (!shared_lib_handle->path) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + shared_lib_handle->module = g_module_open(path, G_MODULE_BIND_LOCAL); + if (!shared_lib_handle->module) { + /* + * DEBUG-level logging because we're only _trying_ to + * open this file as a Babeltrace plugin: if it's not, + * it's not an error. And because this can be tried + * during bt_plugin_find_all_from_dir(), it's not even + * a warning. + */ + BT_LOGD("Cannot open GModule: %s: path=\"%s\"", + g_module_error(), path); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); + +end: + if (shared_lib_handle) { + BT_LOGD("Created shared library handle: path=\"%s\", addr=%p", + path, shared_lib_handle); + } + + return shared_lib_handle; +} + +static +void bt_plugin_so_destroy_spec_data(struct bt_plugin *plugin) +{ + struct bt_plugin_so_spec_data *spec = plugin->spec_data; + + if (!plugin->spec_data) { + return; + } + + BT_ASSERT(plugin->type == BT_PLUGIN_TYPE_SO); + BT_ASSERT(spec); + BT_OBJECT_PUT_REF_AND_RESET(spec->shared_lib_handle); + g_free(plugin->spec_data); + plugin->spec_data = NULL; +} + +/* + * This function does the following: + * + * 1. Iterate on the plugin descriptor attributes section and set the + * plugin's attributes depending on the attribute types. This + * includes the name of the plugin, its description, and its + * initialization function, for example. + * + * 2. Iterate on the component class descriptors section and create one + * "full descriptor" (temporary structure) for each one that is found + * and attached to our plugin descriptor. + * + * 3. Iterate on the component class descriptor attributes section and + * set the corresponding full descriptor's attributes depending on + * the attribute types. This includes the description of the + * component class, as well as its initialization and destroy + * methods. + * + * 4. Call the user's plugin initialization function, if any is + * defined. + * + * 5. For each full component class descriptor, create a component class + * object, set its optional attributes, and add it to the plugin + * object. + * + * 6. Freeze the plugin object. + */ +static +enum bt_plugin_status bt_plugin_so_init( + struct bt_plugin *plugin, + const struct __bt_plugin_descriptor *descriptor, + struct __bt_plugin_descriptor_attribute const * const *attrs_begin, + struct __bt_plugin_descriptor_attribute const * const *attrs_end, + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin, + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end, + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin, + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end) +{ + /* + * This structure's members point to the plugin's memory + * (do NOT free). + */ + struct comp_class_full_descriptor { + const struct __bt_plugin_component_class_descriptor *descriptor; + const char *description; + const char *help; + + union { + struct { + bt_component_class_source_init_method init; + bt_component_class_source_finalize_method finalize; + bt_component_class_source_query_method query; + bt_component_class_source_accept_output_port_connection_method accept_output_port_connection; + bt_component_class_source_output_port_connected_method output_port_connected; + bt_component_class_source_message_iterator_init_method msg_iter_init; + bt_component_class_source_message_iterator_finalize_method msg_iter_finalize; + bt_component_class_source_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; + bt_component_class_source_message_iterator_seek_beginning_method msg_iter_seek_beginning; + bt_component_class_source_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; + bt_component_class_source_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; + } source; + + struct { + bt_component_class_filter_init_method init; + bt_component_class_filter_finalize_method finalize; + bt_component_class_filter_query_method query; + bt_component_class_filter_accept_input_port_connection_method accept_input_port_connection; + bt_component_class_filter_accept_output_port_connection_method accept_output_port_connection; + bt_component_class_filter_input_port_connected_method input_port_connected; + bt_component_class_filter_output_port_connected_method output_port_connected; + bt_component_class_filter_message_iterator_init_method msg_iter_init; + bt_component_class_filter_message_iterator_finalize_method msg_iter_finalize; + bt_component_class_filter_message_iterator_seek_ns_from_origin_method msg_iter_seek_ns_from_origin; + bt_component_class_filter_message_iterator_seek_beginning_method msg_iter_seek_beginning; + bt_component_class_filter_message_iterator_can_seek_ns_from_origin_method msg_iter_can_seek_ns_from_origin; + bt_component_class_filter_message_iterator_can_seek_beginning_method msg_iter_can_seek_beginning; + } filter; + + struct { + bt_component_class_sink_init_method init; + bt_component_class_sink_finalize_method finalize; + bt_component_class_sink_query_method query; + bt_component_class_sink_accept_input_port_connection_method accept_input_port_connection; + bt_component_class_sink_input_port_connected_method input_port_connected; + bt_component_class_sink_graph_is_configured_method graph_is_configured; + } sink; + } methods; + }; + + enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; + struct __bt_plugin_descriptor_attribute const * const *cur_attr_ptr; + struct __bt_plugin_component_class_descriptor const * const *cur_cc_descr_ptr; + struct __bt_plugin_component_class_descriptor_attribute const * const *cur_cc_descr_attr_ptr; + struct bt_plugin_so_spec_data *spec = plugin->spec_data; + GArray *comp_class_full_descriptors; + size_t i; + int ret; + + BT_LOGD("Initializing plugin object from descriptors found in sections: " + "plugin-addr=%p, plugin-path=\"%s\", " + "attrs-begin-addr=%p, attrs-end-addr=%p, " + "cc-descr-begin-addr=%p, cc-descr-end-addr=%p, " + "cc-descr-attrs-begin-addr=%p, cc-descr-attrs-end-addr=%p", + plugin, + spec->shared_lib_handle->path ? + spec->shared_lib_handle->path->str : NULL, + attrs_begin, attrs_end, + cc_descriptors_begin, cc_descriptors_end, + cc_descr_attrs_begin, cc_descr_attrs_end); + comp_class_full_descriptors = g_array_new(FALSE, TRUE, + sizeof(struct comp_class_full_descriptor)); + if (!comp_class_full_descriptors) { + BT_LOGE_STR("Failed to allocate a GArray."); + status = BT_PLUGIN_STATUS_ERROR; + goto end; + } + + /* Set mandatory attributes */ + spec->descriptor = descriptor; + bt_plugin_set_name(plugin, descriptor->name); + + /* + * Find and set optional attributes attached to this plugin + * descriptor. + */ + for (cur_attr_ptr = attrs_begin; cur_attr_ptr != attrs_end; cur_attr_ptr++) { + const struct __bt_plugin_descriptor_attribute *cur_attr = + *cur_attr_ptr; + + if (cur_attr == NULL) { + continue; + } + + if (cur_attr->plugin_descriptor != descriptor) { + continue; + } + + switch (cur_attr->type) { + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_INIT: + spec->init = cur_attr->value.init; + break; + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_EXIT: + spec->shared_lib_handle->exit = cur_attr->value.exit; + break; + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_AUTHOR: + bt_plugin_set_author(plugin, cur_attr->value.author); + break; + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_LICENSE: + bt_plugin_set_license(plugin, cur_attr->value.license); + break; + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_DESCRIPTION: + bt_plugin_set_description(plugin, cur_attr->value.description); + break; + case BT_PLUGIN_DESCRIPTOR_ATTRIBUTE_TYPE_VERSION: + bt_plugin_set_version(plugin, + (unsigned int) cur_attr->value.version.major, + (unsigned int) cur_attr->value.version.minor, + (unsigned int) cur_attr->value.version.patch, + cur_attr->value.version.extra); + break; + default: + /* + * WARN-level logging because this should not + * happen with the appropriate ABI version. If + * we're here, we know that for the reported + * version of the ABI, this attribute is + * unknown. + */ + BT_LOGW("Ignoring unknown plugin descriptor attribute: " + "plugin-path=\"%s\", plugin-name=\"%s\", " + "attr-type-name=\"%s\", attr-type-id=%d", + spec->shared_lib_handle->path ? + spec->shared_lib_handle->path->str : + NULL, + descriptor->name, cur_attr->type_name, + cur_attr->type); + break; + } + } + + /* + * Find component class descriptors attached to this plugin + * descriptor and initialize corresponding full component class + * descriptors in the array. + */ + for (cur_cc_descr_ptr = cc_descriptors_begin; cur_cc_descr_ptr != cc_descriptors_end; cur_cc_descr_ptr++) { + const struct __bt_plugin_component_class_descriptor *cur_cc_descr = + *cur_cc_descr_ptr; + struct comp_class_full_descriptor full_descriptor = {0}; + + if (cur_cc_descr == NULL) { + continue; + } + + if (cur_cc_descr->plugin_descriptor != descriptor) { + continue; + } + + full_descriptor.descriptor = cur_cc_descr; + g_array_append_val(comp_class_full_descriptors, + full_descriptor); + } + + /* + * Find component class descriptor attributes attached to this + * plugin descriptor and update corresponding full component + * class descriptors in the array. + */ + for (cur_cc_descr_attr_ptr = cc_descr_attrs_begin; cur_cc_descr_attr_ptr != cc_descr_attrs_end; cur_cc_descr_attr_ptr++) { + const struct __bt_plugin_component_class_descriptor_attribute *cur_cc_descr_attr = + *cur_cc_descr_attr_ptr; + enum bt_component_class_type cc_type; + + if (cur_cc_descr_attr == NULL) { + continue; + } + + if (cur_cc_descr_attr->comp_class_descriptor->plugin_descriptor != + descriptor) { + continue; + } + + cc_type = cur_cc_descr_attr->comp_class_descriptor->type; + + /* Find the corresponding component class descriptor entry */ + for (i = 0; i < comp_class_full_descriptors->len; i++) { + struct comp_class_full_descriptor *cc_full_descr = + &g_array_index(comp_class_full_descriptors, + struct comp_class_full_descriptor, i); + + if (cur_cc_descr_attr->comp_class_descriptor != + cc_full_descr->descriptor) { + continue; + } + + switch (cur_cc_descr_attr->type) { + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_DESCRIPTION: + cc_full_descr->description = + cur_cc_descr_attr->value.description; + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_HELP: + cc_full_descr->help = + cur_cc_descr_attr->value.help; + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_INIT_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.init = + cur_cc_descr_attr->value.source_init_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.init = + cur_cc_descr_attr->value.filter_init_method; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.init = + cur_cc_descr_attr->value.sink_init_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_FINALIZE_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.finalize = + cur_cc_descr_attr->value.source_finalize_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.finalize = + cur_cc_descr_attr->value.filter_finalize_method; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.finalize = + cur_cc_descr_attr->value.sink_finalize_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_QUERY_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.query = + cur_cc_descr_attr->value.source_query_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.query = + cur_cc_descr_attr->value.filter_query_method; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.query = + cur_cc_descr_attr->value.sink_query_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_ACCEPT_INPUT_PORT_CONNECTION_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.accept_input_port_connection = + cur_cc_descr_attr->value.filter_accept_input_port_connection_method; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.accept_input_port_connection = + cur_cc_descr_attr->value.sink_accept_input_port_connection_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_ACCEPT_OUTPUT_PORT_CONNECTION_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.accept_output_port_connection = + cur_cc_descr_attr->value.source_accept_output_port_connection_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.accept_output_port_connection = + cur_cc_descr_attr->value.filter_accept_output_port_connection_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_INPUT_PORT_CONNECTED_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.input_port_connected = + cur_cc_descr_attr->value.filter_input_port_connected_method; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.input_port_connected = + cur_cc_descr_attr->value.sink_input_port_connected_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_OUTPUT_PORT_CONNECTED_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.output_port_connected = + cur_cc_descr_attr->value.source_output_port_connected_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.output_port_connected = + cur_cc_descr_attr->value.filter_output_port_connected_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_GRAPH_IS_CONFIGURED_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SINK: + cc_full_descr->methods.sink.graph_is_configured = + cur_cc_descr_attr->value.sink_graph_is_configured_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_INIT_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_init = + cur_cc_descr_attr->value.source_msg_iter_init_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_init = + cur_cc_descr_attr->value.filter_msg_iter_init_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_FINALIZE_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_finalize = + cur_cc_descr_attr->value.source_msg_iter_finalize_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_finalize = + cur_cc_descr_attr->value.filter_msg_iter_finalize_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_SEEK_NS_FROM_ORIGIN_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_seek_ns_from_origin = + cur_cc_descr_attr->value.source_msg_iter_seek_ns_from_origin_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin = + cur_cc_descr_attr->value.filter_msg_iter_seek_ns_from_origin_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_SEEK_BEGINNING_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_seek_beginning = + cur_cc_descr_attr->value.source_msg_iter_seek_beginning_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_seek_beginning = + cur_cc_descr_attr->value.filter_msg_iter_seek_beginning_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_CAN_SEEK_NS_FROM_ORIGIN_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin = + cur_cc_descr_attr->value.source_msg_iter_can_seek_ns_from_origin_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin = + cur_cc_descr_attr->value.filter_msg_iter_can_seek_ns_from_origin_method; + break; + default: + abort(); + } + break; + case BT_PLUGIN_COMPONENT_CLASS_DESCRIPTOR_ATTRIBUTE_TYPE_MSG_ITER_CAN_SEEK_BEGINNING_METHOD: + switch (cc_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cc_full_descr->methods.source.msg_iter_can_seek_beginning = + cur_cc_descr_attr->value.source_msg_iter_can_seek_beginning_method; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cc_full_descr->methods.filter.msg_iter_can_seek_beginning = + cur_cc_descr_attr->value.filter_msg_iter_can_seek_beginning_method; + break; + default: + abort(); + } + break; + default: + /* + * WARN-level logging because this + * should not happen with the + * appropriate ABI version. If we're + * here, we know that for the reported + * version of the ABI, this attribute is + * unknown. + */ + BT_LOGW("Ignoring unknown component class descriptor attribute: " + "plugin-path=\"%s\", " + "plugin-name=\"%s\", " + "comp-class-name=\"%s\", " + "comp-class-type=%s, " + "attr-type-name=\"%s\", " + "attr-type-id=%d", + spec->shared_lib_handle->path ? + spec->shared_lib_handle->path->str : + NULL, + descriptor->name, + cur_cc_descr_attr->comp_class_descriptor->name, + bt_component_class_type_string( + cur_cc_descr_attr->comp_class_descriptor->type), + cur_cc_descr_attr->type_name, + cur_cc_descr_attr->type); + break; + } + } + } + + /* Initialize plugin */ + if (spec->init) { + enum bt_self_plugin_status init_status; + + BT_LOGD_STR("Calling user's plugin initialization function."); + init_status = spec->init((void *) plugin); + BT_LOGD("User function returned: %s", + bt_self_plugin_status_string(init_status)); + + if (init_status < 0) { + BT_LOGW_STR("User's plugin initialization function failed."); + status = BT_PLUGIN_STATUS_ERROR; + goto end; + } + } + + spec->shared_lib_handle->init_called = BT_TRUE; + + /* Add described component classes to plugin */ + for (i = 0; i < comp_class_full_descriptors->len; i++) { + struct comp_class_full_descriptor *cc_full_descr = + &g_array_index(comp_class_full_descriptors, + struct comp_class_full_descriptor, i); + struct bt_component_class *comp_class = NULL; + struct bt_component_class_source *src_comp_class = NULL; + struct bt_component_class_filter *flt_comp_class = NULL; + struct bt_component_class_sink *sink_comp_class = NULL; + + BT_LOGD("Creating and setting properties of plugin's component class: " + "plugin-path=\"%s\", plugin-name=\"%s\", " + "comp-class-name=\"%s\", comp-class-type=%s", + spec->shared_lib_handle->path ? + spec->shared_lib_handle->path->str : + NULL, + descriptor->name, + cc_full_descr->descriptor->name, + bt_component_class_type_string( + cc_full_descr->descriptor->type)); + + switch (cc_full_descr->descriptor->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + src_comp_class = bt_component_class_source_create( + cc_full_descr->descriptor->name, + cc_full_descr->descriptor->methods.source.msg_iter_next); + comp_class = bt_component_class_source_as_component_class( + src_comp_class); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + flt_comp_class = bt_component_class_filter_create( + cc_full_descr->descriptor->name, + cc_full_descr->descriptor->methods.source.msg_iter_next); + comp_class = bt_component_class_filter_as_component_class( + flt_comp_class); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + sink_comp_class = bt_component_class_sink_create( + cc_full_descr->descriptor->name, + cc_full_descr->descriptor->methods.sink.consume); + comp_class = bt_component_class_sink_as_component_class( + sink_comp_class); + break; + default: + /* + * WARN-level logging because this should not + * happen with the appropriate ABI version. If + * we're here, we know that for the reported + * version of the ABI, this component class type + * is unknown. + */ + BT_LOGW("Ignoring unknown component class type: " + "plugin-path=\"%s\", plugin-name=\"%s\", " + "comp-class-name=\"%s\", comp-class-type=%d", + spec->shared_lib_handle->path->str ? + spec->shared_lib_handle->path->str : + NULL, + descriptor->name, + cc_full_descr->descriptor->name, + cc_full_descr->descriptor->type); + continue; + } + + if (!comp_class) { + BT_LOGE_STR("Cannot create component class."); + status = BT_PLUGIN_STATUS_ERROR; + goto end; + } + + if (cc_full_descr->description) { + ret = bt_component_class_set_description( + comp_class, cc_full_descr->description); + if (ret) { + BT_LOGE_STR("Cannot set component class's description."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(comp_class); + goto end; + } + } + + if (cc_full_descr->help) { + ret = bt_component_class_set_help(comp_class, + cc_full_descr->help); + if (ret) { + BT_LOGE_STR("Cannot set component class's help string."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(comp_class); + goto end; + } + } + + switch (cc_full_descr->descriptor->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + if (cc_full_descr->methods.source.init) { + ret = bt_component_class_source_set_init_method( + src_comp_class, + cc_full_descr->methods.source.init); + if (ret) { + BT_LOGE_STR("Cannot set source component class's initialization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.finalize) { + ret = bt_component_class_source_set_finalize_method( + src_comp_class, + cc_full_descr->methods.source.finalize); + if (ret) { + BT_LOGE_STR("Cannot set source component class's finalization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.query) { + ret = bt_component_class_source_set_query_method( + src_comp_class, + cc_full_descr->methods.source.query); + if (ret) { + BT_LOGE_STR("Cannot set source component class's query method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.accept_output_port_connection) { + ret = bt_component_class_source_set_accept_output_port_connection_method( + src_comp_class, + cc_full_descr->methods.source.accept_output_port_connection); + if (ret) { + BT_LOGE_STR("Cannot set source component class's \"accept input output connection\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.output_port_connected) { + ret = bt_component_class_source_set_output_port_connected_method( + src_comp_class, + cc_full_descr->methods.source.output_port_connected); + if (ret) { + BT_LOGE_STR("Cannot set source component class's \"output port connected\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_init) { + ret = bt_component_class_source_set_message_iterator_init_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_init); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator initialization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_finalize) { + ret = bt_component_class_source_set_message_iterator_finalize_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_finalize); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator finalization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_seek_ns_from_origin) { + ret = bt_component_class_source_set_message_iterator_seek_ns_from_origin_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_seek_ns_from_origin); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator \"seek nanoseconds from origin\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_seek_beginning) { + ret = bt_component_class_source_set_message_iterator_seek_beginning_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_seek_beginning); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator \"seek beginning\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin) { + ret = bt_component_class_source_set_message_iterator_can_seek_ns_from_origin_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_can_seek_ns_from_origin); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator \"can seek nanoseconds from origin\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.source.msg_iter_can_seek_beginning) { + ret = bt_component_class_source_set_message_iterator_can_seek_beginning_method( + src_comp_class, + cc_full_descr->methods.source.msg_iter_can_seek_beginning); + if (ret) { + BT_LOGE_STR("Cannot set source component class's message iterator \"can seek beginning\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(src_comp_class); + goto end; + } + } + + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + if (cc_full_descr->methods.filter.init) { + ret = bt_component_class_filter_set_init_method( + flt_comp_class, + cc_full_descr->methods.filter.init); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's initialization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.finalize) { + ret = bt_component_class_filter_set_finalize_method( + flt_comp_class, + cc_full_descr->methods.filter.finalize); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's finalization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.query) { + ret = bt_component_class_filter_set_query_method( + flt_comp_class, + cc_full_descr->methods.filter.query); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's query method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.accept_input_port_connection) { + ret = bt_component_class_filter_set_accept_input_port_connection_method( + flt_comp_class, + cc_full_descr->methods.filter.accept_input_port_connection); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's \"accept input port connection\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.accept_output_port_connection) { + ret = bt_component_class_filter_set_accept_output_port_connection_method( + flt_comp_class, + cc_full_descr->methods.filter.accept_output_port_connection); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's \"accept input output connection\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.input_port_connected) { + ret = bt_component_class_filter_set_input_port_connected_method( + flt_comp_class, + cc_full_descr->methods.filter.input_port_connected); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's \"input port connected\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.output_port_connected) { + ret = bt_component_class_filter_set_output_port_connected_method( + flt_comp_class, + cc_full_descr->methods.filter.output_port_connected); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's \"output port connected\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_init) { + ret = bt_component_class_filter_set_message_iterator_init_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_init); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator initialization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_finalize) { + ret = bt_component_class_filter_set_message_iterator_finalize_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_finalize); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator finalization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin) { + ret = bt_component_class_filter_set_message_iterator_seek_ns_from_origin_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_seek_ns_from_origin); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator \"seek nanoseconds from origin\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_seek_beginning) { + ret = bt_component_class_filter_set_message_iterator_seek_beginning_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_seek_beginning); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator \"seek beginning\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin) { + ret = bt_component_class_filter_set_message_iterator_can_seek_ns_from_origin_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_can_seek_ns_from_origin); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek nanoseconds from origin\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.filter.msg_iter_can_seek_beginning) { + ret = bt_component_class_filter_set_message_iterator_can_seek_beginning_method( + flt_comp_class, + cc_full_descr->methods.filter.msg_iter_can_seek_beginning); + if (ret) { + BT_LOGE_STR("Cannot set filter component class's message iterator \"can seek beginning\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(flt_comp_class); + goto end; + } + } + + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + if (cc_full_descr->methods.sink.init) { + ret = bt_component_class_sink_set_init_method( + sink_comp_class, + cc_full_descr->methods.sink.init); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's initialization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.sink.finalize) { + ret = bt_component_class_sink_set_finalize_method( + sink_comp_class, + cc_full_descr->methods.sink.finalize); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's finalization method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.sink.query) { + ret = bt_component_class_sink_set_query_method( + sink_comp_class, + cc_full_descr->methods.sink.query); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's query method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.sink.accept_input_port_connection) { + ret = bt_component_class_sink_set_accept_input_port_connection_method( + sink_comp_class, + cc_full_descr->methods.sink.accept_input_port_connection); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's \"accept input port connection\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.sink.input_port_connected) { + ret = bt_component_class_sink_set_input_port_connected_method( + sink_comp_class, + cc_full_descr->methods.sink.input_port_connected); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's \"input port connected\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + if (cc_full_descr->methods.sink.graph_is_configured) { + ret = bt_component_class_sink_set_graph_is_configured_method( + sink_comp_class, + cc_full_descr->methods.sink.graph_is_configured); + if (ret) { + BT_LOGE_STR("Cannot set sink component class's \"graph is configured\" method."); + status = BT_PLUGIN_STATUS_ERROR; + BT_OBJECT_PUT_REF_AND_RESET(sink_comp_class); + goto end; + } + } + + break; + default: + abort(); + } + + /* + * Add component class to the plugin object. + * + * This will call back + * bt_plugin_so_on_add_component_class() so that we can + * add a mapping in the component class list when we + * know the component class is successfully added. + */ + status = bt_plugin_add_component_class(plugin, + (void *) comp_class); + BT_OBJECT_PUT_REF_AND_RESET(comp_class); + if (status < 0) { + BT_LOGE("Cannot add component class to plugin."); + goto end; + } + } + +end: + g_array_free(comp_class_full_descriptors, TRUE); + return status; +} + +static +struct bt_plugin *bt_plugin_so_create_empty( + struct bt_plugin_so_shared_lib_handle *shared_lib_handle) +{ + struct bt_plugin *plugin; + struct bt_plugin_so_spec_data *spec; + + plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_SO); + if (!plugin) { + goto error; + } + + plugin->destroy_spec_data = bt_plugin_so_destroy_spec_data; + plugin->spec_data = g_new0(struct bt_plugin_so_spec_data, 1); + if (!plugin->spec_data) { + BT_LOGE_STR("Failed to allocate one SO plugin specific data structure."); + goto error; + } + + spec = plugin->spec_data; + spec->shared_lib_handle = shared_lib_handle; + bt_object_get_no_null_check(spec->shared_lib_handle); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(plugin); + +end: + return plugin; +} + +static +size_t count_non_null_items_in_section(const void *begin, const void *end) +{ + size_t count = 0; + const int * const *begin_int = (const int * const *) begin; + const int * const *end_int = (const int * const *) end; + const int * const *iter; + + for (iter = begin_int; iter != end_int; iter++) { + if (*iter) { + count++; + } + } + + return count; +} + +static +struct bt_plugin_set *bt_plugin_so_create_all_from_sections( + struct bt_plugin_so_shared_lib_handle *shared_lib_handle, + struct __bt_plugin_descriptor const * const *descriptors_begin, + struct __bt_plugin_descriptor const * const *descriptors_end, + struct __bt_plugin_descriptor_attribute const * const *attrs_begin, + struct __bt_plugin_descriptor_attribute const * const *attrs_end, + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin, + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end, + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin, + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end) +{ + size_t descriptor_count; + size_t attrs_count; + size_t cc_descriptors_count; + size_t cc_descr_attrs_count; + size_t i; + struct bt_plugin_set *plugin_set = NULL; + + descriptor_count = count_non_null_items_in_section(descriptors_begin, descriptors_end); + attrs_count = count_non_null_items_in_section(attrs_begin, attrs_end); + cc_descriptors_count = count_non_null_items_in_section(cc_descriptors_begin, cc_descriptors_end); + cc_descr_attrs_count = count_non_null_items_in_section(cc_descr_attrs_begin, cc_descr_attrs_end); + + BT_LOGD("Creating all SO plugins from sections: " + "plugin-path=\"%s\", " + "descr-begin-addr=%p, descr-end-addr=%p, " + "attrs-begin-addr=%p, attrs-end-addr=%p, " + "cc-descr-begin-addr=%p, cc-descr-end-addr=%p, " + "cc-descr-attrs-begin-addr=%p, cc-descr-attrs-end-addr=%p, " + "descr-count=%zu, attrs-count=%zu, " + "cc-descr-count=%zu, cc-descr-attrs-count=%zu", + shared_lib_handle->path ? shared_lib_handle->path->str : NULL, + descriptors_begin, descriptors_end, + attrs_begin, attrs_end, + cc_descriptors_begin, cc_descriptors_end, + cc_descr_attrs_begin, cc_descr_attrs_end, + descriptor_count, attrs_count, + cc_descriptors_count, cc_descr_attrs_count); + plugin_set = bt_plugin_set_create(); + if (!plugin_set) { + BT_LOGE_STR("Cannot create empty plugin set."); + goto error; + } + + for (i = 0; i < descriptors_end - descriptors_begin; i++) { + enum bt_plugin_status status; + const struct __bt_plugin_descriptor *descriptor = + descriptors_begin[i]; + struct bt_plugin *plugin; + + if (descriptor == NULL) { + continue; + } + + BT_LOGD("Creating plugin object for plugin: " + "name=\"%s\", abi-major=%d, abi-minor=%d", + descriptor->name, descriptor->major, descriptor->minor); + + if (descriptor->major > __BT_PLUGIN_VERSION_MAJOR) { + /* + * DEBUG-level logging because we're only + * _trying_ to open this file as a compatible + * Babeltrace plugin: if it's not, it's not an + * error. And because this can be tried during + * bt_plugin_find_all_from_dir(), it's not + * even a warning. + */ + BT_LOGD("Unknown ABI major version: abi-major=%d", + descriptor->major); + goto error; + } + + plugin = bt_plugin_so_create_empty(shared_lib_handle); + if (!plugin) { + BT_LOGE_STR("Cannot create empty shared library handle."); + goto error; + } + + if (shared_lib_handle && shared_lib_handle->path) { + bt_plugin_set_path(plugin, shared_lib_handle->path->str); + } + + status = bt_plugin_so_init(plugin, descriptor, attrs_begin, + attrs_end, cc_descriptors_begin, cc_descriptors_end, + cc_descr_attrs_begin, cc_descr_attrs_end); + if (status < 0) { + /* + * DEBUG-level logging because we're only + * _trying_ to open this file as a compatible + * Babeltrace plugin: if it's not, it's not an + * error. And because this can be tried during + * bt_plugin_find_all_from_dir(), it's not + * even a warning. + */ + BT_LOGD_STR("Cannot initialize SO plugin object from sections."); + BT_OBJECT_PUT_REF_AND_RESET(plugin); + goto error; + } + + /* Add to plugin set */ + bt_plugin_set_add_plugin(plugin_set, plugin); + bt_object_put_ref(plugin); + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(plugin_set); + +end: + return plugin_set; +} + +BT_HIDDEN +struct bt_plugin_set *bt_plugin_so_create_all_from_static(void) +{ + struct bt_plugin_set *plugin_set = NULL; + struct bt_plugin_so_shared_lib_handle *shared_lib_handle = + bt_plugin_so_shared_lib_handle_create(NULL); + + if (!shared_lib_handle) { + goto end; + } + + BT_LOGD_STR("Creating all SO plugins from built-in plugins."); + plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle, + __bt_get_begin_section_plugin_descriptors(), + __bt_get_end_section_plugin_descriptors(), + __bt_get_begin_section_plugin_descriptor_attributes(), + __bt_get_end_section_plugin_descriptor_attributes(), + __bt_get_begin_section_component_class_descriptors(), + __bt_get_end_section_component_class_descriptors(), + __bt_get_begin_section_component_class_descriptor_attributes(), + __bt_get_end_section_component_class_descriptor_attributes()); + +end: + BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); + + return plugin_set; +} + +BT_HIDDEN +struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path) +{ + size_t path_len; + struct bt_plugin_set *plugin_set = NULL; + struct __bt_plugin_descriptor const * const *descriptors_begin = NULL; + struct __bt_plugin_descriptor const * const *descriptors_end = NULL; + struct __bt_plugin_descriptor_attribute const * const *attrs_begin = NULL; + struct __bt_plugin_descriptor_attribute const * const *attrs_end = NULL; + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_begin = NULL; + struct __bt_plugin_component_class_descriptor const * const *cc_descriptors_end = NULL; + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_begin = NULL; + struct __bt_plugin_component_class_descriptor_attribute const * const *cc_descr_attrs_end = NULL; + struct __bt_plugin_descriptor const * const *(*get_begin_section_plugin_descriptors)(void); + struct __bt_plugin_descriptor const * const *(*get_end_section_plugin_descriptors)(void); + struct __bt_plugin_descriptor_attribute const * const *(*get_begin_section_plugin_descriptor_attributes)(void); + struct __bt_plugin_descriptor_attribute const * const *(*get_end_section_plugin_descriptor_attributes)(void); + struct __bt_plugin_component_class_descriptor const * const *(*get_begin_section_component_class_descriptors)(void); + struct __bt_plugin_component_class_descriptor const * const *(*get_end_section_component_class_descriptors)(void); + struct __bt_plugin_component_class_descriptor_attribute const * const *(*get_begin_section_component_class_descriptor_attributes)(void); + struct __bt_plugin_component_class_descriptor_attribute const * const *(*get_end_section_component_class_descriptor_attributes)(void); + bt_bool is_libtool_wrapper = BT_FALSE, is_shared_object = BT_FALSE; + struct bt_plugin_so_shared_lib_handle *shared_lib_handle = NULL; + + if (!path) { + BT_LOGW_STR("Invalid parameter: path is NULL."); + goto end; + } + + BT_LOGD("Creating all SO plugins from file: path=\"%s\"", path); + path_len = strlen(path); + if (path_len <= PLUGIN_SUFFIX_LEN) { + BT_LOGW("Invalid parameter: path length is too short: " + "path-length=%zu", path_len); + goto end; + } + + path_len++; + /* + * Check if the file ends with a known plugin file type suffix (i.e. .so + * or .la on Linux). + */ + is_libtool_wrapper = !strncmp(LIBTOOL_PLUGIN_SUFFIX, + path + path_len - LIBTOOL_PLUGIN_SUFFIX_LEN, + LIBTOOL_PLUGIN_SUFFIX_LEN); + is_shared_object = !strncmp(NATIVE_PLUGIN_SUFFIX, + path + path_len - NATIVE_PLUGIN_SUFFIX_LEN, + NATIVE_PLUGIN_SUFFIX_LEN); + if (!is_shared_object && !is_libtool_wrapper) { + /* Name indicates this is not a plugin file; not an error */ + BT_LOGV("File is not a SO plugin file: path=\"%s\"", path); + goto end; + } + + shared_lib_handle = bt_plugin_so_shared_lib_handle_create(path); + if (!shared_lib_handle) { + BT_LOGD_STR("Cannot create shared library handle."); + goto end; + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_plugin_descriptors", + (gpointer *) &get_begin_section_plugin_descriptors)) { + descriptors_begin = get_begin_section_plugin_descriptors(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_begin_section_plugin_descriptors"); + goto end; + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_plugin_descriptors", + (gpointer *) &get_end_section_plugin_descriptors)) { + descriptors_end = get_end_section_plugin_descriptors(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_end_section_plugin_descriptors"); + goto end; + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_plugin_descriptor_attributes", + (gpointer *) &get_begin_section_plugin_descriptor_attributes)) { + attrs_begin = get_begin_section_plugin_descriptor_attributes(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_begin_section_plugin_descriptor_attributes"); + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_plugin_descriptor_attributes", + (gpointer *) &get_end_section_plugin_descriptor_attributes)) { + attrs_end = get_end_section_plugin_descriptor_attributes(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_end_section_plugin_descriptor_attributes"); + } + + if ((!!attrs_begin - !!attrs_end) != 0) { + BT_LOGD("Found section start or end symbol, but not both: " + "path=\"%s\", symbol-start=\"%s\", " + "symbol-end=\"%s\", symbol-start-addr=%p, " + "symbol-end-addr=%p", + path, "__bt_get_begin_section_plugin_descriptor_attributes", + "__bt_get_end_section_plugin_descriptor_attributes", + attrs_begin, attrs_end); + goto end; + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_component_class_descriptors", + (gpointer *) &get_begin_section_component_class_descriptors)) { + cc_descriptors_begin = get_begin_section_component_class_descriptors(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_begin_section_component_class_descriptors"); + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_component_class_descriptors", + (gpointer *) &get_end_section_component_class_descriptors)) { + cc_descriptors_end = get_end_section_component_class_descriptors(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_end_section_component_class_descriptors"); + } + + if ((!!cc_descriptors_begin - !!cc_descriptors_end) != 0) { + BT_LOGD("Found section start or end symbol, but not both: " + "path=\"%s\", symbol-start=\"%s\", " + "symbol-end=\"%s\", symbol-start-addr=%p, " + "symbol-end-addr=%p", + path, "__bt_get_begin_section_component_class_descriptors", + "__bt_get_end_section_component_class_descriptors", + cc_descriptors_begin, cc_descriptors_end); + goto end; + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_begin_section_component_class_descriptor_attributes", + (gpointer *) &get_begin_section_component_class_descriptor_attributes)) { + cc_descr_attrs_begin = get_begin_section_component_class_descriptor_attributes(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_begin_section_component_class_descriptor_attributes"); + } + + if (g_module_symbol(shared_lib_handle->module, "__bt_get_end_section_component_class_descriptor_attributes", + (gpointer *) &get_end_section_component_class_descriptor_attributes)) { + cc_descr_attrs_end = get_end_section_component_class_descriptor_attributes(); + } else { + BT_LOGD("Cannot resolve plugin symbol: path=\"%s\", " + "symbol=\"%s\"", path, + "__bt_get_end_section_component_class_descriptor_attributes"); + } + + if ((!!cc_descr_attrs_begin - !!cc_descr_attrs_end) != 0) { + BT_LOGD("Found section start or end symbol, but not both: " + "path=\"%s\", symbol-start=\"%s\", " + "symbol-end=\"%s\", symbol-start-addr=%p, " + "symbol-end-addr=%p", + path, "__bt_get_begin_section_component_class_descriptor_attributes", + "__bt_get_end_section_component_class_descriptor_attributes", + cc_descr_attrs_begin, cc_descr_attrs_end); + goto end; + } + + /* Initialize plugin */ + BT_LOGD_STR("Initializing plugin object."); + plugin_set = bt_plugin_so_create_all_from_sections(shared_lib_handle, + descriptors_begin, descriptors_end, attrs_begin, attrs_end, + cc_descriptors_begin, cc_descriptors_end, + cc_descr_attrs_begin, cc_descr_attrs_end); + +end: + BT_OBJECT_PUT_REF_AND_RESET(shared_lib_handle); + return plugin_set; +} + +static +void plugin_comp_class_destroy_listener(struct bt_component_class *comp_class, + void *data) +{ + bt_list_del(&comp_class->node); + BT_OBJECT_PUT_REF_AND_RESET(comp_class->so_handle); + BT_LOGV("Component class destroyed: removed entry from list: " + "comp-cls-addr=%p", comp_class); +} + +void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin, + struct bt_component_class *comp_class) +{ + struct bt_plugin_so_spec_data *spec = plugin->spec_data; + + BT_ASSERT(plugin->spec_data); + BT_ASSERT(plugin->type == BT_PLUGIN_TYPE_SO); + + bt_list_add(&comp_class->node, &component_class_list); + comp_class->so_handle = spec->shared_lib_handle; + bt_object_get_no_null_check(comp_class->so_handle); + + /* Add our custom destroy listener */ + bt_component_class_add_destroy_listener(comp_class, + plugin_comp_class_destroy_listener, NULL); +} diff --git a/src/lib/plugin/plugin-so.h b/src/lib/plugin/plugin-so.h new file mode 100644 index 00000000..094b87ad --- /dev/null +++ b/src/lib/plugin/plugin-so.h @@ -0,0 +1,63 @@ +#ifndef BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H +#define BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +struct bt_plugin; +struct bt_component_class; + +struct bt_plugin_so_shared_lib_handle { + struct bt_object base; + GString *path; + GModule *module; + + /* True if initialization function was called */ + bt_bool init_called; + bt_plugin_exit_func exit; +}; + +struct bt_plugin_so_spec_data { + /* Shared lib. handle: owned by this */ + struct bt_plugin_so_shared_lib_handle *shared_lib_handle; + + /* Pointers to plugin's memory: do NOT free */ + const struct __bt_plugin_descriptor *descriptor; + bt_plugin_init_func init; + const struct __bt_plugin_descriptor_version *version; +}; + +BT_HIDDEN +struct bt_plugin_set *bt_plugin_so_create_all_from_file(const char *path); + +BT_HIDDEN +struct bt_plugin_set *bt_plugin_so_create_all_from_static(void); + +void bt_plugin_so_on_add_component_class(struct bt_plugin *plugin, + struct bt_component_class *comp_class); + +#endif /* BABELTRACE_PLUGIN_PLUGIN_SO_INTERNAL_H */ diff --git a/src/lib/plugin/plugin.c b/src/lib/plugin/plugin.c new file mode 100644 index 00000000..53b783ff --- /dev/null +++ b/src/lib/plugin/plugin.c @@ -0,0 +1,635 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN" +#include "lib/lib-logging.h" + +#include "common/assert.h" +#include "lib/assert-pre.h" +#include "common/babeltrace.h" +#include "compat/compiler.h" +#include "common/common.h" +#include +#include +#include "lib/graph/component-class.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "plugin-so.h" + +#define PYTHON_PLUGIN_PROVIDER_FILENAME "libbabeltrace2-python-plugin-provider." G_MODULE_SUFFIX +#define PYTHON_PLUGIN_PROVIDER_SYM_NAME bt_plugin_python_create_all_from_file +#define PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR TOSTRING(PYTHON_PLUGIN_PROVIDER_SYM_NAME) + +#define APPEND_ALL_FROM_DIR_NFDOPEN_MAX 8 + +#ifdef BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT +#include + +static +struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path) = + bt_plugin_python_create_all_from_file; + +static +void init_python_plugin_provider(void) {} +#else /* BT_BUILT_IN_PYTHON_PLUGIN_SUPPORT */ +static GModule *python_plugin_provider_module; +static +struct bt_plugin_set *(*bt_plugin_python_create_all_from_file_sym)(const char *path); + +static +void init_python_plugin_provider(void) { + if (bt_plugin_python_create_all_from_file_sym != NULL) { + return; + } + + BT_LOGD_STR("Loading Python plugin provider module."); + python_plugin_provider_module = + g_module_open(PYTHON_PLUGIN_PROVIDER_FILENAME, 0); + if (!python_plugin_provider_module) { + BT_LOGI("Cannot open `%s`: %s: continuing without Python plugin support.", + PYTHON_PLUGIN_PROVIDER_FILENAME, g_module_error()); + return; + } + + if (!g_module_symbol(python_plugin_provider_module, + PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR, + (gpointer) &bt_plugin_python_create_all_from_file_sym)) { + BT_LOGI("Cannot find the Python plugin provider loading symbol: continuing without Python plugin support: " + "file=\"%s\", symbol=\"%s\"", + PYTHON_PLUGIN_PROVIDER_FILENAME, + PYTHON_PLUGIN_PROVIDER_SYM_NAME_STR); + return; + } + + BT_LOGI("Loaded Python plugin provider module: addr=%p", + python_plugin_provider_module); +} + +__attribute__((destructor)) static +void fini_python_plugin_provider(void) { + if (python_plugin_provider_module) { + BT_LOGD("Unloading Python plugin provider module."); + + if (!g_module_close(python_plugin_provider_module)) { + BT_LOGE("Failed to close the Python plugin provider module: %s.", + g_module_error()); + } + + python_plugin_provider_module = NULL; + } +} +#endif + +uint64_t bt_plugin_set_get_plugin_count(struct bt_plugin_set *plugin_set) +{ + BT_ASSERT_PRE_NON_NULL(plugin_set, "Plugin set"); + return (uint64_t) plugin_set->plugins->len; +} + +const struct bt_plugin *bt_plugin_set_borrow_plugin_by_index_const( + const struct bt_plugin_set *plugin_set, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(plugin_set, "Plugin set"); + BT_ASSERT_PRE_VALID_INDEX(index, plugin_set->plugins->len); + return g_ptr_array_index(plugin_set->plugins, index); +} + +const struct bt_plugin_set *bt_plugin_find_all_from_static(void) +{ + /* bt_plugin_so_create_all_from_static() logs errors */ + return bt_plugin_so_create_all_from_static(); +} + +const struct bt_plugin_set *bt_plugin_find_all_from_file(const char *path) +{ + struct bt_plugin_set *plugin_set = NULL; + + BT_ASSERT_PRE_NON_NULL(path, "Path"); + BT_LOGD("Creating plugins from file: path=\"%s\"", path); + + /* Try shared object plugins */ + plugin_set = bt_plugin_so_create_all_from_file(path); + if (plugin_set) { + goto end; + } + + /* Try Python plugins if support is available */ + init_python_plugin_provider(); + if (bt_plugin_python_create_all_from_file_sym) { + plugin_set = bt_plugin_python_create_all_from_file_sym(path); + if (plugin_set) { + goto end; + } + } + +end: + if (plugin_set) { + BT_LOGD("Created %u plugins from file: " + "path=\"%s\", count=%u, plugin-set-addr=%p", + plugin_set->plugins->len, path, + plugin_set->plugins->len, plugin_set); + } else { + BT_LOGD("Found no plugins in file: path=\"%s\"", path); + } + + return plugin_set; +} + +static void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + +const struct bt_plugin *bt_plugin_find(const char *plugin_name) +{ + const char *system_plugin_dir; + char *home_plugin_dir = NULL; + const char *envvar; + const struct bt_plugin *plugin = NULL; + const struct bt_plugin_set *plugin_set = NULL; + GPtrArray *dirs = NULL; + int ret; + size_t i, j; + + BT_ASSERT_PRE_NON_NULL(plugin_name, "Name"); + BT_LOGD("Finding named plugin in standard directories and built-in plugins: " + "name=\"%s\"", plugin_name); + dirs = g_ptr_array_new_with_free_func((GDestroyNotify) destroy_gstring); + if (!dirs) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto end; + } + + /* + * Search order is: + * + * 1. BABELTRACE_PLUGIN_PATH environment variable + * (colon-separated list of directories) + * 2. ~/.local/lib/babeltrace2/plugins + * 3. Default system directory for Babeltrace plugins, usually + * /usr/lib/babeltrace2/plugins or + * /usr/local/lib/babeltrace2/plugins if installed + * locally + * 4. Built-in plugins (static) + * + * Directories are searched non-recursively. + */ + envvar = getenv("BABELTRACE_PLUGIN_PATH"); + if (envvar) { + ret = bt_common_append_plugin_path_dirs(envvar, dirs); + if (ret) { + BT_LOGE_STR("Failed to append plugin path to array of directories."); + goto end; + } + } + + home_plugin_dir = bt_common_get_home_plugin_path(); + if (home_plugin_dir) { + GString *home_plugin_dir_str = + g_string_new(home_plugin_dir); + + if (!home_plugin_dir_str) { + BT_LOGE_STR("Failed to allocate a GString."); + goto end; + } + + g_ptr_array_add(dirs, home_plugin_dir_str); + } + + system_plugin_dir = bt_common_get_system_plugin_path(); + if (system_plugin_dir) { + GString *system_plugin_dir_str = + g_string_new(system_plugin_dir); + + if (!system_plugin_dir_str) { + BT_LOGE_STR("Failed to allocate a GString."); + goto end; + } + + g_ptr_array_add(dirs, system_plugin_dir_str); + } + + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); + + BT_OBJECT_PUT_REF_AND_RESET(plugin_set); + + /* + * Skip this if the directory does not exist because + * bt_plugin_find_all_from_dir() would log a warning. + */ + if (!g_file_test(dir->str, G_FILE_TEST_IS_DIR)) { + BT_LOGV("Skipping nonexistent directory path: " + "path=\"%s\"", dir->str); + continue; + } + + /* bt_plugin_find_all_from_dir() logs details/errors */ + plugin_set = bt_plugin_find_all_from_dir(dir->str, BT_FALSE); + if (!plugin_set) { + BT_LOGD("No plugins found in directory: path=\"%s\"", + dir->str); + continue; + } + + for (j = 0; j < plugin_set->plugins->len; j++) { + const struct bt_plugin *candidate_plugin = + g_ptr_array_index(plugin_set->plugins, j); + + if (strcmp(bt_plugin_get_name(candidate_plugin), + plugin_name) == 0) { + BT_LOGD("Plugin found in directory: name=\"%s\", path=\"%s\"", + plugin_name, dir->str); + plugin = candidate_plugin; + bt_object_get_no_null_check(plugin); + goto end; + } + } + + BT_LOGD("Plugin not found in directory: name=\"%s\", path=\"%s\"", + plugin_name, dir->str); + } + + bt_object_put_ref(plugin_set); + plugin_set = bt_plugin_find_all_from_static(); + if (plugin_set) { + for (j = 0; j < plugin_set->plugins->len; j++) { + const struct bt_plugin *candidate_plugin = + g_ptr_array_index(plugin_set->plugins, j); + + if (strcmp(bt_plugin_get_name(candidate_plugin), + plugin_name) == 0) { + BT_LOGD("Plugin found in built-in plugins: " + "name=\"%s\"", plugin_name); + plugin = candidate_plugin; + bt_object_get_no_null_check(plugin); + goto end; + } + } + } + +end: + free(home_plugin_dir); + bt_object_put_ref(plugin_set); + + if (dirs) { + g_ptr_array_free(dirs, TRUE); + } + + if (plugin) { + BT_LIB_LOGD("Found plugin in standard directories and built-in plugins: " + "%!+l", plugin); + } else { + BT_LOGD("No plugin found in standard directories and built-in plugins: " + "name=\"%s\"", plugin_name); + } + + return plugin; +} + +static struct { + pthread_mutex_t lock; + struct bt_plugin_set *plugin_set; + bool recurse; +} append_all_from_dir_info = { + .lock = PTHREAD_MUTEX_INITIALIZER +}; + +static +int nftw_append_all_from_dir(const char *file, const struct stat *sb, int flag, + struct FTW *s) +{ + int ret = 0; + const char *name = file + s->base; + + /* Check for recursion */ + if (!append_all_from_dir_info.recurse && s->level > 1) { + goto end; + } + + switch (flag) { + case FTW_F: + { + const struct bt_plugin_set *plugins_from_file; + + if (name[0] == '.') { + /* Skip hidden files */ + BT_LOGV("Skipping hidden file: path=\"%s\"", file); + goto end; + } + + plugins_from_file = bt_plugin_find_all_from_file(file); + + if (plugins_from_file) { + size_t j; + + for (j = 0; j < plugins_from_file->plugins->len; j++) { + struct bt_plugin *plugin = + g_ptr_array_index(plugins_from_file->plugins, j); + + BT_LIB_LOGD("Adding plugin to plugin set: " + "plugin-path=\"%s\", %![plugin-]+l", + file, plugin); + bt_plugin_set_add_plugin( + append_all_from_dir_info.plugin_set, + plugin); + } + + bt_object_put_ref(plugins_from_file); + } + break; + } + case FTW_DNR: + /* Continue to next file / directory. */ + BT_LOGW("Cannot enter directory: continuing: path=\"%s\"", file); + break; + case FTW_NS: + /* Continue to next file / directory. */ + BT_LOGD("Cannot get file information: continuing: path=\"%s\"", file); + break; + } + +end: + return ret; +} + +static +enum bt_plugin_status bt_plugin_create_append_all_from_dir( + struct bt_plugin_set *plugin_set, const char *path, + bt_bool recurse) +{ + int nftw_flags = FTW_PHYS; + enum bt_plugin_status ret = BT_PLUGIN_STATUS_OK; + + BT_ASSERT(plugin_set); + BT_ASSERT(path); + BT_ASSERT(strlen(path) < PATH_MAX); + pthread_mutex_lock(&append_all_from_dir_info.lock); + append_all_from_dir_info.plugin_set = plugin_set; + append_all_from_dir_info.recurse = recurse; + ret = nftw(path, nftw_append_all_from_dir, + APPEND_ALL_FROM_DIR_NFDOPEN_MAX, nftw_flags); + pthread_mutex_unlock(&append_all_from_dir_info.lock); + if (ret != 0) { + BT_LOGW_ERRNO("Cannot open directory", ": path=\"%s\"", path); + ret = BT_PLUGIN_STATUS_ERROR; + } + + return ret; +} + +const struct bt_plugin_set *bt_plugin_find_all_from_dir(const char *path, + bt_bool recurse) +{ + struct bt_plugin_set *plugin_set; + enum bt_plugin_status status; + + BT_LOGD("Creating all plugins in directory: path=\"%s\", recurse=%d", + path, recurse); + plugin_set = bt_plugin_set_create(); + if (!plugin_set) { + BT_LOGE_STR("Cannot create empty plugin set."); + goto error; + } + + /* Append found plugins to array */ + status = bt_plugin_create_append_all_from_dir(plugin_set, path, + recurse); + if (status < 0) { + BT_LOGW("Cannot append plugins found in directory: " + "path=\"%s\", status=%s", + path, bt_plugin_status_string(status)); + goto error; + } + + BT_LOGD("Created %u plugins from directory: count=%u, path=\"%s\"", + plugin_set->plugins->len, plugin_set->plugins->len, path); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(plugin_set); + +end: + return plugin_set; +} + +const char *bt_plugin_get_name(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return plugin->info.name_set ? plugin->info.name->str : NULL; +} + +const char *bt_plugin_get_author(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return plugin->info.author_set ? plugin->info.author->str : NULL; +} + +const char *bt_plugin_get_license(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return plugin->info.license_set ? plugin->info.license->str : NULL; +} + +const char *bt_plugin_get_path(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return plugin->info.path_set ? plugin->info.path->str : NULL; +} + +const char *bt_plugin_get_description(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return plugin->info.description_set ? + plugin->info.description->str : NULL; +} + +enum bt_property_availability bt_plugin_get_version(const struct bt_plugin *plugin, + unsigned int *major, unsigned int *minor, unsigned int *patch, + const char **extra) +{ + enum bt_property_availability avail = + BT_PROPERTY_AVAILABILITY_AVAILABLE; + + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + + if (!plugin->info.version_set) { + BT_LIB_LOGV("Plugin's version is not set: %!+l", plugin); + avail = BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE; + goto end; + } + + if (major) { + *major = plugin->info.version.major; + } + + if (minor) { + *minor = plugin->info.version.minor; + } + + if (patch) { + *patch = plugin->info.version.patch; + } + + if (extra) { + *extra = plugin->info.version.extra->str; + } + +end: + return avail; +} + +uint64_t bt_plugin_get_source_component_class_count(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return (uint64_t) plugin->src_comp_classes->len; +} + +uint64_t bt_plugin_get_filter_component_class_count(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return (uint64_t) plugin->flt_comp_classes->len; +} + +uint64_t bt_plugin_get_sink_component_class_count(const struct bt_plugin *plugin) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + return (uint64_t) plugin->sink_comp_classes->len; +} + +static inline +struct bt_component_class *borrow_component_class_by_index( + const struct bt_plugin *plugin, GPtrArray *comp_classes, + uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + BT_ASSERT_PRE_VALID_INDEX(index, comp_classes->len); + return g_ptr_array_index(comp_classes, index); +} + +const struct bt_component_class_source * +bt_plugin_borrow_source_component_class_by_index_const( + const struct bt_plugin *plugin, uint64_t index) +{ + return (const void *) borrow_component_class_by_index(plugin, + plugin->src_comp_classes, index); +} + +const struct bt_component_class_filter * +bt_plugin_borrow_filter_component_class_by_index_const( + const struct bt_plugin *plugin, uint64_t index) +{ + return (const void *) borrow_component_class_by_index(plugin, + plugin->flt_comp_classes, index); +} + +const struct bt_component_class_sink * +bt_plugin_borrow_sink_component_class_by_index_const( + const struct bt_plugin *plugin, uint64_t index) +{ + return (const void *) borrow_component_class_by_index(plugin, + plugin->sink_comp_classes, index); +} + +static inline +struct bt_component_class *borrow_component_class_by_name( + const struct bt_plugin *plugin, GPtrArray *comp_classes, + const char *name) +{ + struct bt_component_class *comp_class = NULL; + size_t i; + + BT_ASSERT_PRE_NON_NULL(plugin, "Plugin"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + + for (i = 0; i < comp_classes->len; i++) { + struct bt_component_class *comp_class_candidate = + g_ptr_array_index(comp_classes, i); + const char *comp_class_cand_name = + bt_component_class_get_name(comp_class_candidate); + + BT_ASSERT(comp_class_cand_name); + + if (strcmp(name, comp_class_cand_name) == 0) { + comp_class = comp_class_candidate; + break; + } + } + + return comp_class; +} + +const struct bt_component_class_source * +bt_plugin_borrow_source_component_class_by_name_const( + const struct bt_plugin *plugin, const char *name) +{ + return (const void *) borrow_component_class_by_name(plugin, + plugin->src_comp_classes, name); +} + +const struct bt_component_class_filter * +bt_plugin_borrow_filter_component_class_by_name_const( + const struct bt_plugin *plugin, const char *name) +{ + return (const void *) borrow_component_class_by_name(plugin, + plugin->flt_comp_classes, name); +} + +const struct bt_component_class_sink * +bt_plugin_borrow_sink_component_class_by_name_const( + const struct bt_plugin *plugin, const char *name) +{ + return (const void *) borrow_component_class_by_name(plugin, + plugin->sink_comp_classes, name); +} + +void bt_plugin_get_ref(const struct bt_plugin *plugin) +{ + bt_object_get_ref(plugin); +} + +void bt_plugin_put_ref(const struct bt_plugin *plugin) +{ + bt_object_put_ref(plugin); +} + +void bt_plugin_set_get_ref(const struct bt_plugin_set *plugin_set) +{ + bt_object_get_ref(plugin_set); +} + +void bt_plugin_set_put_ref(const struct bt_plugin_set *plugin_set) +{ + bt_object_put_ref(plugin_set); +} diff --git a/src/lib/plugin/plugin.h b/src/lib/plugin/plugin.h new file mode 100644 index 00000000..cdd20d69 --- /dev/null +++ b/src/lib/plugin/plugin.h @@ -0,0 +1,443 @@ +#ifndef BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H +#define BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "lib/graph/component-class.h" +#include +#include +#include "lib/object.h" +#include +#include "common/assert.h" +#include "lib/lib-logging.h" +#include + +#include "plugin-so.h" + +enum bt_plugin_type { + BT_PLUGIN_TYPE_SO = 0, + BT_PLUGIN_TYPE_PYTHON = 1, +}; + +enum bt_plugin_status { + BT_PLUGIN_STATUS_OK = 0, + BT_PLUGIN_STATUS_ERROR = -1, + BT_PLUGIN_STATUS_NOMEM = -12, +}; + +struct bt_plugin { + struct bt_object base; + enum bt_plugin_type type; + + /* Arrays of `struct bt_component_class *` (owned by this) */ + GPtrArray *src_comp_classes; + GPtrArray *flt_comp_classes; + GPtrArray *sink_comp_classes; + + /* Info (owned by this) */ + struct { + GString *path; + GString *name; + GString *author; + GString *license; + GString *description; + struct { + unsigned int major; + unsigned int minor; + unsigned int patch; + GString *extra; + } version; + bool path_set; + bool name_set; + bool author_set; + bool license_set; + bool description_set; + bool version_set; + } info; + + /* Value depends on the specific plugin type */ + void *spec_data; + void (*destroy_spec_data)(struct bt_plugin *); +}; + +struct bt_plugin_set { + struct bt_object base; + + /* Array of struct bt_plugin * */ + GPtrArray *plugins; +}; + +static inline +const char *bt_plugin_status_string(enum bt_plugin_status status) +{ + switch (status) { + case BT_PLUGIN_STATUS_OK: + return "BT_PLUGIN_STATUS_OK"; + case BT_PLUGIN_STATUS_ERROR: + return "BT_PLUGIN_STATUS_ERROR"; + case BT_PLUGIN_STATUS_NOMEM: + return "BT_PLUGIN_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_plugin_type_string(enum bt_plugin_type type) +{ + switch (type) { + case BT_PLUGIN_TYPE_SO: + return "BT_PLUGIN_TYPE_SO"; + case BT_PLUGIN_TYPE_PYTHON: + return "BT_PLUGIN_TYPE_PYTHON"; + default: + return "(unknown)"; + } +} + +static inline +void bt_plugin_destroy(struct bt_object *obj) +{ + struct bt_plugin *plugin; + + BT_ASSERT(obj); + plugin = container_of(obj, struct bt_plugin, base); + BT_LIB_LOGD("Destroying plugin object: %!+l", plugin); + + if (plugin->destroy_spec_data) { + plugin->destroy_spec_data(plugin); + } + + if (plugin->src_comp_classes) { + BT_LOGD_STR("Putting source component classes."); + g_ptr_array_free(plugin->src_comp_classes, TRUE); + plugin->src_comp_classes = NULL; + } + + if (plugin->flt_comp_classes) { + BT_LOGD_STR("Putting filter component classes."); + g_ptr_array_free(plugin->flt_comp_classes, TRUE); + plugin->flt_comp_classes = NULL; + } + + if (plugin->sink_comp_classes) { + BT_LOGD_STR("Putting sink component classes."); + g_ptr_array_free(plugin->sink_comp_classes, TRUE); + plugin->sink_comp_classes = NULL; + } + + if (plugin->info.name) { + g_string_free(plugin->info.name, TRUE); + plugin->info.name = NULL; + } + + if (plugin->info.path) { + g_string_free(plugin->info.path, TRUE); + plugin->info.path = NULL; + } + + if (plugin->info.description) { + g_string_free(plugin->info.description, TRUE); + plugin->info.description = NULL; + } + + if (plugin->info.author) { + g_string_free(plugin->info.author, TRUE); + plugin->info.author = NULL; + } + + if (plugin->info.license) { + g_string_free(plugin->info.license, TRUE); + plugin->info.license = NULL; + } + + if (plugin->info.version.extra) { + g_string_free(plugin->info.version.extra, TRUE); + plugin->info.version.extra = NULL; + } + + g_free(plugin); +} + +static inline +struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type) +{ + struct bt_plugin *plugin = NULL; + + BT_LOGD("Creating empty plugin object: type=%s", + bt_plugin_type_string(type)); + + plugin = g_new0(struct bt_plugin, 1); + if (!plugin) { + BT_LOGE_STR("Failed to allocate one plugin."); + goto error; + } + + bt_object_init_shared(&plugin->base, bt_plugin_destroy); + plugin->type = type; + + /* Create empty arrays of component classes */ + plugin->src_comp_classes = + g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!plugin->src_comp_classes) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + plugin->flt_comp_classes = + g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!plugin->flt_comp_classes) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + plugin->sink_comp_classes = + g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!plugin->sink_comp_classes) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + /* Create empty info */ + plugin->info.name = g_string_new(NULL); + if (!plugin->info.name) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + plugin->info.path = g_string_new(NULL); + if (!plugin->info.path) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + plugin->info.description = g_string_new(NULL); + if (!plugin->info.description) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + plugin->info.author = g_string_new(NULL); + if (!plugin->info.author) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + plugin->info.license = g_string_new(NULL); + if (!plugin->info.license) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + plugin->info.version.extra = g_string_new(NULL); + if (!plugin->info.version.extra) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + BT_LIB_LOGD("Created empty plugin object: %!+l", plugin); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(plugin); + +end: + return plugin; +} + +static inline +void bt_plugin_set_path(struct bt_plugin *plugin, const char *path) +{ + BT_ASSERT(plugin); + BT_ASSERT(path); + g_string_assign(plugin->info.path, path); + plugin->info.path_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's path: %![plugin-]+l, path=\"%s\"", + plugin, path); +} + +static inline +void bt_plugin_set_name(struct bt_plugin *plugin, const char *name) +{ + BT_ASSERT(plugin); + BT_ASSERT(name); + g_string_assign(plugin->info.name, name); + plugin->info.name_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's name: %![plugin-]+l, name=\"%s\"", + plugin, name); +} + +static inline +void bt_plugin_set_description(struct bt_plugin *plugin, + const char *description) +{ + BT_ASSERT(plugin); + BT_ASSERT(description); + g_string_assign(plugin->info.description, description); + plugin->info.description_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's description: %![plugin-]+l", plugin); +} + +static inline +void bt_plugin_set_author(struct bt_plugin *plugin, const char *author) +{ + BT_ASSERT(plugin); + BT_ASSERT(author); + g_string_assign(plugin->info.author, author); + plugin->info.author_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's author: %![plugin-]+l, author=\"%s\"", + plugin, author); +} + +static inline +void bt_plugin_set_license(struct bt_plugin *plugin, const char *license) +{ + BT_ASSERT(plugin); + BT_ASSERT(license); + g_string_assign(plugin->info.license, license); + plugin->info.license_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's path: %![plugin-]+l, license=\"%s\"", + plugin, license); +} + +static inline +void bt_plugin_set_version(struct bt_plugin *plugin, unsigned int major, + unsigned int minor, unsigned int patch, const char *extra) +{ + BT_ASSERT(plugin); + plugin->info.version.major = major; + plugin->info.version.minor = minor; + plugin->info.version.patch = patch; + + if (extra) { + g_string_assign(plugin->info.version.extra, extra); + } + + plugin->info.version_set = BT_TRUE; + BT_LIB_LOGV("Set plugin's version: %![plugin-]+l, " + "major=%u, minor=%u, patch=%u, extra=\"%s\"", + plugin, major, minor, patch, extra); +} + +static inline +enum bt_plugin_status bt_plugin_add_component_class( + struct bt_plugin *plugin, struct bt_component_class *comp_class) +{ + GPtrArray *comp_classes; + + BT_ASSERT(plugin); + BT_ASSERT(comp_class); + + switch (comp_class->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + comp_classes = plugin->src_comp_classes; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + comp_classes = plugin->flt_comp_classes; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + comp_classes = plugin->sink_comp_classes; + break; + default: + abort(); + } + + /* Add new component class */ + bt_object_get_ref(comp_class); + g_ptr_array_add(comp_classes, comp_class); + + /* Special case for a shared object plugin */ + if (plugin->type == BT_PLUGIN_TYPE_SO) { + bt_plugin_so_on_add_component_class(plugin, comp_class); + } + + BT_LIB_LOGD("Added component class to plugin: " + "%![plugin-]+l, %![cc-]+C", plugin, comp_class); + return BT_PLUGIN_STATUS_OK; +} + +static +void bt_plugin_set_destroy(struct bt_object *obj) +{ + struct bt_plugin_set *plugin_set = + container_of(obj, struct bt_plugin_set, base); + + if (!plugin_set) { + return; + } + + BT_LOGD("Destroying plugin set: addr=%p", plugin_set); + + if (plugin_set->plugins) { + BT_LOGD_STR("Putting plugins."); + g_ptr_array_free(plugin_set->plugins, TRUE); + } + + g_free(plugin_set); +} + +static inline +struct bt_plugin_set *bt_plugin_set_create(void) +{ + struct bt_plugin_set *plugin_set = g_new0(struct bt_plugin_set, 1); + + if (!plugin_set) { + goto end; + } + + BT_LOGD_STR("Creating empty plugin set."); + bt_object_init_shared(&plugin_set->base, bt_plugin_set_destroy); + + plugin_set->plugins = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!plugin_set->plugins) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + BT_OBJECT_PUT_REF_AND_RESET(plugin_set); + goto end; + } + + BT_LOGD("Created empty plugin set: addr=%p", plugin_set); + +end: + return plugin_set; +} + +static inline +void bt_plugin_set_add_plugin(struct bt_plugin_set *plugin_set, + struct bt_plugin *plugin) +{ + BT_ASSERT(plugin_set); + BT_ASSERT(plugin); + bt_object_get_ref(plugin); + g_ptr_array_add(plugin_set->plugins, plugin); + BT_LIB_LOGV("Added plugin to plugin set: " + "plugin-set-addr=%p, %![plugin-]+l", + plugin_set, plugin); +} + +#endif /* BABELTRACE_PLUGIN_PLUGIN_INTERNAL_H */ diff --git a/src/lib/prio-heap/Makefile.am b/src/lib/prio-heap/Makefile.am new file mode 100644 index 00000000..c7a54d4d --- /dev/null +++ b/src/lib/prio-heap/Makefile.am @@ -0,0 +1,5 @@ +noinst_LTLIBRARIES = libprio-heap.la + +libprio_heap_la_SOURCES = \ + prio-heap.c \ + prio-heap.h diff --git a/src/lib/prio-heap/prio-heap.c b/src/lib/prio-heap/prio-heap.c new file mode 100644 index 00000000..29e90688 --- /dev/null +++ b/src/lib/prio-heap/prio-heap.c @@ -0,0 +1,236 @@ +/* + * Static-sized priority heap containing pointers. Based on CLRS, + * chapter 6. + * + * Copyright 2011 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include + +#include "prio-heap.h" + +#ifdef DEBUG_HEAP +void check_heap(const struct ptr_heap *heap) +{ + size_t i; + + if (!heap->len) + return; + + for (i = 1; i < heap->len; i++) + BT_ASSERT(!heap->gt(heap->ptrs[i], heap->ptrs[0])); +} +#endif + +static +size_t parent(size_t i) +{ + return (i - 1) >> 1; +} + +static +size_t left(size_t i) +{ + return (i << 1) + 1; +} + +static +size_t right(size_t i) +{ + return (i << 1) + 2; +} + +/* + * Copy of heap->ptrs pointer is invalid after heap_grow. + */ +static +int heap_grow(struct ptr_heap *heap, size_t new_len) +{ + void **new_ptrs; + + if (likely(heap->alloc_len >= new_len)) + return 0; + + heap->alloc_len = max_t(size_t, new_len, heap->alloc_len << 1); + new_ptrs = calloc(heap->alloc_len, sizeof(void *)); + if (unlikely(!new_ptrs)) + return -ENOMEM; + if (likely(heap->ptrs)) + memcpy(new_ptrs, heap->ptrs, heap->len * sizeof(void *)); + free(heap->ptrs); + heap->ptrs = new_ptrs; + return 0; +} + +static +int heap_set_len(struct ptr_heap *heap, size_t new_len) +{ + int ret; + + ret = heap_grow(heap, new_len); + if (unlikely(ret)) + return ret; + heap->len = new_len; + return 0; +} + +int bt_heap_init(struct ptr_heap *heap, size_t alloc_len, + int gt(void *a, void *b)) +{ + heap->ptrs = NULL; + heap->len = 0; + heap->alloc_len = 0; + heap->gt = gt; + /* + * Minimum size allocated is 1 entry to ensure memory allocation + * never fails within bt_heap_replace_max. + */ + return heap_grow(heap, max_t(size_t, 1, alloc_len)); +} + +void bt_heap_free(struct ptr_heap *heap) +{ + free(heap->ptrs); +} + +static void heapify(struct ptr_heap *heap, size_t i) +{ + void **ptrs = heap->ptrs; + size_t l, r, largest; + + for (;;) { + void *tmp; + + l = left(i); + r = right(i); + if (l < heap->len && heap->gt(ptrs[l], ptrs[i])) + largest = l; + else + largest = i; + if (r < heap->len && heap->gt(ptrs[r], ptrs[largest])) + largest = r; + if (unlikely(largest == i)) + break; + tmp = ptrs[i]; + ptrs[i] = ptrs[largest]; + ptrs[largest] = tmp; + i = largest; + } + check_heap(heap); +} + +void *bt_heap_replace_max(struct ptr_heap *heap, void *p) +{ + void *res; + + if (unlikely(!heap->len)) { + (void) heap_set_len(heap, 1); + heap->ptrs[0] = p; + check_heap(heap); + return NULL; + } + + /* Replace the current max and heapify */ + res = heap->ptrs[0]; + heap->ptrs[0] = p; + heapify(heap, 0); + return res; +} + +int bt_heap_insert(struct ptr_heap *heap, void *p) +{ + void **ptrs; + size_t pos; + int ret; + + ret = heap_set_len(heap, heap->len + 1); + if (unlikely(ret)) + return ret; + ptrs = heap->ptrs; + pos = heap->len - 1; + while (pos > 0 && heap->gt(p, ptrs[parent(pos)])) { + /* Move parent down until we find the right spot */ + ptrs[pos] = ptrs[parent(pos)]; + pos = parent(pos); + } + ptrs[pos] = p; + check_heap(heap); + return 0; +} + +void *bt_heap_remove(struct ptr_heap *heap) +{ + switch (heap->len) { + case 0: + return NULL; + case 1: + (void) heap_set_len(heap, 0); + return heap->ptrs[0]; + } + /* Shrink, replace the current max by previous last entry and heapify */ + heap_set_len(heap, heap->len - 1); + /* len changed. previous last entry is at heap->len */ + return bt_heap_replace_max(heap, heap->ptrs[heap->len]); +} + +void *bt_heap_cherrypick(struct ptr_heap *heap, void *p) +{ + size_t pos, len = heap->len; + + for (pos = 0; pos < len; pos++) + if (unlikely(heap->ptrs[pos] == p)) + goto found; + return NULL; +found: + if (unlikely(heap->len == 1)) { + (void) heap_set_len(heap, 0); + check_heap(heap); + return heap->ptrs[0]; + } + /* Replace p with previous last entry and heapify. */ + heap_set_len(heap, heap->len - 1); + /* len changed. previous last entry is at heap->len */ + heap->ptrs[pos] = heap->ptrs[heap->len]; + heapify(heap, pos); + return p; +} + +int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src) +{ + int ret; + + ret = bt_heap_init(dst, src->alloc_len, src->gt); + if (ret < 0) + goto end; + + ret = heap_set_len(dst, src->len); + if (ret < 0) + goto end; + + memcpy(dst->ptrs, src->ptrs, src->len * sizeof(void *)); + +end: + return ret; +} diff --git a/src/lib/prio-heap/prio-heap.h b/src/lib/prio-heap/prio-heap.h new file mode 100644 index 00000000..8ea51bc1 --- /dev/null +++ b/src/lib/prio-heap/prio-heap.h @@ -0,0 +1,132 @@ +#ifndef _BABELTRACE_PRIO_HEAP_H +#define _BABELTRACE_PRIO_HEAP_H + +/* + * Static-sized priority heap containing pointers. Based on CLRS, + * chapter 6. + * + * Copyright 2011 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +struct ptr_heap { + size_t len, alloc_len; + void **ptrs; + int (*gt)(void *a, void *b); +}; + +#ifdef DEBUG_HEAP +void check_heap(const struct ptr_heap *heap); +#else +static inline +void check_heap(const struct ptr_heap *heap) +{ +} +#endif + +/** + * bt_heap_maximum - return the largest element in the heap + * @heap: the heap to be operated on + * + * Returns the largest element in the heap, without performing any modification + * to the heap structure. Returns NULL if the heap is empty. + */ +static inline void *bt_heap_maximum(const struct ptr_heap *heap) +{ + check_heap(heap); + return likely(heap->len) ? heap->ptrs[0] : NULL; +} + +/** + * bt_heap_init - initialize the heap + * @heap: the heap to initialize + * @alloc_len: number of elements initially allocated + * @gt: function to compare the elements + * + * Returns -ENOMEM if out of memory. + */ +extern int bt_heap_init(struct ptr_heap *heap, + size_t alloc_len, + int gt(void *a, void *b)); + +/** + * bt_heap_free - free the heap + * @heap: the heap to free + */ +extern void bt_heap_free(struct ptr_heap *heap); + +/** + * bt_heap_insert - insert an element into the heap + * @heap: the heap to be operated on + * @p: the element to add + * + * Insert an element into the heap. + * + * Returns -ENOMEM if out of memory. + */ +extern int bt_heap_insert(struct ptr_heap *heap, void *p); + +/** + * bt_heap_remove - remove the largest element from the heap + * @heap: the heap to be operated on + * + * Returns the largest element in the heap. It removes this element from the + * heap. Returns NULL if the heap is empty. + */ +extern void *bt_heap_remove(struct ptr_heap *heap); + +/** + * bt_heap_cherrypick - remove a given element from the heap + * @heap: the heap to be operated on + * @p: the element + * + * Remove the given element from the heap. Return the element if present, else + * return NULL. This algorithm has a complexity of O(n), which is higher than + * O(log(n)) provided by the rest of this API. + */ +extern void *bt_heap_cherrypick(struct ptr_heap *heap, void *p); + +/** + * bt_heap_replace_max - replace the the largest element from the heap + * @heap: the heap to be operated on + * @p: the pointer to be inserted as topmost element replacement + * + * Returns the largest element in the heap. It removes this element from the + * heap. The heap is rebalanced only once after the insertion. Returns NULL if + * the heap is empty. + * + * This is the equivalent of calling bt_heap_remove() and then bt_heap_insert(), but + * it only rebalances the heap once. It never allocates memory. + */ +extern void *bt_heap_replace_max(struct ptr_heap *heap, void *p); + +/** + * bt_heap_copy - copy a heap + * @dst: the destination heap (must be allocated) + * @src: the source heap + * + * Returns -ENOMEM if out of memory. + */ +extern int bt_heap_copy(struct ptr_heap *dst, struct ptr_heap *src); + +#endif /* _BABELTRACE_PRIO_HEAP_H */ diff --git a/src/lib/property.h b/src/lib/property.h new file mode 100644 index 00000000..9bc3f482 --- /dev/null +++ b/src/lib/property.h @@ -0,0 +1,60 @@ +#ifndef BABELTRACE_PROPERTY_INTERNAL_H +#define BABELTRACE_PROPERTY_INTERNAL_H + +/* + * Copyright (c) 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/assert.h" +#include +#include +#include +#include +#include +#include + +struct bt_property { + enum bt_property_availability avail; +}; + +struct bt_property_uint { + struct bt_property base; + uint64_t value; +}; + +static inline +void bt_property_uint_set(struct bt_property_uint *prop, uint64_t value) +{ + BT_ASSERT(prop); + prop->base.avail = BT_PROPERTY_AVAILABILITY_AVAILABLE; + prop->value = value; +} + +static inline +void bt_property_uint_init(struct bt_property_uint *prop, + enum bt_property_availability avail, uint64_t value) +{ + BT_ASSERT(prop); + prop->base.avail = avail; + prop->value = value; +} + +#endif /* BABELTRACE_PROPERTY_INTERNAL_H */ diff --git a/src/lib/trace-ir/Makefile.am b/src/lib/trace-ir/Makefile.am new file mode 100644 index 00000000..159f4b4a --- /dev/null +++ b/src/lib/trace-ir/Makefile.am @@ -0,0 +1,39 @@ +noinst_LTLIBRARIES = libtrace-ir.la + +libtrace_ir_la_SOURCES = \ + attributes.c \ + attributes.h \ + clock-class.c \ + clock-class.h \ + clock-snapshot.c \ + clock-snapshot.h \ + clock-snapshot-set.h \ + event.c \ + event-class.c \ + event-class.h \ + event.h \ + field.c \ + field-class.c \ + field-class.h \ + field.h \ + field-path.c \ + field-path.h \ + field-wrapper.c \ + field-wrapper.h \ + packet.c \ + packet-context-field.c \ + packet.h \ + resolve-field-path.c \ + resolve-field-path.h \ + stream.c \ + stream-class.c \ + stream-class.h \ + stream.h \ + trace.c \ + trace-class.c \ + trace-class.h \ + trace.h \ + utils.c \ + utils.h + +libtrace_ir_la_LIBADD = $(UUID_LIBS) diff --git a/src/lib/trace-ir/attributes.c b/src/lib/trace-ir/attributes.c new file mode 100644 index 00000000..7d20c1e5 --- /dev/null +++ b/src/lib/trace-ir/attributes.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "ATTRS" +#include "lib/lib-logging.h" + +#include "common/babeltrace.h" +#include +#include "lib/assert-pre.h" +#include "lib/object.h" +#include +#include "lib/value.h" +#include "attributes.h" +#include +#include "compat/string.h" +#include "common/assert.h" + +#define BT_ATTR_NAME_INDEX 0 +#define BT_ATTR_VALUE_INDEX 1 + +BT_HIDDEN +struct bt_value *bt_attributes_create(void) +{ + struct bt_value *attr_obj; + + /* + * Attributes: array value object of array value objects, each one + * containing two entries: a string value object (attributes + * field name), and a value object (attributes field value). + * + * Example (JSON representation): + * + * [ + * ["hostname", "eeppdesk"], + * ["sysname", "Linux"], + * ["tracer_major", 2], + * ["tracer_minor", 5] + * ] + */ + BT_LOGD_STR("Creating attributes object."); + attr_obj = bt_value_array_create(); + if (!attr_obj) { + BT_LOGE_STR("Failed to create array value."); + } else { + BT_LOGD("Created attributes object: addr=%p", + attr_obj); + } + + return attr_obj; +} + +BT_HIDDEN +void bt_attributes_destroy(struct bt_value *attr_obj) +{ + BT_LOGD("Destroying attributes object: addr=%p", attr_obj); + BT_OBJECT_PUT_REF_AND_RESET(attr_obj); +} + +BT_HIDDEN +int64_t bt_attributes_get_count(const struct bt_value *attr_obj) +{ + return bt_value_array_get_size(attr_obj); +} + +BT_HIDDEN +const char *bt_attributes_get_field_name(const struct bt_value *attr_obj, + uint64_t index) +{ + const char *ret = NULL; + const struct bt_value *attr_field_obj = NULL; + const struct bt_value *attr_field_name_obj = NULL; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + goto end; + } + + if (index >= bt_value_array_get_size(attr_obj)) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "index=%" PRIu64 ", count=%" PRId64, + index, bt_value_array_get_size(attr_obj)); + goto end; + } + + attr_field_obj = bt_value_array_borrow_element_by_index_const( + attr_obj, index); + if (!attr_field_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, index); + goto end; + } + + attr_field_name_obj = + bt_value_array_borrow_element_by_index_const(attr_field_obj, + BT_ATTR_NAME_INDEX); + if (!attr_field_name_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_ATTR_NAME_INDEX); + goto end; + } + + ret = bt_value_string_get(attr_field_name_obj); + +end: + return ret; +} + +BT_HIDDEN +struct bt_value *bt_attributes_borrow_field_value( + struct bt_value *attr_obj, uint64_t index) +{ + struct bt_value *value_obj = NULL; + struct bt_value *attr_field_obj = NULL; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + goto end; + } + + if (index >= bt_value_array_get_size(attr_obj)) { + BT_LOGW("Invalid parameter: index is out of bounds: " + "index=%" PRIu64 ", count=%" PRId64, + index, bt_value_array_get_size(attr_obj)); + goto end; + } + + attr_field_obj = + bt_value_array_borrow_element_by_index(attr_obj, index); + if (!attr_field_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, index); + goto end; + } + + value_obj = bt_value_array_borrow_element_by_index( + attr_field_obj, BT_ATTR_VALUE_INDEX); + if (!value_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_ATTR_VALUE_INDEX); + } + +end: + return value_obj; +} + +static +struct bt_value *bt_attributes_borrow_field_by_name( + struct bt_value *attr_obj, const char *name) +{ + uint64_t i; + int64_t attr_size; + struct bt_value *value_obj = NULL; + struct bt_value *attr_field_name_obj = NULL; + + attr_size = bt_value_array_get_size(attr_obj); + if (attr_size < 0) { + BT_LOGE("Cannot get array value's size: value-addr=%p", + attr_obj); + goto error; + } + + for (i = 0; i < attr_size; ++i) { + const char *field_name; + + value_obj = bt_value_array_borrow_element_by_index( + attr_obj, i); + if (!value_obj) { + BT_LOGE("Cannot get attributes object's array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_obj, i); + goto error; + } + + attr_field_name_obj = + bt_value_array_borrow_element_by_index( + value_obj, BT_ATTR_NAME_INDEX); + if (!attr_field_name_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, + value_obj, (int64_t) BT_ATTR_NAME_INDEX); + goto error; + } + + field_name = bt_value_string_get(attr_field_name_obj); + + if (!strcmp(field_name, name)) { + break; + } + + value_obj = NULL; + } + + return value_obj; + +error: + value_obj = NULL; + return value_obj; +} + +BT_HIDDEN +int bt_attributes_set_field_value(struct bt_value *attr_obj, + const char *name, struct bt_value *value_obj) +{ + int ret = 0; + struct bt_value *attr_field_obj = NULL; + + if (!attr_obj || !name || !value_obj) { + BT_LOGW("Invalid parameter: attributes object, name, or value object is NULL: " + "attr-value-addr=%p, name-addr=%p, value-addr=%p", + attr_obj, name, value_obj); + ret = -1; + goto end; + } + + attr_field_obj = bt_attributes_borrow_field_by_name(attr_obj, name); + if (attr_field_obj) { + ret = bt_value_array_set_element_by_index( + attr_field_obj, BT_ATTR_VALUE_INDEX, + value_obj); + attr_field_obj = NULL; + goto end; + } + + attr_field_obj = bt_value_array_create(); + if (!attr_field_obj) { + BT_LOGE_STR("Failed to create empty array value."); + ret = -1; + goto end; + } + + ret = bt_value_array_append_string_element(attr_field_obj, + name); + ret |= bt_value_array_append_element(attr_field_obj, + value_obj); + if (ret) { + BT_LOGE("Cannot append elements to array value: addr=%p", + attr_field_obj); + goto end; + } + + ret = bt_value_array_append_element(attr_obj, + attr_field_obj); + if (ret) { + BT_LOGE("Cannot append element to array value: " + "array-value-addr=%p, element-value-addr=%p", + attr_obj, attr_field_obj); + } + +end: + bt_object_put_ref(attr_field_obj); + return ret; +} + +BT_HIDDEN +struct bt_value *bt_attributes_borrow_field_value_by_name( + struct bt_value *attr_obj, const char *name) +{ + struct bt_value *value_obj = NULL; + struct bt_value *attr_field_obj = NULL; + + if (!attr_obj || !name) { + BT_LOGW("Invalid parameter: attributes object or name is NULL: " + "value-addr=%p, name-addr=%p", attr_obj, name); + goto end; + } + + attr_field_obj = bt_attributes_borrow_field_by_name(attr_obj, name); + if (!attr_field_obj) { + BT_LOGD("Cannot find attributes object's field by name: " + "value-addr=%p, name=\"%s\"", attr_obj, name); + goto end; + } + + value_obj = bt_value_array_borrow_element_by_index( + attr_field_obj, BT_ATTR_VALUE_INDEX); + if (!value_obj) { + BT_LOGE("Cannot get attribute array value's element by index: " + "value-addr=%p, index=%" PRIu64, attr_field_obj, + (uint64_t) BT_ATTR_VALUE_INDEX); + } + +end: + return value_obj; +} + +BT_HIDDEN +int bt_attributes_freeze(const struct bt_value *attr_obj) +{ + uint64_t i; + int64_t count; + int ret = 0; + + if (!attr_obj) { + BT_LOGW_STR("Invalid parameter: attributes object is NULL."); + ret = -1; + goto end; + } + + BT_LOGD("Freezing attributes object: value-addr=%p", attr_obj); + count = bt_value_array_get_size(attr_obj); + BT_ASSERT(count >= 0); + + /* + * We do not freeze the array value object itself here, since + * internal stuff could need to modify/add attributes. Each + * attribute is frozen one by one. + */ + for (i = 0; i < count; ++i) { + struct bt_value *obj = NULL; + + obj = bt_attributes_borrow_field_value( + (void *) attr_obj, i); + if (!obj) { + BT_LOGE("Cannot get attributes object's field value by index: " + "value-addr=%p, index=%" PRIu64, + attr_obj, i); + ret = -1; + goto end; + } + + bt_value_freeze(obj); + } + +end: + return ret; +} diff --git a/src/lib/trace-ir/attributes.h b/src/lib/trace-ir/attributes.h new file mode 100644 index 00000000..e493815c --- /dev/null +++ b/src/lib/trace-ir/attributes.h @@ -0,0 +1,68 @@ +#ifndef BABELTRACE_TRACE_IR_ATTRIBUTES_H +#define BABELTRACE_TRACE_IR_ATTRIBUTES_H + +/* + * Copyright (c) 2015-2018 Philippe Proulx + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "common/babeltrace.h" +#include + +BT_HIDDEN +struct bt_value *bt_attributes_create(void); + +BT_HIDDEN +void bt_attributes_destroy(struct bt_value *attr_obj); + +BT_HIDDEN +int64_t bt_attributes_get_count(const struct bt_value *attr_obj); + +BT_HIDDEN +const char *bt_attributes_get_field_name(const struct bt_value *attr_obj, + uint64_t index); + +BT_HIDDEN +struct bt_value *bt_attributes_borrow_field_value( + struct bt_value *attr_obj, + uint64_t index); + +BT_HIDDEN +int bt_attributes_set_field_value(struct bt_value *attr_obj, + const char *name, struct bt_value *value_obj); + +BT_HIDDEN +struct bt_value *bt_attributes_borrow_field_value_by_name( + struct bt_value *attr_obj, const char *name); + +BT_HIDDEN +int bt_attributes_freeze(const struct bt_value *attr_obj); + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_TRACE_IR_ATTRIBUTES_H */ diff --git a/src/lib/trace-ir/clock-class.c b/src/lib/trace-ir/clock-class.c new file mode 100644 index 00000000..46fa7332 --- /dev/null +++ b/src/lib/trace-ir/clock-class.c @@ -0,0 +1,310 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CLOCK-CLASS" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "compat/uuid.h" +#include +#include +#include "clock-class.h" +#include "clock-snapshot.h" +#include "utils.h" +#include "compat/compiler.h" +#include +#include "compat/string.h" +#include +#include "lib/object.h" +#include "common/assert.h" + +#define BT_ASSERT_PRE_CLOCK_CLASS_HOT(_cc) \ + BT_ASSERT_PRE_HOT((_cc), "Clock class", ": %!+K", (_cc)) + +static +void destroy_clock_class(struct bt_object *obj) +{ + struct bt_clock_class *clock_class = (void *) obj; + + BT_LIB_LOGD("Destroying clock class: %!+K", clock_class); + + if (clock_class->name.str) { + g_string_free(clock_class->name.str, TRUE); + clock_class->name.str = NULL; + clock_class->name.value = NULL; + } + + if (clock_class->description.str) { + g_string_free(clock_class->description.str, TRUE); + clock_class->description.str = NULL; + clock_class->description.value = NULL; + } + + bt_object_pool_finalize(&clock_class->cs_pool); + g_free(clock_class); +} + +static +void free_clock_snapshot(struct bt_clock_snapshot *clock_snapshot, + struct bt_clock_class *clock_class) +{ + bt_clock_snapshot_destroy(clock_snapshot); +} + +static inline +void set_base_offset(struct bt_clock_class *clock_class) +{ + clock_class->base_offset.overflows = bt_util_get_base_offset_ns( + clock_class->offset_seconds, clock_class->offset_cycles, + clock_class->frequency, &clock_class->base_offset.value_ns); +} + +struct bt_clock_class *bt_clock_class_create(bt_self_component *self_comp) +{ + int ret; + struct bt_clock_class *clock_class = NULL; + + BT_ASSERT_PRE_NON_NULL(self_comp, "Self component"); + BT_LOGD_STR("Creating default clock class object"); + + clock_class = g_new0(struct bt_clock_class, 1); + if (!clock_class) { + BT_LOGE_STR("Failed to allocate one clock class."); + goto error; + } + + bt_object_init_shared(&clock_class->base, destroy_clock_class); + clock_class->name.str = g_string_new(NULL); + if (!clock_class->name.str) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + clock_class->description.str = g_string_new(NULL); + if (!clock_class->description.str) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + clock_class->frequency = UINT64_C(1000000000); + clock_class->origin_is_unix_epoch = BT_TRUE; + set_base_offset(clock_class); + ret = bt_object_pool_initialize(&clock_class->cs_pool, + (bt_object_pool_new_object_func) bt_clock_snapshot_new, + (bt_object_pool_destroy_object_func) + free_clock_snapshot, + clock_class); + if (ret) { + BT_LOGE("Failed to initialize clock snapshot pool: ret=%d", + ret); + goto error; + } + + BT_LIB_LOGD("Created clock class object: %!+K", clock_class); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(clock_class); + +end: + return clock_class; +} + +const char *bt_clock_class_get_name(const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return clock_class->name.value; +} + +enum bt_clock_class_status bt_clock_class_set_name( + struct bt_clock_class *clock_class, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + g_string_assign(clock_class->name.str, name); + clock_class->name.value = clock_class->name.str->str; + BT_LIB_LOGV("Set clock class's name: %!+K", clock_class); + return BT_CLOCK_CLASS_STATUS_OK; +} + +const char *bt_clock_class_get_description( + const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return clock_class->description.value; +} + +enum bt_clock_class_status bt_clock_class_set_description( + struct bt_clock_class *clock_class, const char *descr) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_NON_NULL(descr, "Description"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + g_string_assign(clock_class->description.str, descr); + clock_class->description.value = clock_class->description.str->str; + BT_LIB_LOGV("Set clock class's description: %!+K", + clock_class); + return BT_CLOCK_CLASS_STATUS_OK; +} + +uint64_t bt_clock_class_get_frequency(const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return clock_class->frequency; +} + +void bt_clock_class_set_frequency(struct bt_clock_class *clock_class, + uint64_t frequency) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + BT_ASSERT_PRE(frequency != UINT64_C(-1) && frequency != 0, + "Invalid frequency: %![cc-]+K, new-freq=%" PRIu64, + clock_class, frequency); + BT_ASSERT_PRE(clock_class->offset_cycles < frequency, + "Offset (cycles) is greater than clock class's frequency: " + "%![cc-]+K, new-freq=%" PRIu64, clock_class, frequency); + clock_class->frequency = frequency; + set_base_offset(clock_class); + BT_LIB_LOGV("Set clock class's frequency: %!+K", clock_class); +} + +uint64_t bt_clock_class_get_precision(const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return clock_class->precision; +} + +void bt_clock_class_set_precision(struct bt_clock_class *clock_class, + uint64_t precision) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + BT_ASSERT_PRE(precision != UINT64_C(-1), + "Invalid precision: %![cc-]+K, new-precision=%" PRIu64, + clock_class, precision); + clock_class->precision = precision; + BT_LIB_LOGV("Set clock class's precision: %!+K", clock_class); +} + +void bt_clock_class_get_offset(const struct bt_clock_class *clock_class, + int64_t *seconds, uint64_t *cycles) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_NON_NULL(seconds, "Seconds (output)"); + BT_ASSERT_PRE_NON_NULL(cycles, "Cycles (output)"); + *seconds = clock_class->offset_seconds; + *cycles = clock_class->offset_cycles; +} + +void bt_clock_class_set_offset(struct bt_clock_class *clock_class, + int64_t seconds, uint64_t cycles) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + BT_ASSERT_PRE(cycles < clock_class->frequency, + "Offset (cycles) is greater than clock class's frequency: " + "%![cc-]+K, new-offset-cycles=%" PRIu64, clock_class, cycles); + clock_class->offset_seconds = seconds; + clock_class->offset_cycles = cycles; + set_base_offset(clock_class); + BT_LIB_LOGV("Set clock class's offset: %!+K", clock_class); +} + +bt_bool bt_clock_class_origin_is_unix_epoch(const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return (bool) clock_class->origin_is_unix_epoch; +} + +void bt_clock_class_set_origin_is_unix_epoch(struct bt_clock_class *clock_class, + bt_bool origin_is_unix_epoch) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + clock_class->origin_is_unix_epoch = (bool) origin_is_unix_epoch; + BT_LIB_LOGV("Set clock class's origin is Unix epoch property: %!+K", + clock_class); +} + +bt_uuid bt_clock_class_get_uuid(const struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + return clock_class->uuid.value; +} + +void bt_clock_class_set_uuid(struct bt_clock_class *clock_class, + bt_uuid uuid) +{ + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_NON_NULL(uuid, "UUID"); + BT_ASSERT_PRE_CLOCK_CLASS_HOT(clock_class); + memcpy(clock_class->uuid.uuid, uuid, BABELTRACE_UUID_LEN); + clock_class->uuid.value = clock_class->uuid.uuid; + BT_LIB_LOGV("Set clock class's UUID: %!+K", clock_class); +} + +BT_HIDDEN +void _bt_clock_class_freeze(const struct bt_clock_class *clock_class) +{ + BT_ASSERT(clock_class); + + if (clock_class->frozen) { + return; + } + + BT_LIB_LOGD("Freezing clock class: %!+K", clock_class); + ((struct bt_clock_class *) clock_class)->frozen = 1; +} + +enum bt_clock_class_status bt_clock_class_cycles_to_ns_from_origin( + const struct bt_clock_class *clock_class, + uint64_t cycles, int64_t *ns) +{ + int ret; + + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_NON_NULL(ns, "Nanoseconds (output)"); + ret = bt_util_ns_from_origin_clock_class(clock_class, cycles, ns); + if (ret) { + ret = BT_CLOCK_CLASS_STATUS_OVERFLOW; + BT_LIB_LOGW("Cannot convert cycles to nanoseconds " + "from origin for given clock class: " + "value overflows the signed 64-bit integer range: " + "%![cc-]+K, cycles=%" PRIu64, + clock_class, cycles); + } + + return ret; +} + +void bt_clock_class_get_ref(const struct bt_clock_class *clock_class) +{ + bt_object_get_ref(clock_class); +} + +void bt_clock_class_put_ref(const struct bt_clock_class *clock_class) +{ + bt_object_put_ref(clock_class); +} diff --git a/src/lib/trace-ir/clock-class.h b/src/lib/trace-ir/clock-class.h new file mode 100644 index 00000000..e6e57e90 --- /dev/null +++ b/src/lib/trace-ir/clock-class.h @@ -0,0 +1,119 @@ +#ifndef BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H +#define BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/object.h" +#include "common/babeltrace.h" +#include "common/common.h" +#include "lib/object-pool.h" +#include "compat/uuid.h" +#include +#include "lib/property.h" +#include "common/assert.h" +#include +#include +#include + +struct bt_clock_class { + struct bt_object base; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } description; + + uint64_t frequency; + uint64_t precision; + int64_t offset_seconds; + uint64_t offset_cycles; + + struct { + uint8_t uuid[BABELTRACE_UUID_LEN]; + + /* NULL or `uuid` above */ + bt_uuid value; + } uuid; + + bool origin_is_unix_epoch; + + /* + * This is computed every time you call + * bt_clock_class_set_frequency() or + * bt_clock_class_set_offset(), as well as initially. It is the + * base offset in nanoseconds including both `offset_seconds` + * and `offset_cycles` above in the result. It is used to + * accelerate future calls to + * bt_clock_snapshot_get_ns_from_origin() and + * bt_clock_class_cycles_to_ns_from_origin(). + * + * `overflows` is true if the base offset cannot be computed + * because of an overflow. + */ + struct { + int64_t value_ns; + bool overflows; + } base_offset; + + /* Pool of `struct bt_clock_snapshot *` */ + struct bt_object_pool cs_pool; + + bool frozen; +}; + +BT_HIDDEN +void _bt_clock_class_freeze(const struct bt_clock_class *clock_class); + +#ifdef BT_DEV_MODE +# define bt_clock_class_freeze _bt_clock_class_freeze +#else +# define bt_clock_class_freeze(_cc) +#endif + +BT_HIDDEN +bt_bool bt_clock_class_is_valid(struct bt_clock_class *clock_class); + +static inline +int bt_clock_class_clock_value_from_ns_from_origin( + struct bt_clock_class *cc, int64_t ns_from_origin, + uint64_t *raw_value) +{ + BT_ASSERT(cc); + + return bt_common_clock_value_from_ns_from_origin(cc->offset_seconds, + cc->offset_cycles, cc->frequency, ns_from_origin, + raw_value); +} + +#endif /* BABELTRACE_TRACE_IR_CLOCK_CLASS_INTERNAL_H */ diff --git a/src/lib/trace-ir/clock-snapshot-set.h b/src/lib/trace-ir/clock-snapshot-set.h new file mode 100644 index 00000000..0ad92809 --- /dev/null +++ b/src/lib/trace-ir/clock-snapshot-set.h @@ -0,0 +1,160 @@ +#ifndef BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H +#define BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H + +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/assert.h" + +#include "clock-snapshot.h" +#include "clock-class.h" + +struct bt_clock_snapshot_set { + /* Unique objects owned by this */ + GPtrArray *clock_snapshots; + + /* Weak; points to one of the clock snapshots above */ + struct bt_clock_snapshot *default_cs; +}; + +static inline +int bt_clock_snapshot_set_initialize(struct bt_clock_snapshot_set *cs_set) +{ + int ret = 0; + + cs_set->clock_snapshots = g_ptr_array_sized_new(1); + if (!cs_set->clock_snapshots) { +#ifdef BT_LOGE_STR + BT_LOGE_STR("Failed to allocate one GPtrArray."); +#endif + + ret = -1; + goto end; + } + + cs_set->default_cs = NULL; + +end: + return ret; +} + +static inline +void bt_clock_snapshot_set_reset(struct bt_clock_snapshot_set *cs_set) +{ + uint64_t i; + + BT_ASSERT(cs_set); + BT_ASSERT(cs_set->clock_snapshots); + + for (i = 0; i < cs_set->clock_snapshots->len; i++) { + struct bt_clock_snapshot *cs = cs_set->clock_snapshots->pdata[i]; + + BT_ASSERT(cs); + bt_clock_snapshot_reset(cs); + } + + cs_set->default_cs = NULL; +} + +static inline +void bt_clock_snapshot_set_finalize(struct bt_clock_snapshot_set *cs_set) +{ + uint64_t i; + + BT_ASSERT(cs_set); + + if (cs_set->clock_snapshots) { + for (i = 0; i < cs_set->clock_snapshots->len; i++) { + struct bt_clock_snapshot *cs = + cs_set->clock_snapshots->pdata[i]; + + BT_ASSERT(cs); + bt_clock_snapshot_recycle(cs); + } + + g_ptr_array_free(cs_set->clock_snapshots, TRUE); + } + + cs_set->default_cs = NULL; +} + +static inline +int bt_clock_snapshot_set_set_clock_snapshot(struct bt_clock_snapshot_set *cs_set, + struct bt_clock_class *cc, uint64_t raw_value) +{ + int ret = 0; + struct bt_clock_snapshot *clock_snapshot = NULL; + uint64_t i; + + BT_ASSERT(cs_set); + BT_ASSERT(cc); + + /* + * Check if we already have a value for this clock class. + * + * TODO: When we have many clock classes, make this more + * efficient. + */ + for (i = 0; i < cs_set->clock_snapshots->len; i++) { + struct bt_clock_snapshot *cs = cs_set->clock_snapshots->pdata[i]; + + BT_ASSERT(cs); + + if (cs->clock_class == cc) { + clock_snapshot = cs; + break; + } + } + + if (!clock_snapshot) { + clock_snapshot = bt_clock_snapshot_create(cc); + if (!clock_snapshot) { +#ifdef BT_LIB_LOGE + BT_LIB_LOGE("Cannot create a clock snapshot from a clock class: " + "%![cc-]+K", cc); +#endif + + ret = -1; + goto end; + } + + g_ptr_array_add(cs_set->clock_snapshots, clock_snapshot); + } + + bt_clock_snapshot_set_raw_value(clock_snapshot, raw_value); + +end: + return ret; +} + +static inline +void bt_clock_snapshot_set_set_default_clock_snapshot( + struct bt_clock_snapshot_set *cs_set, uint64_t raw_value) +{ + BT_ASSERT(cs_set); + BT_ASSERT(cs_set->default_cs); + bt_clock_snapshot_set_raw_value(cs_set->default_cs, raw_value); +} + +#endif /* BABELTRACE_GRAPH_CLOCK_SNAPSHOT_SET_H */ diff --git a/src/lib/trace-ir/clock-snapshot.c b/src/lib/trace-ir/clock-snapshot.c new file mode 100644 index 00000000..04ef345e --- /dev/null +++ b/src/lib/trace-ir/clock-snapshot.c @@ -0,0 +1,179 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "CLOCK-SNAPSHOT" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "compat/uuid.h" +#include "clock-class.h" +#include "clock-snapshot.h" +#include +#include "compat/compiler.h" +#include +#include "compat/string.h" +#include +#include "lib/object.h" +#include "common/assert.h" + +BT_HIDDEN +void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot) +{ + BT_LIB_LOGD("Destroying clock snapshot: %!+k", clock_snapshot); + BT_OBJECT_PUT_REF_AND_RESET(clock_snapshot->clock_class); + g_free(clock_snapshot); +} + +BT_HIDDEN +struct bt_clock_snapshot *bt_clock_snapshot_new( + struct bt_clock_class *clock_class) +{ + struct bt_clock_snapshot *ret = NULL; + + BT_ASSERT(clock_class); + BT_LIB_LOGD("Creating clock snapshot object: %![cc-]+K=", + clock_class); + ret = g_new0(struct bt_clock_snapshot, 1); + if (!ret) { + BT_LOGE_STR("Failed to allocate one clock snapshot."); + goto end; + } + + bt_object_init_unique(&ret->base); + ret->clock_class = clock_class; + bt_object_get_no_null_check(clock_class); + bt_clock_class_freeze(clock_class); + BT_LIB_LOGD("Created clock snapshot object: %!+k", ret); + +end: + return ret; +} + +BT_HIDDEN +struct bt_clock_snapshot *bt_clock_snapshot_create( + struct bt_clock_class *clock_class) +{ + struct bt_clock_snapshot *clock_snapshot = NULL; + + BT_ASSERT(clock_class); + clock_snapshot = bt_object_pool_create_object(&clock_class->cs_pool); + if (!clock_snapshot) { + BT_LIB_LOGE("Cannot allocate one clock snapshot from clock class's clock snapshot pool: " + "%![cc-]+K", clock_class); + goto error; + } + + if (likely(!clock_snapshot->clock_class)) { + clock_snapshot->clock_class = clock_class; + bt_object_get_no_null_check(clock_class); + } + + goto end; + +error: + if (clock_snapshot) { + bt_clock_snapshot_recycle(clock_snapshot); + clock_snapshot = NULL; + } + +end: + return clock_snapshot; +} + +BT_HIDDEN +void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot) +{ + struct bt_clock_class *clock_class; + + BT_ASSERT(clock_snapshot); + BT_LIB_LOGD("Recycling clock snapshot: %!+k", clock_snapshot); + + /* + * Those are the important ordered steps: + * + * 1. Reset the clock snapshot object, but do NOT put its clock + * class's reference. This clock class contains the pool to + * which we're about to recycle this clock snapshot object, + * so we must guarantee its existence thanks to this existing + * reference. + * + * 2. Move the clock class reference to our `clock_class` + * variable so that we can set the clock snapshot's clock + * class member to NULL before recycling it. We CANNOT do + * this after we put the clock class reference because this + * bt_object_put_ref() could destroy the clock class, also + * destroying its clock snapshot pool, thus also destroying + * our clock snapshot object (this would result in an invalid + * write access). + * + * 3. Recycle the clock snapshot object. + * + * 4. Put our clock class reference. + */ + bt_clock_snapshot_reset(clock_snapshot); + clock_class = clock_snapshot->clock_class; + BT_ASSERT(clock_class); + clock_snapshot->clock_class = NULL; + bt_object_pool_recycle_object(&clock_class->cs_pool, clock_snapshot); + bt_object_put_ref(clock_class); +} + +uint64_t bt_clock_snapshot_get_value( + const struct bt_clock_snapshot *clock_snapshot) +{ + BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); + BT_ASSERT_PRE(clock_snapshot->is_set, + "Clock snapshot is not set: %!+k", clock_snapshot); + return clock_snapshot->value_cycles; +} + +enum bt_clock_snapshot_status bt_clock_snapshot_get_ns_from_origin( + const struct bt_clock_snapshot *clock_snapshot, + int64_t *ret_value_ns) +{ + int ret = BT_CLOCK_SNAPSHOT_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); + BT_ASSERT_PRE_NON_NULL(ret_value_ns, "Value (ns) (output)"); + BT_ASSERT_PRE(clock_snapshot->is_set, + "Clock snapshot is not set: %!+k", clock_snapshot); + + if (clock_snapshot->ns_from_origin_overflows) { + BT_LIB_LOGD("Clock snapshot, once converted to nanoseconds from origin, " + "overflows the signed 64-bit integer range: " + "%![cs-]+k", clock_snapshot); + ret = BT_CLOCK_SNAPSHOT_STATUS_OVERFLOW; + goto end; + } + + *ret_value_ns = clock_snapshot->ns_from_origin; + +end: + return ret; +} + +const struct bt_clock_class *bt_clock_snapshot_borrow_clock_class_const( + const struct bt_clock_snapshot *clock_snapshot) +{ + BT_ASSERT_PRE_NON_NULL(clock_snapshot, "Clock snapshot"); + return clock_snapshot->clock_class; +} diff --git a/src/lib/trace-ir/clock-snapshot.h b/src/lib/trace-ir/clock-snapshot.h new file mode 100644 index 00000000..5375f3ab --- /dev/null +++ b/src/lib/trace-ir/clock-snapshot.h @@ -0,0 +1,92 @@ +#ifndef BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H +#define BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "lib/object.h" +#include +#include + +#include "clock-class.h" +#include "utils.h" + +struct bt_clock_class; + +struct bt_clock_snapshot { + struct bt_object base; + struct bt_clock_class *clock_class; + uint64_t value_cycles; + bool ns_from_origin_overflows; + int64_t ns_from_origin; + bool is_set; +}; + +static inline +void bt_clock_snapshot_set(struct bt_clock_snapshot *clock_snapshot) +{ + BT_ASSERT(clock_snapshot); + clock_snapshot->is_set = true; +} + +static inline +void bt_clock_snapshot_reset(struct bt_clock_snapshot *clock_snapshot) +{ + BT_ASSERT(clock_snapshot); + clock_snapshot->is_set = false; +} + +static inline +void set_ns_from_origin(struct bt_clock_snapshot *clock_snapshot) +{ + if (bt_util_ns_from_origin_clock_class(clock_snapshot->clock_class, + clock_snapshot->value_cycles, + &clock_snapshot->ns_from_origin)) { + clock_snapshot->ns_from_origin_overflows = true; + } +} + +static inline +void bt_clock_snapshot_set_raw_value(struct bt_clock_snapshot *clock_snapshot, + uint64_t cycles) +{ + BT_ASSERT(clock_snapshot); + clock_snapshot->value_cycles = cycles; + set_ns_from_origin(clock_snapshot); + bt_clock_snapshot_set(clock_snapshot); +} + +BT_HIDDEN +void bt_clock_snapshot_destroy(struct bt_clock_snapshot *clock_snapshot); + +BT_HIDDEN +struct bt_clock_snapshot *bt_clock_snapshot_new(struct bt_clock_class *clock_class); + +BT_HIDDEN +struct bt_clock_snapshot *bt_clock_snapshot_create( + struct bt_clock_class *clock_class); + +BT_HIDDEN +void bt_clock_snapshot_recycle(struct bt_clock_snapshot *clock_snapshot); + +#endif /* BABELTRACE_TRACE_IR_CLOCK_SNAPSHOT_INTERNAL_H */ diff --git a/src/lib/trace-ir/event-class.c b/src/lib/trace-ir/event-class.c new file mode 100644 index 00000000..e4676638 --- /dev/null +++ b/src/lib/trace-ir/event-class.c @@ -0,0 +1,414 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "EVENT-CLASS" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include "compat/compiler.h" +#include "compat/endian.h" +#include +#include "lib/value.h" +#include "common/assert.h" +#include +#include + +#include "attributes.h" +#include "clock-snapshot.h" +#include "event-class.h" +#include "event.h" +#include "field-class.h" +#include "field.h" +#include "resolve-field-path.h" +#include "stream-class.h" +#include "trace.h" +#include "utils.h" + +#define BT_ASSERT_PRE_EVENT_CLASS_HOT(_ec) \ + BT_ASSERT_PRE_HOT(((const struct bt_event_class *) (_ec)), \ + "Event class", ": %!+E", (_ec)) + +static +void destroy_event_class(struct bt_object *obj) +{ + struct bt_event_class *event_class = (void *) obj; + + BT_LIB_LOGD("Destroying event class: %!+E", event_class); + + if (event_class->name.str) { + g_string_free(event_class->name.str, TRUE); + event_class->name.str = NULL; + } + + if (event_class->emf_uri.str) { + g_string_free(event_class->emf_uri.str, TRUE); + event_class->emf_uri.str = NULL; + } + + BT_LOGD_STR("Putting context field class."); + BT_OBJECT_PUT_REF_AND_RESET(event_class->specific_context_fc); + BT_LOGD_STR("Putting payload field class."); + BT_OBJECT_PUT_REF_AND_RESET(event_class->payload_fc); + bt_object_pool_finalize(&event_class->event_pool); + g_free(obj); +} + +static +void free_event(struct bt_event *event, + struct bt_event_class *event_class) +{ + bt_event_destroy(event); +} + +BT_ASSERT_PRE_FUNC +static +bool event_class_id_is_unique(const struct bt_stream_class *stream_class, + uint64_t id) +{ + uint64_t i; + bool is_unique = true; + + for (i = 0; i < stream_class->event_classes->len; i++) { + const struct bt_event_class *ec = + stream_class->event_classes->pdata[i]; + + if (ec->id == id) { + is_unique = false; + goto end; + } + } + +end: + return is_unique; +} + +static +struct bt_event_class *create_event_class_with_id( + struct bt_stream_class *stream_class, uint64_t id) +{ + int ret; + struct bt_event_class *event_class; + + BT_ASSERT(stream_class); + BT_ASSERT_PRE(event_class_id_is_unique(stream_class, id), + "Duplicate event class ID: %![sc-]+S, id=%" PRIu64, + stream_class, id); + BT_LIB_LOGD("Creating event class object: %![sc-]+S, id=%" PRIu64, + stream_class, id); + event_class = g_new0(struct bt_event_class, 1); + if (!event_class) { + BT_LOGE_STR("Failed to allocate one event class."); + goto error; + } + + bt_object_init_shared_with_parent(&event_class->base, + destroy_event_class); + event_class->id = id; + bt_property_uint_init(&event_class->log_level, + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE, 0); + event_class->name.str = g_string_new(NULL); + if (!event_class->name.str) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = -1; + goto end; + } + + event_class->emf_uri.str = g_string_new(NULL); + if (!event_class->emf_uri.str) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = -1; + goto end; + } + + ret = bt_object_pool_initialize(&event_class->event_pool, + (bt_object_pool_new_object_func) bt_event_new, + (bt_object_pool_destroy_object_func) free_event, + event_class); + if (ret) { + BT_LOGE("Failed to initialize event pool: ret=%d", + ret); + goto error; + } + + bt_object_set_parent(&event_class->base, &stream_class->base); + g_ptr_array_add(stream_class->event_classes, event_class); + bt_stream_class_freeze(stream_class); + BT_LIB_LOGD("Created event class object: %!+E", event_class); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(event_class); + +end: + return event_class; +} + +struct bt_event_class *bt_event_class_create( + struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE(stream_class->assigns_automatic_event_class_id, + "Stream class does not automatically assigns event class IDs: " + "%![sc-]+S", stream_class); + return create_event_class_with_id(stream_class, + (uint64_t) stream_class->event_classes->len); +} + +struct bt_event_class *bt_event_class_create_with_id( + struct bt_stream_class *stream_class, uint64_t id) +{ + BT_ASSERT_PRE(!stream_class->assigns_automatic_event_class_id, + "Stream class automatically assigns event class IDs: " + "%![sc-]+S", stream_class); + return create_event_class_with_id(stream_class, id); +} + +const char *bt_event_class_get_name(const struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->name.value; +} + +enum bt_event_class_status bt_event_class_set_name( + struct bt_event_class *event_class, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); + g_string_assign(event_class->name.str, name); + event_class->name.value = event_class->name.str->str; + BT_LIB_LOGV("Set event class's name: %!+E", event_class); + return BT_EVENT_CLASS_STATUS_OK; +} + +uint64_t bt_event_class_get_id(const struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->id; +} + +enum bt_property_availability bt_event_class_get_log_level( + const struct bt_event_class *event_class, + enum bt_event_class_log_level *log_level) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(log_level, "Log level (output)"); + *log_level = (enum bt_event_class_log_level) + event_class->log_level.value; + return event_class->log_level.base.avail; +} + +void bt_event_class_set_log_level( + struct bt_event_class *event_class, + enum bt_event_class_log_level log_level) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); + bt_property_uint_set(&event_class->log_level, + (uint64_t) log_level); + BT_LIB_LOGV("Set event class's log level: %!+E", event_class); +} + +const char *bt_event_class_get_emf_uri(const struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->emf_uri.value; +} + +enum bt_event_class_status bt_event_class_set_emf_uri( + struct bt_event_class *event_class, + const char *emf_uri) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(emf_uri, "EMF URI"); + BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); + g_string_assign(event_class->emf_uri.str, emf_uri); + event_class->emf_uri.value = event_class->emf_uri.str->str; + BT_LIB_LOGV("Set event class's EMF URI: %!+E", event_class); + return BT_EVENT_CLASS_STATUS_OK; +} + +struct bt_stream_class *bt_event_class_borrow_stream_class( + struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return bt_event_class_borrow_stream_class_inline(event_class); +} + +const struct bt_stream_class * +bt_event_class_borrow_stream_class_const( + const struct bt_event_class *event_class) +{ + return bt_event_class_borrow_stream_class((void *) event_class); +} + +const struct bt_field_class * +bt_event_class_borrow_specific_context_field_class_const( + const struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->specific_context_fc; +} + +struct bt_field_class * +bt_event_class_borrow_specific_context_field_class( + struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->specific_context_fc; +} + +enum bt_event_class_status bt_event_class_set_specific_context_field_class( + struct bt_event_class *event_class, + struct bt_field_class *field_class) +{ + int ret; + struct bt_stream_class *stream_class; + struct bt_resolve_field_path_context resolve_ctx = { + .packet_context = NULL, + .event_common_context = NULL, + .event_specific_context = field_class, + .event_payload = NULL, + }; + + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); + BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); + BT_ASSERT_PRE(bt_field_class_get_type(field_class) == + BT_FIELD_CLASS_TYPE_STRUCTURE, + "Specific context field class is not a structure field class: " + "%!+F", field_class); + stream_class = bt_event_class_borrow_stream_class_inline( + event_class); + resolve_ctx.packet_context = stream_class->packet_context_fc; + resolve_ctx.event_common_context = + stream_class->event_common_context_fc; + + ret = bt_resolve_field_paths(field_class, &resolve_ctx); + if (ret) { + /* + * This is the only reason for which + * bt_resolve_field_paths() can fail: anything else + * would be because a precondition is not satisfied. + */ + ret = BT_EVENT_CLASS_STATUS_NOMEM; + goto end; + } + + bt_field_class_make_part_of_trace_class(field_class); + bt_object_put_ref(event_class->specific_context_fc); + event_class->specific_context_fc = field_class; + bt_object_get_no_null_check(event_class->specific_context_fc); + bt_field_class_freeze(field_class); + BT_LIB_LOGV("Set event class's specific context field class: %!+E", + event_class); + +end: + return ret; +} + +const struct bt_field_class *bt_event_class_borrow_payload_field_class_const( + const struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->payload_fc; +} + +struct bt_field_class *bt_event_class_borrow_payload_field_class( + struct bt_event_class *event_class) +{ + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + return event_class->payload_fc; +} + +enum bt_event_class_status bt_event_class_set_payload_field_class( + struct bt_event_class *event_class, + struct bt_field_class *field_class) +{ + int ret; + struct bt_stream_class *stream_class; + struct bt_resolve_field_path_context resolve_ctx = { + .packet_context = NULL, + .event_common_context = NULL, + .event_specific_context = NULL, + .event_payload = field_class, + }; + + BT_ASSERT_PRE_NON_NULL(event_class, "Event class"); + BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); + BT_ASSERT_PRE_EVENT_CLASS_HOT(event_class); + BT_ASSERT_PRE(bt_field_class_get_type(field_class) == + BT_FIELD_CLASS_TYPE_STRUCTURE, + "Payload field class is not a structure field class: %!+F", + field_class); + stream_class = bt_event_class_borrow_stream_class_inline( + event_class); + resolve_ctx.packet_context = stream_class->packet_context_fc; + resolve_ctx.event_common_context = + stream_class->event_common_context_fc; + resolve_ctx.event_specific_context = event_class->specific_context_fc; + + ret = bt_resolve_field_paths(field_class, &resolve_ctx); + if (ret) { + /* + * This is the only reason for which + * bt_resolve_field_paths() can fail: anything else + * would be because a precondition is not satisfied. + */ + ret = BT_EVENT_CLASS_STATUS_NOMEM; + goto end; + } + + bt_field_class_make_part_of_trace_class(field_class); + bt_object_put_ref(event_class->payload_fc); + event_class->payload_fc = field_class; + bt_object_get_no_null_check(event_class->payload_fc); + bt_field_class_freeze(field_class); + BT_LIB_LOGV("Set event class's payload field class: %!+E", event_class); + +end: + return ret; +} + +BT_HIDDEN +void _bt_event_class_freeze(const struct bt_event_class *event_class) +{ + /* The field classes are already frozen */ + BT_ASSERT(event_class); + BT_LIB_LOGD("Freezing event class: %!+E", event_class); + ((struct bt_event_class *) event_class)->frozen = true; +} + +void bt_event_class_get_ref(const struct bt_event_class *event_class) +{ + bt_object_get_ref(event_class); +} + +void bt_event_class_put_ref(const struct bt_event_class *event_class) +{ + bt_object_put_ref(event_class); +} diff --git a/src/lib/trace-ir/event-class.h b/src/lib/trace-ir/event-class.h new file mode 100644 index 00000000..b368a286 --- /dev/null +++ b/src/lib/trace-ir/event-class.h @@ -0,0 +1,89 @@ +#ifndef BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H +#define BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/assert-pre.h" +#include +#include +#include "common/babeltrace.h" +#include +#include +#include +#include +#include "lib/object.h" +#include "common/assert.h" +#include "lib/object-pool.h" +#include "lib/property.h" +#include +#include + +#include "trace.h" + +struct bt_event_class { + struct bt_object base; + struct bt_field_class *specific_context_fc; + struct bt_field_class *payload_fc; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + uint64_t id; + struct bt_property_uint log_level; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } emf_uri; + + /* Pool of `struct bt_event *` */ + struct bt_object_pool event_pool; + + bool frozen; +}; + +BT_HIDDEN +void _bt_event_class_freeze(const struct bt_event_class *event_class); + +#ifdef BT_DEV_MODE +# define bt_event_class_freeze _bt_event_class_freeze +#else +# define bt_event_class_freeze(_ec) +#endif + +static inline +struct bt_stream_class *bt_event_class_borrow_stream_class_inline( + const struct bt_event_class *event_class) +{ + BT_ASSERT(event_class); + return (void *) bt_object_borrow_parent(&event_class->base); +} + +#endif /* BABELTRACE_TRACE_IR_EVENT_CLASS_INTERNAL_H */ diff --git a/src/lib/trace-ir/event.c b/src/lib/trace-ir/event.c new file mode 100644 index 00000000..133d0d2a --- /dev/null +++ b/src/lib/trace-ir/event.c @@ -0,0 +1,239 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "EVENT" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "compat/compiler.h" +#include + +#include "field.h" +#include "field-class.h" +#include "event.h" +#include "stream-class.h" +#include "stream.h" +#include "packet.h" +#include "trace.h" +#include "packet.h" +#include "attributes.h" +#include "event-class.h" + +BT_HIDDEN +void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen) +{ + BT_ASSERT(event); + BT_LIB_LOGD("Setting event's frozen state: %!+e, is-frozen=%d", + event, is_frozen); + + if (event->common_context_field) { + BT_LOGD_STR("Setting event's common context field's frozen state."); + bt_field_set_is_frozen( + event->common_context_field, is_frozen); + } + + if (event->specific_context_field) { + BT_LOGD_STR("Setting event's specific context field's frozen state."); + bt_field_set_is_frozen(event->specific_context_field, + is_frozen); + } + + if (event->payload_field) { + BT_LOGD_STR("Setting event's payload field's frozen state."); + bt_field_set_is_frozen(event->payload_field, + is_frozen); + } + + ((struct bt_event *) event)->frozen = is_frozen; + BT_LOGD_STR("Setting event's packet's frozen state."); + bt_packet_set_is_frozen(event->packet, is_frozen); +} + +BT_HIDDEN +struct bt_event *bt_event_new(struct bt_event_class *event_class) +{ + struct bt_event *event = NULL; + struct bt_stream_class *stream_class; + struct bt_field_class *fc; + + BT_ASSERT(event_class); + event = g_new0(struct bt_event, 1); + if (!event) { + BT_LOGE_STR("Failed to allocate one event."); + goto error; + } + + bt_object_init_unique(&event->base); + stream_class = bt_event_class_borrow_stream_class(event_class); + BT_ASSERT(stream_class); + fc = stream_class->event_common_context_fc; + if (fc) { + event->common_context_field = bt_field_create(fc); + if (!event->common_context_field) { + /* bt_field_create() logs errors */ + goto error; + } + } + + fc = event_class->specific_context_fc; + if (fc) { + event->specific_context_field = bt_field_create(fc); + if (!event->specific_context_field) { + /* bt_field_create() logs errors */ + goto error; + } + } + + fc = event_class->payload_fc; + if (fc) { + event->payload_field = bt_field_create(fc); + if (!event->payload_field) { + /* bt_field_create() logs errors */ + goto error; + } + } + + goto end; + +error: + if (event) { + bt_event_destroy(event); + event = NULL; + } + +end: + return event; +} + +struct bt_event_class *bt_event_borrow_class(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->class; +} + +const struct bt_event_class *bt_event_borrow_class_const( + const struct bt_event *event) +{ + return bt_event_borrow_class((void *) event); +} + +struct bt_stream *bt_event_borrow_stream(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->packet ? event->packet->stream : NULL; +} + +const struct bt_stream *bt_event_borrow_stream_const( + const struct bt_event *event) +{ + return bt_event_borrow_stream((void *) event); +} + +struct bt_field *bt_event_borrow_common_context_field(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->common_context_field; +} + +const struct bt_field *bt_event_borrow_common_context_field_const( + const struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->common_context_field; +} + +struct bt_field *bt_event_borrow_specific_context_field(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->specific_context_field; +} + +const struct bt_field *bt_event_borrow_specific_context_field_const( + const struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->specific_context_field; +} + +struct bt_field *bt_event_borrow_payload_field(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->payload_field; +} + +const struct bt_field *bt_event_borrow_payload_field_const( + const struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->payload_field; +} + +BT_HIDDEN +void bt_event_destroy(struct bt_event *event) +{ + BT_ASSERT(event); + BT_LIB_LOGD("Destroying event: %!+e", event); + + if (event->common_context_field) { + BT_LOGD_STR("Destroying event's stream event context field."); + bt_field_destroy(event->common_context_field); + event->common_context_field = NULL; + } + + if (event->specific_context_field) { + BT_LOGD_STR("Destroying event's context field."); + bt_field_destroy(event->specific_context_field); + event->specific_context_field = NULL; + } + + if (event->payload_field) { + BT_LOGD_STR("Destroying event's payload field."); + bt_field_destroy(event->payload_field); + event->payload_field = NULL; + } + + BT_LOGD_STR("Putting event's class."); + bt_object_put_ref(event->class); + BT_LOGD_STR("Putting event's packet."); + BT_OBJECT_PUT_REF_AND_RESET(event->packet); + g_free(event); +} + +struct bt_packet *bt_event_borrow_packet(struct bt_event *event) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + return event->packet; +} + +const struct bt_packet *bt_event_borrow_packet_const( + const struct bt_event *event) +{ + return bt_event_borrow_packet((void *) event); +} diff --git a/src/lib/trace-ir/event.h b/src/lib/trace-ir/event.h new file mode 100644 index 00000000..308c451e --- /dev/null +++ b/src/lib/trace-ir/event.h @@ -0,0 +1,208 @@ +#ifndef BABELTRACE_TRACE_IR_EVENT_INTERNAL_H +#define BABELTRACE_TRACE_IR_EVENT_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Protection: this file uses BT_LIB_LOG*() macros directly */ +#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H +# error Please include before including this file. +#endif + +#include "lib/assert-pre.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include +#include "lib/object.h" +#include "common/assert.h" +#include + +#include "event-class.h" +#include "field.h" +#include "field-wrapper.h" +#include "packet.h" +#include "stream.h" + +#define BT_ASSERT_PRE_EVENT_HOT(_event) \ + BT_ASSERT_PRE_HOT(((const struct bt_event *) (_event)), \ + "Event", ": %!+e", (_event)) + +struct bt_event { + struct bt_object base; + + /* Owned by this */ + struct bt_event_class *class; + + /* Owned by this */ + struct bt_packet *packet; + + struct bt_field *common_context_field; + struct bt_field *specific_context_field; + struct bt_field *payload_field; + bool frozen; +}; + +BT_HIDDEN +void bt_event_destroy(struct bt_event *event); + +BT_HIDDEN +struct bt_event *bt_event_new(struct bt_event_class *event_class); + +BT_HIDDEN +void _bt_event_set_is_frozen(const struct bt_event *event, bool is_frozen); + +#ifdef BT_DEV_MODE +# define bt_event_set_is_frozen _bt_event_set_is_frozen +#else +# define bt_event_set_is_frozen(_event, _is_frozen) +#endif + +BT_UNUSED +static inline +void _bt_event_reset_dev_mode(struct bt_event *event) +{ + BT_ASSERT(event); + + if (event->common_context_field) { + bt_field_set_is_frozen( + event->common_context_field, false); + bt_field_reset( + event->common_context_field); + } + + if (event->specific_context_field) { + bt_field_set_is_frozen( + event->specific_context_field, false); + bt_field_reset(event->specific_context_field); + } + + if (event->payload_field) { + bt_field_set_is_frozen( + event->payload_field, false); + bt_field_reset(event->payload_field); + } +} + +#ifdef BT_DEV_MODE +# define bt_event_reset_dev_mode _bt_event_reset_dev_mode +#else +# define bt_event_reset_dev_mode(_x) +#endif + +static inline +void bt_event_reset(struct bt_event *event) +{ + BT_ASSERT(event); + BT_LIB_LOGD("Resetting event: %!+e", event); + bt_event_set_is_frozen(event, false); + bt_object_put_no_null_check(&event->packet->base); + event->packet = NULL; +} + +static inline +void bt_event_recycle(struct bt_event *event) +{ + struct bt_event_class *event_class; + + BT_ASSERT(event); + BT_LIB_LOGD("Recycling event: %!+e", event); + + /* + * Those are the important ordered steps: + * + * 1. Reset the event object (put any permanent reference it + * has, unfreeze it and its fields in developer mode, etc.), + * but do NOT put its class's reference. This event class + * contains the pool to which we're about to recycle this + * event object, so we must guarantee its existence thanks + * to this existing reference. + * + * 2. Move the event class reference to our `event_class` + * variable so that we can set the event's class member + * to NULL before recycling it. We CANNOT do this after + * we put the event class reference because this bt_object_put_ref() + * could destroy the event class, also destroying its + * event pool, thus also destroying our event object (this + * would result in an invalid write access). + * + * 3. Recycle the event object. + * + * 4. Put our event class reference. + */ + bt_event_reset(event); + event_class = event->class; + BT_ASSERT(event_class); + event->class = NULL; + bt_object_pool_recycle_object(&event_class->event_pool, event); + bt_object_put_no_null_check(&event_class->base); +} + +static inline +void bt_event_set_packet(struct bt_event *event, struct bt_packet *packet) +{ + BT_ASSERT_PRE_NON_NULL(event, "Event"); + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + BT_ASSERT_PRE_EVENT_HOT(event); + BT_ASSERT_PRE(bt_event_class_borrow_stream_class( + event->class) == packet->stream->class, + "Packet's stream class and event's stream class differ: " + "%![event-]+e, %![packet-]+a", event, packet); + + BT_ASSERT(!event->packet); + event->packet = packet; + bt_object_get_no_null_check_no_parent_check(&event->packet->base); + BT_LIB_LOGV("Set event's packet: %![event-]+e, %![packet-]+a", + event, packet); +} + +static inline +struct bt_event *bt_event_create(struct bt_event_class *event_class, + struct bt_packet *packet) +{ + struct bt_event *event = NULL; + + BT_ASSERT(event_class); + event = bt_object_pool_create_object(&event_class->event_pool); + if (unlikely(!event)) { + BT_LIB_LOGE("Cannot allocate one event from event class's event pool: " + "%![ec-]+E", event_class); + goto end; + } + + if (likely(!event->class)) { + event->class = event_class; + bt_object_get_no_null_check(&event_class->base); + } + + BT_ASSERT(packet); + bt_event_set_packet(event, packet); + goto end; + +end: + return event; +} + +#endif /* BABELTRACE_TRACE_IR_EVENT_INTERNAL_H */ diff --git a/src/lib/trace-ir/field-class.c b/src/lib/trace-ir/field-class.c new file mode 100644 index 00000000..40fe2159 --- /dev/null +++ b/src/lib/trace-ir/field-class.c @@ -0,0 +1,1356 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "FIELD-CLASSES" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" +#include "compat/endian.h" +#include "common/assert.h" +#include "compat/glib.h" +#include +#include +#include + +#include "clock-class.h" +#include "field-class.h" +#include "field.h" +#include "field-path.h" +#include "utils.h" + +enum bt_field_class_type bt_field_class_get_type( + const struct bt_field_class *fc) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + return fc->type; +} + +static +void init_field_class(struct bt_field_class *fc, enum bt_field_class_type type, + bt_object_release_func release_func) +{ + BT_ASSERT(fc); + BT_ASSERT(bt_field_class_has_known_type(fc)); + BT_ASSERT(release_func); + bt_object_init_shared(&fc->base, release_func); + fc->type = type; +} + +static +void init_integer_field_class(struct bt_field_class_integer *fc, + enum bt_field_class_type type, + bt_object_release_func release_func) +{ + init_field_class((void *) fc, type, release_func); + fc->range = 64; + fc->base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; +} + +static +void destroy_integer_field_class(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_LIB_LOGD("Destroying integer field class object: %!+F", obj); + g_free(obj); +} + +static inline +struct bt_field_class *create_integer_field_class(bt_trace_class *trace_class, + enum bt_field_class_type type) +{ + struct bt_field_class_integer *int_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD("Creating default integer field class object: type=%s", + bt_common_field_class_type_string(type)); + int_fc = g_new0(struct bt_field_class_integer, 1); + if (!int_fc) { + BT_LOGE_STR("Failed to allocate one integer field class."); + goto error; + } + + init_integer_field_class(int_fc, type, destroy_integer_field_class); + BT_LIB_LOGD("Created integer field class object: %!+F", int_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(int_fc); + +end: + return (void *) int_fc; +} + +struct bt_field_class *bt_field_class_unsigned_integer_create( + bt_trace_class *trace_class) +{ + return create_integer_field_class(trace_class, + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); +} + +struct bt_field_class *bt_field_class_signed_integer_create( + bt_trace_class *trace_class) +{ + return create_integer_field_class(trace_class, + BT_FIELD_CLASS_TYPE_SIGNED_INTEGER); +} + +uint64_t bt_field_class_integer_get_field_value_range( + const struct bt_field_class *fc) +{ + const struct bt_field_class_integer *int_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); + return int_fc->range; +} + +BT_ASSERT_PRE_FUNC +static +bool size_is_valid_for_enumeration_field_class(struct bt_field_class *fc, + uint64_t size) +{ + // TODO + return true; +} + +void bt_field_class_integer_set_field_value_range( + struct bt_field_class *fc, uint64_t size) +{ + struct bt_field_class_integer *int_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); + BT_ASSERT_PRE_FC_HOT(fc, "Field class"); + BT_ASSERT_PRE(size <= 64, + "Unsupported size for integer field class's field value range " + "(maximum is 64): size=%" PRIu64, size); + BT_ASSERT_PRE( + int_fc->common.type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || + int_fc->common.type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || + size_is_valid_for_enumeration_field_class(fc, size), + "Invalid field value range for enumeration field class: " + "at least one of the current mapping ranges contains values " + "which are outside this range: %!+F, size=%" PRIu64, fc, size); + int_fc->range = size; + BT_LIB_LOGV("Set integer field class's field value range: %!+F", fc); +} + +enum bt_field_class_integer_preferred_display_base +bt_field_class_integer_get_preferred_display_base(const struct bt_field_class *fc) +{ + const struct bt_field_class_integer *int_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); + return int_fc->base; +} + +void bt_field_class_integer_set_preferred_display_base( + struct bt_field_class *fc, + enum bt_field_class_integer_preferred_display_base base) +{ + struct bt_field_class_integer *int_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_INT(fc, "Field class"); + BT_ASSERT_PRE_FC_HOT(fc, "Field class"); + int_fc->base = base; + BT_LIB_LOGV("Set integer field class's preferred display base: %!+F", fc); +} + +static +void finalize_enumeration_field_class_mapping( + struct bt_field_class_enumeration_mapping *mapping) +{ + BT_ASSERT(mapping); + + if (mapping->label) { + g_string_free(mapping->label, TRUE); + } + + if (mapping->ranges) { + g_array_free(mapping->ranges, TRUE); + } +} + +static +void destroy_enumeration_field_class(struct bt_object *obj) +{ + struct bt_field_class_enumeration *fc = (void *) obj; + + BT_ASSERT(fc); + BT_LIB_LOGD("Destroying enumeration field class object: %!+F", fc); + + if (fc->mappings) { + uint64_t i; + + for (i = 0; i < fc->mappings->len; i++) { + finalize_enumeration_field_class_mapping( + BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, i)); + } + + g_array_free(fc->mappings, TRUE); + fc->mappings = NULL; + } + + if (fc->label_buf) { + g_ptr_array_free(fc->label_buf, TRUE); + fc->label_buf = NULL; + } + + g_free(fc); +} + +static +struct bt_field_class *create_enumeration_field_class( + bt_trace_class *trace_class, enum bt_field_class_type type) +{ + struct bt_field_class_enumeration *enum_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD("Creating default enumeration field class object: type=%s", + bt_common_field_class_type_string(type)); + enum_fc = g_new0(struct bt_field_class_enumeration, 1); + if (!enum_fc) { + BT_LOGE_STR("Failed to allocate one enumeration field class."); + goto error; + } + + init_integer_field_class((void *) enum_fc, type, + destroy_enumeration_field_class); + enum_fc->mappings = g_array_new(FALSE, TRUE, + sizeof(struct bt_field_class_enumeration_mapping)); + if (!enum_fc->mappings) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + enum_fc->label_buf = g_ptr_array_new(); + if (!enum_fc->label_buf) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + BT_LIB_LOGD("Created enumeration field class object: %!+F", enum_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(enum_fc); + +end: + return (void *) enum_fc; +} + +struct bt_field_class *bt_field_class_unsigned_enumeration_create( + bt_trace_class *trace_class) +{ + return create_enumeration_field_class(trace_class, + BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION); +} + +struct bt_field_class *bt_field_class_signed_enumeration_create( + bt_trace_class *trace_class) +{ + return create_enumeration_field_class(trace_class, + BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); +} + +uint64_t bt_field_class_enumeration_get_mapping_count( + const struct bt_field_class *fc) +{ + const struct bt_field_class_enumeration *enum_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_ENUM(fc, "Field class"); + return (uint64_t) enum_fc->mappings->len; +} + +const struct bt_field_class_unsigned_enumeration_mapping * +bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( + const struct bt_field_class *fc, uint64_t index) +{ + const struct bt_field_class_enumeration *enum_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_VALID_INDEX(index, enum_fc->mappings->len); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, + "Field class"); + return (const void *) BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, index); +} + +const struct bt_field_class_signed_enumeration_mapping * +bt_field_class_signed_enumeration_borrow_mapping_by_index_const( + const struct bt_field_class *fc, uint64_t index) +{ + const struct bt_field_class_enumeration *enum_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_VALID_INDEX(index, enum_fc->mappings->len); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, + "Field class"); + return (const void *) BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(fc, index); +} + +const char *bt_field_class_enumeration_mapping_get_label( + const struct bt_field_class_enumeration_mapping *mapping) +{ + BT_ASSERT_PRE_NON_NULL(mapping, "Enumeration field class mapping"); + return mapping->label->str; +} + +uint64_t bt_field_class_enumeration_mapping_get_range_count( + const struct bt_field_class_enumeration_mapping *mapping) +{ + BT_ASSERT_PRE_NON_NULL(mapping, "Enumeration field class mapping"); + return (uint64_t) mapping->ranges->len; +} + +static inline +void get_enumeration_field_class_mapping_range_at_index( + const struct bt_field_class_enumeration_mapping *mapping, + uint64_t index, uint64_t *lower, uint64_t *upper) +{ + const struct bt_field_class_enumeration_mapping_range *range; + + BT_ASSERT_PRE_NON_NULL(mapping, "Ranges"); + BT_ASSERT_PRE_NON_NULL(lower, "Range's lower (output)"); + BT_ASSERT_PRE_NON_NULL(upper, "Range's upper (output)"); + BT_ASSERT_PRE_VALID_INDEX(index, mapping->ranges->len); + range = BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(mapping, index); + *lower = range->lower.u; + *upper = range->upper.u; +} + +void bt_field_class_unsigned_enumeration_mapping_get_range_by_index( + const struct bt_field_class_unsigned_enumeration_mapping *ranges, + uint64_t index, uint64_t *lower, uint64_t *upper) +{ + get_enumeration_field_class_mapping_range_at_index( + (const void *) ranges, index, lower, upper); +} + +void bt_field_class_signed_enumeration_mapping_get_range_by_index( + const struct bt_field_class_signed_enumeration_mapping *ranges, + uint64_t index, int64_t *lower, int64_t *upper) +{ + get_enumeration_field_class_mapping_range_at_index( + (const void *) ranges, index, + (uint64_t *) lower, (uint64_t *) upper); +} + +enum bt_field_class_status +bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( + const struct bt_field_class *fc, uint64_t value, + bt_field_class_enumeration_mapping_label_array *label_array, + uint64_t *count) +{ + const struct bt_field_class_enumeration *enum_fc = (const void *) fc; + uint64_t i; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); + BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, + "Field class"); + g_ptr_array_set_size(enum_fc->label_buf, 0); + + for (i = 0; i < enum_fc->mappings->len; i++) { + uint64_t j; + const struct bt_field_class_enumeration_mapping *mapping = + BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); + + for (j = 0; j < mapping->ranges->len; j++) { + const struct bt_field_class_enumeration_mapping_range *range = + BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX( + mapping, j); + + if (value >= range->lower.u && + value <= range->upper.u) { + g_ptr_array_add(enum_fc->label_buf, + mapping->label->str); + break; + } + } + } + + *label_array = (void *) enum_fc->label_buf->pdata; + *count = (uint64_t) enum_fc->label_buf->len; + return BT_FIELD_CLASS_STATUS_OK; +} + +enum bt_field_class_status +bt_field_class_signed_enumeration_get_mapping_labels_by_value( + const struct bt_field_class *fc, int64_t value, + bt_field_class_enumeration_mapping_label_array *label_array, + uint64_t *count) +{ + const struct bt_field_class_enumeration *enum_fc = (const void *) fc; + uint64_t i; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); + BT_ASSERT_PRE_NON_NULL(count, "Count (output)"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, + "Field class"); + g_ptr_array_set_size(enum_fc->label_buf, 0); + + for (i = 0; i < enum_fc->mappings->len; i++) { + uint64_t j; + const struct bt_field_class_enumeration_mapping *mapping = + BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); + + for (j = 0; j < mapping->ranges->len; j++) { + const struct bt_field_class_enumeration_mapping_range *range = + BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX( + mapping, j); + + if (value >= range->lower.i && + value <= range->upper.i) { + g_ptr_array_add(enum_fc->label_buf, + mapping->label->str); + break; + } + } + } + + *label_array = (void *) enum_fc->label_buf->pdata; + *count = (uint64_t) enum_fc->label_buf->len; + return BT_FIELD_CLASS_STATUS_OK; +} + +static inline +enum bt_field_class_status add_mapping_to_enumeration_field_class( + struct bt_field_class *fc, + const char *label, uint64_t lower, uint64_t upper) +{ + int ret = BT_FIELD_CLASS_STATUS_OK; + uint64_t i; + struct bt_field_class_enumeration *enum_fc = (void *) fc; + struct bt_field_class_enumeration_mapping *mapping = NULL; + struct bt_field_class_enumeration_mapping_range *range; + + BT_ASSERT(fc); + BT_ASSERT_PRE_NON_NULL(label, "Label"); + + /* Find existing mapping identified by this label */ + for (i = 0; i < enum_fc->mappings->len; i++) { + struct bt_field_class_enumeration_mapping *mapping_candidate = + BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, i); + + if (strcmp(mapping_candidate->label->str, label) == 0) { + mapping = mapping_candidate; + break; + } + } + + if (!mapping) { + /* Create new mapping for this label */ + g_array_set_size(enum_fc->mappings, enum_fc->mappings->len + 1); + mapping = BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(enum_fc, + enum_fc->mappings->len - 1); + mapping->ranges = g_array_new(FALSE, TRUE, + sizeof(struct bt_field_class_enumeration_mapping_range)); + if (!mapping->ranges) { + finalize_enumeration_field_class_mapping(mapping); + g_array_set_size(enum_fc->mappings, + enum_fc->mappings->len - 1); + ret = BT_FIELD_CLASS_STATUS_NOMEM; + goto end; + } + + mapping->label = g_string_new(label); + if (!mapping->label) { + finalize_enumeration_field_class_mapping(mapping); + g_array_set_size(enum_fc->mappings, + enum_fc->mappings->len - 1); + ret = BT_FIELD_CLASS_STATUS_NOMEM; + goto end; + } + } + + /* Add range */ + BT_ASSERT(mapping); + g_array_set_size(mapping->ranges, mapping->ranges->len + 1); + range = BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(mapping, + mapping->ranges->len - 1); + range->lower.u = lower; + range->upper.u = upper; + BT_LIB_LOGV("Added mapping to enumeration field class: " + "%![fc-]+F, label=\"%s\", lower-unsigned=%" PRIu64 ", " + "upper-unsigned=%" PRIu64, fc, label, lower, upper); + +end: + return ret; +} + +enum bt_field_class_status bt_field_class_unsigned_enumeration_map_range( + struct bt_field_class *fc, const char *label, + uint64_t range_lower, uint64_t range_upper) +{ + struct bt_field_class_enumeration *enum_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, + "Field class"); + BT_ASSERT_PRE(range_lower <= range_upper, + "Range's upper bound is less than lower bound: " + "upper=%" PRIu64 ", lower=%" PRIu64, + range_lower, range_upper); + BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned(enum_fc->common.range, + range_lower), + "Range's lower bound is outside the enumeration field class's value range: " + "%![fc-]+F, lower=%" PRIu64, fc, range_lower); + BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned(enum_fc->common.range, + range_upper), + "Range's upper bound is outside the enumeration field class's value range: " + "%![fc-]+F, upper=%" PRIu64, fc, range_upper); + return add_mapping_to_enumeration_field_class(fc, label, range_lower, + range_upper); +} + +enum bt_field_class_status bt_field_class_signed_enumeration_map_range( + struct bt_field_class *fc, const char *label, + int64_t range_lower, int64_t range_upper) +{ + struct bt_field_class_enumeration *enum_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, + "Field class"); + BT_ASSERT_PRE(range_lower <= range_upper, + "Range's upper bound is less than lower bound: " + "upper=%" PRId64 ", lower=%" PRId64, + range_lower, range_upper); + BT_ASSERT_PRE(bt_util_value_is_in_range_signed(enum_fc->common.range, + range_lower), + "Range's lower bound is outside the enumeration field class's value range: " + "%![fc-]+F, lower=%" PRId64, fc, range_lower); + BT_ASSERT_PRE(bt_util_value_is_in_range_signed(enum_fc->common.range, + range_upper), + "Range's upper bound is outside the enumeration field class's value range: " + "%![fc-]+F, upper=%" PRId64, fc, range_upper); + return add_mapping_to_enumeration_field_class(fc, label, range_lower, + range_upper); +} + +static +void destroy_real_field_class(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_LIB_LOGD("Destroying real field class object: %!+F", obj); + g_free(obj); +} + +struct bt_field_class *bt_field_class_real_create(bt_trace_class *trace_class) +{ + struct bt_field_class_real *real_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD_STR("Creating default real field class object."); + real_fc = g_new0(struct bt_field_class_real, 1); + if (!real_fc) { + BT_LOGE_STR("Failed to allocate one real field class."); + goto error; + } + + init_field_class((void *) real_fc, BT_FIELD_CLASS_TYPE_REAL, + destroy_real_field_class); + BT_LIB_LOGD("Created real field class object: %!+F", real_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(real_fc); + +end: + return (void *) real_fc; +} + +bt_bool bt_field_class_real_is_single_precision(const struct bt_field_class *fc) +{ + const struct bt_field_class_real *real_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_REAL, "Field class"); + return real_fc->is_single_precision; +} + +void bt_field_class_real_set_is_single_precision(struct bt_field_class *fc, + bt_bool is_single_precision) +{ + struct bt_field_class_real *real_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_REAL, "Field class"); + BT_ASSERT_PRE_FC_HOT(fc, "Field class"); + real_fc->is_single_precision = (bool) is_single_precision; + BT_LIB_LOGV("Set real field class's \"is single precision\" property: " + "%!+F", fc); +} + +static +int init_named_field_classes_container( + struct bt_field_class_named_field_class_container *fc, + enum bt_field_class_type type, + bt_object_release_func release_func) +{ + int ret = 0; + + init_field_class((void *) fc, type, release_func); + fc->named_fcs = g_array_new(FALSE, TRUE, + sizeof(struct bt_named_field_class)); + if (!fc->named_fcs) { + BT_LOGE_STR("Failed to allocate a GArray."); + ret = -1; + goto end; + } + + fc->name_to_index = g_hash_table_new(g_str_hash, g_str_equal); + if (!fc->name_to_index) { + BT_LOGE_STR("Failed to allocate a GHashTable."); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +void finalize_named_field_class(struct bt_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + BT_LIB_LOGD("Finalizing named field class: " + "addr=%p, name=\"%s\", %![fc-]+F", + named_fc, named_fc->name ? named_fc->name->str : NULL, + named_fc->fc); + + if (named_fc->name) { + g_string_free(named_fc->name, TRUE); + } + + BT_LOGD_STR("Putting named field class's field class."); + BT_OBJECT_PUT_REF_AND_RESET(named_fc->fc); +} + +static +void finalize_named_field_classes_container( + struct bt_field_class_named_field_class_container *fc) +{ + uint64_t i; + + BT_ASSERT(fc); + + if (fc->named_fcs) { + for (i = 0; i < fc->named_fcs->len; i++) { + finalize_named_field_class( + &g_array_index(fc->named_fcs, + struct bt_named_field_class, i)); + } + + g_array_free(fc->named_fcs, TRUE); + } + + if (fc->name_to_index) { + g_hash_table_destroy(fc->name_to_index); + } +} + +static +void destroy_structure_field_class(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_LIB_LOGD("Destroying structure field class object: %!+F", obj); + finalize_named_field_classes_container((void *) obj); + g_free(obj); +} + +struct bt_field_class *bt_field_class_structure_create( + bt_trace_class *trace_class) +{ + int ret; + struct bt_field_class_structure *struct_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD_STR("Creating default structure field class object."); + struct_fc = g_new0(struct bt_field_class_structure, 1); + if (!struct_fc) { + BT_LOGE_STR("Failed to allocate one structure field class."); + goto error; + } + + ret = init_named_field_classes_container((void *) struct_fc, + BT_FIELD_CLASS_TYPE_STRUCTURE, destroy_structure_field_class); + if (ret) { + goto error; + } + + BT_LIB_LOGD("Created structure field class object: %!+F", struct_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(struct_fc); + +end: + return (void *) struct_fc; +} + +static +enum bt_field_class_status append_named_field_class_to_container_field_class( + struct bt_field_class_named_field_class_container *container_fc, + const char *name, struct bt_field_class *fc) +{ + int ret = BT_FIELD_CLASS_STATUS_OK; + struct bt_named_field_class *named_fc; + GString *name_str; + + BT_ASSERT(container_fc); + BT_ASSERT_PRE_FC_HOT(container_fc, "Field class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE(!bt_g_hash_table_contains(container_fc->name_to_index, + name), + "Duplicate member/option name in structure/variant field class: " + "%![container-fc-]+F, name=\"%s\"", container_fc, name); + name_str = g_string_new(name); + if (!name_str) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = BT_FIELD_CLASS_STATUS_NOMEM; + goto end; + } + + g_array_set_size(container_fc->named_fcs, + container_fc->named_fcs->len + 1); + named_fc = &g_array_index(container_fc->named_fcs, + struct bt_named_field_class, container_fc->named_fcs->len - 1); + named_fc->name = name_str; + named_fc->fc = fc; + bt_object_get_no_null_check(fc); + g_hash_table_insert(container_fc->name_to_index, named_fc->name->str, + GUINT_TO_POINTER(container_fc->named_fcs->len - 1)); + + /* + * Freeze the field class, but not the named field class (the + * user can still modify it, if possible, until the container + * itself is frozen). + */ + bt_field_class_freeze(fc); + +end: + return ret; +} + +enum bt_field_class_status bt_field_class_structure_append_member( + struct bt_field_class *fc, const char *name, + struct bt_field_class *member_fc) +{ + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return append_named_field_class_to_container_field_class((void *) fc, + name, member_fc); +} + +uint64_t bt_field_class_structure_get_member_count( + const struct bt_field_class *fc) +{ + struct bt_field_class_structure *struct_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return (uint64_t) struct_fc->common.named_fcs->len; +} + +static +struct bt_named_field_class * +borrow_named_field_class_from_container_field_class_at_index( + struct bt_field_class_named_field_class_container *fc, + uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT_PRE_VALID_INDEX(index, fc->named_fcs->len); + return BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, index); +} + +const struct bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_index_const( + const struct bt_field_class *fc, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return (const void *) + borrow_named_field_class_from_container_field_class_at_index( + (void *) fc, index); +} + +struct bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_index( + struct bt_field_class *fc, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return (void *) + borrow_named_field_class_from_container_field_class_at_index( + (void *) fc, index); +} + +static +struct bt_named_field_class * +borrow_named_field_class_from_container_field_class_by_name( + struct bt_field_class_named_field_class_container *fc, + const char *name) +{ + struct bt_named_field_class *named_fc = NULL; + gpointer orig_key; + gpointer value; + + BT_ASSERT(fc); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + if (!g_hash_table_lookup_extended(fc->name_to_index, name, &orig_key, + &value)) { + goto end; + } + + named_fc = BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, + GPOINTER_TO_UINT(value)); + +end: + return named_fc; +} + +const struct bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_name_const( + const struct bt_field_class *fc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return (const void *) + borrow_named_field_class_from_container_field_class_by_name( + (void *) fc, name); +} + +struct bt_field_class_structure_member * +bt_field_class_structure_borrow_member_by_name( + struct bt_field_class *fc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STRUCTURE, + "Field class"); + return (void *) + borrow_named_field_class_from_container_field_class_by_name( + (void *) fc, name); +} + +const char *bt_field_class_structure_member_get_name( + const struct bt_field_class_structure_member *member) +{ + const struct bt_named_field_class *named_fc = (const void *) member; + + BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); + return named_fc->name->str; +} + +const struct bt_field_class * +bt_field_class_structure_member_borrow_field_class_const( + const struct bt_field_class_structure_member *member) +{ + const struct bt_named_field_class *named_fc = (const void *) member; + + BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); + return named_fc->fc; +} + +struct bt_field_class * +bt_field_class_structure_member_borrow_field_class( + struct bt_field_class_structure_member *member) +{ + struct bt_named_field_class *named_fc = (void *) member; + + BT_ASSERT_PRE_NON_NULL(member, "Structure field class member"); + return named_fc->fc; +} + +static +void destroy_variant_field_class(struct bt_object *obj) +{ + struct bt_field_class_variant *fc = (void *) obj; + + BT_ASSERT(fc); + BT_LIB_LOGD("Destroying variant field class object: %!+F", fc); + finalize_named_field_classes_container((void *) fc); + BT_LOGD_STR("Putting selector field path."); + BT_OBJECT_PUT_REF_AND_RESET(fc->selector_field_path); + BT_LOGD_STR("Putting selector field class."); + BT_OBJECT_PUT_REF_AND_RESET(fc->selector_fc); + g_free(fc); +} + +struct bt_field_class *bt_field_class_variant_create( + bt_trace_class *trace_class) +{ + int ret; + struct bt_field_class_variant *var_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD_STR("Creating default variant field class object."); + var_fc = g_new0(struct bt_field_class_variant, 1); + if (!var_fc) { + BT_LOGE_STR("Failed to allocate one variant field class."); + goto error; + } + + ret = init_named_field_classes_container((void *) var_fc, + BT_FIELD_CLASS_TYPE_VARIANT, destroy_variant_field_class); + if (ret) { + goto error; + } + + BT_LIB_LOGD("Created variant field class object: %!+F", var_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(var_fc); + +end: + return (void *) var_fc; +} + +enum bt_field_class_status bt_field_class_variant_set_selector_field_class( + struct bt_field_class *fc, + struct bt_field_class *selector_fc) +{ + struct bt_field_class_variant *var_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Variant field class"); + BT_ASSERT_PRE_NON_NULL(selector_fc, "Selector field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + BT_ASSERT_PRE_FC_IS_ENUM(selector_fc, "Selector field class"); + BT_ASSERT_PRE_FC_HOT(fc, "Variant field class"); + var_fc->selector_fc = selector_fc; + bt_object_get_no_null_check(selector_fc); + bt_field_class_freeze(selector_fc); + return BT_FIELD_CLASS_STATUS_OK; +} + +enum bt_field_class_status bt_field_class_variant_append_option( + struct bt_field_class *fc, + const char *name, struct bt_field_class *option_fc) +{ + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return append_named_field_class_to_container_field_class((void *) fc, + name, option_fc); +} + +const struct bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_name_const( + const struct bt_field_class *fc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return (const void *) + borrow_named_field_class_from_container_field_class_by_name( + (void *) fc, name); +} + +struct bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_name( + struct bt_field_class *fc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return (void *) + borrow_named_field_class_from_container_field_class_by_name( + (void *) fc, name); +} + +uint64_t bt_field_class_variant_get_option_count(const struct bt_field_class *fc) +{ + const struct bt_field_class_variant *var_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return (uint64_t) var_fc->common.named_fcs->len; +} + +const struct bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_index_const( + const struct bt_field_class *fc, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return (const void *) + borrow_named_field_class_from_container_field_class_at_index( + (void *) fc, index); +} + +struct bt_field_class_variant_option * +bt_field_class_variant_borrow_option_by_index( + struct bt_field_class *fc, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, "Field class"); + return (void *) + borrow_named_field_class_from_container_field_class_at_index( + (void *) fc, index); +} + +const char *bt_field_class_variant_option_get_name( + const struct bt_field_class_variant_option *option) +{ + const struct bt_named_field_class *named_fc = (const void *) option; + + BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); + return named_fc->name->str; +} + +const struct bt_field_class * +bt_field_class_variant_option_borrow_field_class_const( + const struct bt_field_class_variant_option *option) +{ + const struct bt_named_field_class *named_fc = (const void *) option; + + BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); + return named_fc->fc; +} + +struct bt_field_class * +bt_field_class_variant_option_borrow_field_class( + struct bt_field_class_variant_option *option) +{ + struct bt_named_field_class *named_fc = (void *) option; + + BT_ASSERT_PRE_NON_NULL(option, "Variant field class option"); + return named_fc->fc; +} + +const struct bt_field_path * +bt_field_class_variant_borrow_selector_field_path_const( + const struct bt_field_class *fc) +{ + const struct bt_field_class_variant *var_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_VARIANT, + "Field class"); + return var_fc->selector_field_path; +} + +static +void init_array_field_class(struct bt_field_class_array *fc, + enum bt_field_class_type type, bt_object_release_func release_func, + struct bt_field_class *element_fc) +{ + BT_ASSERT(element_fc); + init_field_class((void *) fc, type, release_func); + fc->element_fc = element_fc; + bt_object_get_no_null_check(element_fc); + bt_field_class_freeze(element_fc); +} + +static +void finalize_array_field_class(struct bt_field_class_array *array_fc) +{ + BT_ASSERT(array_fc); + BT_LOGD_STR("Putting element field class."); + BT_OBJECT_PUT_REF_AND_RESET(array_fc->element_fc); +} + +static +void destroy_static_array_field_class(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_LIB_LOGD("Destroying static array field class object: %!+F", obj); + finalize_array_field_class((void *) obj); + g_free(obj); +} + +struct bt_field_class * +bt_field_class_static_array_create(bt_trace_class *trace_class, + struct bt_field_class *element_fc, uint64_t length) +{ + struct bt_field_class_static_array *array_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_ASSERT_PRE_NON_NULL(element_fc, "Element field class"); + BT_LOGD_STR("Creating default static array field class object."); + array_fc = g_new0(struct bt_field_class_static_array, 1); + if (!array_fc) { + BT_LOGE_STR("Failed to allocate one static array field class."); + goto error; + } + + init_array_field_class((void *) array_fc, BT_FIELD_CLASS_TYPE_STATIC_ARRAY, + destroy_static_array_field_class, element_fc); + array_fc->length = length; + BT_LIB_LOGD("Created static array field class object: %!+F", array_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(array_fc); + +end: + return (void *) array_fc; +} + +const struct bt_field_class * +bt_field_class_array_borrow_element_field_class_const( + const struct bt_field_class *fc) +{ + const struct bt_field_class_array *array_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_ARRAY(fc, "Field class"); + return array_fc->element_fc; +} + +struct bt_field_class * +bt_field_class_array_borrow_element_field_class(struct bt_field_class *fc) +{ + struct bt_field_class_array *array_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_IS_ARRAY(fc, "Field class"); + return array_fc->element_fc; +} + +uint64_t bt_field_class_static_array_get_length(const struct bt_field_class *fc) +{ + const struct bt_field_class_static_array *array_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_STATIC_ARRAY, + "Field class"); + return (uint64_t) array_fc->length; +} + +static +void destroy_dynamic_array_field_class(struct bt_object *obj) +{ + struct bt_field_class_dynamic_array *fc = (void *) obj; + + BT_ASSERT(fc); + BT_LIB_LOGD("Destroying dynamic array field class object: %!+F", fc); + finalize_array_field_class((void *) fc); + BT_LOGD_STR("Putting length field path."); + BT_OBJECT_PUT_REF_AND_RESET(fc->length_field_path); + BT_LOGD_STR("Putting length field class."); + BT_OBJECT_PUT_REF_AND_RESET(fc->length_fc); + g_free(fc); +} + +struct bt_field_class *bt_field_class_dynamic_array_create( + bt_trace_class *trace_class, + struct bt_field_class *element_fc) +{ + struct bt_field_class_dynamic_array *array_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_ASSERT_PRE_NON_NULL(element_fc, "Element field class"); + BT_LOGD_STR("Creating default dynamic array field class object."); + array_fc = g_new0(struct bt_field_class_dynamic_array, 1); + if (!array_fc) { + BT_LOGE_STR("Failed to allocate one dynamic array field class."); + goto error; + } + + init_array_field_class((void *) array_fc, + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, + destroy_dynamic_array_field_class, element_fc); + BT_LIB_LOGD("Created dynamic array field class object: %!+F", array_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(array_fc); + +end: + return (void *) array_fc; +} + +enum bt_field_class_status bt_field_class_dynamic_array_set_length_field_class( + struct bt_field_class *fc, + struct bt_field_class *length_fc) +{ + struct bt_field_class_dynamic_array *array_fc = (void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Dynamic array field class"); + BT_ASSERT_PRE_NON_NULL(length_fc, "Length field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, + "Field class"); + BT_ASSERT_PRE_FC_IS_UNSIGNED_INT(length_fc, "Length field class"); + BT_ASSERT_PRE_FC_HOT(fc, "Dynamic array field class"); + array_fc->length_fc = length_fc; + bt_object_get_no_null_check(length_fc); + bt_field_class_freeze(length_fc); + return BT_FIELD_CLASS_STATUS_OK; +} + +const struct bt_field_path * +bt_field_class_dynamic_array_borrow_length_field_path_const( + const struct bt_field_class *fc) +{ + const struct bt_field_class_dynamic_array *seq_fc = (const void *) fc; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT_PRE_FC_HAS_ID(fc, BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, + "Field class"); + return seq_fc->length_field_path; +} + +static +void destroy_string_field_class(struct bt_object *obj) +{ + BT_ASSERT(obj); + BT_LIB_LOGD("Destroying string field class object: %!+F", obj); + g_free(obj); +} + +struct bt_field_class *bt_field_class_string_create(bt_trace_class *trace_class) +{ + struct bt_field_class_string *string_fc = NULL; + + BT_ASSERT_PRE_NON_NULL(trace_class, "Trace class"); + BT_LOGD_STR("Creating default string field class object."); + string_fc = g_new0(struct bt_field_class_string, 1); + if (!string_fc) { + BT_LOGE_STR("Failed to allocate one string field class."); + goto error; + } + + init_field_class((void *) string_fc, BT_FIELD_CLASS_TYPE_STRING, + destroy_string_field_class); + BT_LIB_LOGD("Created string field class object: %!+F", string_fc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(string_fc); + +end: + return (void *) string_fc; +} + +BT_HIDDEN +void _bt_field_class_freeze(const struct bt_field_class *c_fc) +{ + struct bt_field_class *fc = (void *) c_fc; + + /* + * Element/member/option field classes are frozen when added to + * their owner. + */ + BT_ASSERT(fc); + fc->frozen = true; + + switch (fc->type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_field_class_named_field_class_container *container_fc = + (void *) fc; + uint64_t i; + + for (i = 0; i < container_fc->named_fcs->len; i++) { + struct bt_named_field_class *named_fc = + BT_FIELD_CLASS_NAMED_FC_AT_INDEX( + container_fc, i); + + bt_named_field_class_freeze(named_fc); + } + + break; + } + default: + break; + } +} + +BT_HIDDEN +void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + ((struct bt_named_field_class *) named_fc)->frozen = true; + bt_field_class_freeze(named_fc->fc); +} + +BT_HIDDEN +void _bt_field_class_make_part_of_trace_class(const struct bt_field_class *c_fc) +{ + struct bt_field_class *fc = (void *) c_fc; + + BT_ASSERT(fc); + BT_ASSERT_PRE(!fc->part_of_trace_class, + "Field class is already part of a trace: %!+F", fc); + fc->part_of_trace_class = true; + + switch (fc->type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_field_class_named_field_class_container *container_fc = + (void *) fc; + uint64_t i; + + for (i = 0; i < container_fc->named_fcs->len; i++) { + struct bt_named_field_class *named_fc = + BT_FIELD_CLASS_NAMED_FC_AT_INDEX( + container_fc, i); + + bt_field_class_make_part_of_trace_class(named_fc->fc); + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + struct bt_field_class_array *array_fc = (void *) fc; + + bt_field_class_make_part_of_trace_class(array_fc->element_fc); + break; + } + default: + break; + } +} + +void bt_field_class_get_ref(const struct bt_field_class *field_class) +{ + bt_object_get_ref(field_class); +} + +void bt_field_class_put_ref(const struct bt_field_class *field_class) +{ + bt_object_put_ref(field_class); +} diff --git a/src/lib/trace-ir/field-class.h b/src/lib/trace-ir/field-class.h new file mode 100644 index 00000000..b5692dae --- /dev/null +++ b/src/lib/trace-ir/field-class.h @@ -0,0 +1,270 @@ +#ifndef BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H +#define BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/assert-pre.h" +#include +#include +#include "common/babeltrace.h" +#include "lib/object.h" +#include +#include +#include + +#define BT_ASSERT_PRE_FC_IS_INT(_fc, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ + _name " is not an integer field class: %![fc-]+F", (_fc)) + +#define BT_ASSERT_PRE_FC_IS_UNSIGNED_INT(_fc, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, \ + _name " is not an unsigned integer field class: %![fc-]+F", (_fc)) + +#define BT_ASSERT_PRE_FC_IS_ENUM(_fc, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ + _name " is not an enumeration field class: %![fc-]+F", (_fc)) + +#define BT_ASSERT_PRE_FC_IS_ARRAY(_fc, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || \ + ((const struct bt_field_class *) (_fc))->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, \ + _name " is not an array field class: %![fc-]+F", (_fc)) + +#define BT_ASSERT_PRE_FC_HAS_ID(_fc, _type, _name) \ + BT_ASSERT_PRE(((const struct bt_field_class *) (_fc))->type == (_type), \ + _name " has the wrong type: expected-type=%s, " \ + "%![fc-]+F", bt_common_field_class_type_string(_type), (_fc)) + +#define BT_ASSERT_PRE_FC_HOT(_fc, _name) \ + BT_ASSERT_PRE_HOT((const struct bt_field_class *) (_fc), \ + (_name), ": %!+F", (_fc)) + +#define BT_FIELD_CLASS_NAMED_FC_AT_INDEX(_fc, _index) \ + (&g_array_index(((struct bt_field_class_named_field_class_container *) (_fc))->named_fcs, \ + struct bt_named_field_class, (_index))) + +#define BT_FIELD_CLASS_ENUM_MAPPING_AT_INDEX(_fc, _index) \ + (&g_array_index(((struct bt_field_class_enumeration *) (_fc))->mappings, \ + struct bt_field_class_enumeration_mapping, (_index))) + +#define BT_FIELD_CLASS_ENUM_MAPPING_RANGE_AT_INDEX(_mapping, _index) \ + (&g_array_index((_mapping)->ranges, \ + struct bt_field_class_enumeration_mapping_range, (_index))) + +struct bt_field; +struct bt_field_class; + +struct bt_field_class { + struct bt_object base; + enum bt_field_class_type type; + bool frozen; + + /* + * Only used in developer mode, this flag indicates whether or + * not this field class is part of a trace class. + */ + bool part_of_trace_class; +}; + +struct bt_field_class_integer { + struct bt_field_class common; + + /* + * Value range of fields built from this integer field class: + * this is an equivalent integer size in bits. More formally, + * `range` is `n` in: + * + * Unsigned range: [0, 2^n - 1] + * Signed range: [-2^(n - 1), 2^(n - 1) - 1] + */ + uint64_t range; + + enum bt_field_class_integer_preferred_display_base base; +}; + +struct bt_field_class_enumeration_mapping_range { + union { + uint64_t u; + int64_t i; + } lower; + + union { + uint64_t u; + int64_t i; + } upper; +}; + +struct bt_field_class_enumeration_mapping { + GString *label; + + /* Array of `struct bt_field_class_enumeration_mapping_range` */ + GArray *ranges; +}; + +struct bt_field_class_unsigned_enumeration_mapping; +struct bt_field_class_signed_enumeration_mapping; + +struct bt_field_class_enumeration { + struct bt_field_class_integer common; + + /* Array of `struct bt_field_class_enumeration_mapping *` */ + GArray *mappings; + + /* + * This is an array of `const char *` which acts as a temporary + * (potentially growing) buffer for + * bt_field_class_unsigned_enumeration_get_mapping_labels_by_value() + * and + * bt_field_class_signed_enumeration_get_mapping_labels_by_value(). + * + * The actual strings are owned by the mappings above. + */ + GPtrArray *label_buf; +}; + +struct bt_field_class_real { + struct bt_field_class common; + bool is_single_precision; +}; + +struct bt_field_class_string { + struct bt_field_class common; +}; + +/* A named field class is a (name, field class) pair */ +struct bt_named_field_class { + GString *name; + + /* Owned by this */ + struct bt_field_class *fc; + + bool frozen; +}; + +struct bt_field_class_structure_member; +struct bt_field_class_variant_option; + +/* + * This is the base field class for a container of named field classes. + * Structure and variant field classes inherit this. + */ +struct bt_field_class_named_field_class_container { + struct bt_field_class common; + + /* + * Key: `const char *`, not owned by this (owned by named field + * type objects contained in `named_fcs` below). + */ + GHashTable *name_to_index; + + /* Array of `struct bt_named_field_class` */ + GArray *named_fcs; +}; + +struct bt_field_class_structure { + struct bt_field_class_named_field_class_container common; +}; + +struct bt_field_class_array { + struct bt_field_class common; + + /* Owned by this */ + struct bt_field_class *element_fc; +}; + +struct bt_field_class_static_array { + struct bt_field_class_array common; + uint64_t length; +}; + +struct bt_field_class_dynamic_array { + struct bt_field_class_array common; + + /* Weak: never dereferenced, only use to find it elsewhere */ + struct bt_field_class *length_fc; + + /* Owned by this */ + struct bt_field_path *length_field_path; +}; + +struct bt_field_class_variant { + struct bt_field_class_named_field_class_container common; + + /* Weak: never dereferenced, only use to find it elsewhere */ + struct bt_field_class *selector_fc; + + /* Owned by this */ + struct bt_field_path *selector_field_path; +}; + +static inline +bool bt_field_class_has_known_type(const struct bt_field_class *fc) +{ + return fc->type >= BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER && + fc->type <= BT_FIELD_CLASS_TYPE_VARIANT; +} + +BT_HIDDEN +void _bt_field_class_freeze(const struct bt_field_class *field_class); + +#ifdef BT_DEV_MODE +# define bt_field_class_freeze _bt_field_class_freeze +#else +# define bt_field_class_freeze(_fc) ((void) _fc) +#endif + +BT_HIDDEN +void _bt_named_field_class_freeze(const struct bt_named_field_class *named_fc); + +#ifdef BT_DEV_MODE +# define bt_named_field_class_freeze _bt_named_field_class_freeze +#else +# define bt_named_field_class_freeze(_named_fc) ((void) _named_fc) +#endif + +/* + * This function recursively marks `field_class` and its children as + * being part of a trace. This is used to validate that all field classes + * are used at a single location within trace objects even if they are + * shared objects for other purposes. + */ +BT_HIDDEN +void _bt_field_class_make_part_of_trace_class( + const struct bt_field_class *field_class); + +#ifdef BT_DEV_MODE +# define bt_field_class_make_part_of_trace_class _bt_field_class_make_part_of_trace_class +#else +# define bt_field_class_make_part_of_trace_class(_fc) ((void) _fc) +#endif + +#endif /* BABELTRACE_TRACE_IR_FIELD_CLASSES_INTERNAL_H */ diff --git a/src/lib/trace-ir/field-path.c b/src/lib/trace-ir/field-path.c new file mode 100644 index 00000000..d43cc5b1 --- /dev/null +++ b/src/lib/trace-ir/field-path.c @@ -0,0 +1,129 @@ +/* + * Copyright 2016-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "FIELD-PATH" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "common/assert.h" +#include + +#include "field-class.h" +#include "field-path.h" + +static +void destroy_field_path(struct bt_object *obj) +{ + struct bt_field_path *field_path = (struct bt_field_path *) obj; + + BT_ASSERT(field_path); + BT_LIB_LOGD("Destroying field path: %!+P", field_path); + g_array_free(field_path->items, TRUE); + field_path->items = NULL; + g_free(field_path); +} + +BT_HIDDEN +struct bt_field_path *bt_field_path_create(void) +{ + struct bt_field_path *field_path = NULL; + + BT_LOGD_STR("Creating empty field path object."); + + field_path = g_new0(struct bt_field_path, 1); + if (!field_path) { + BT_LOGE_STR("Failed to allocate one field path."); + goto error; + } + + bt_object_init_shared(&field_path->base, destroy_field_path); + field_path->items = g_array_new(FALSE, FALSE, + sizeof(struct bt_field_path_item)); + if (!field_path->items) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + BT_LIB_LOGD("Created empty field path object: %!+P", field_path); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(field_path); + +end: + return field_path; +} + +enum bt_scope bt_field_path_get_root_scope( + const struct bt_field_path *field_path) +{ + BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); + return field_path->root; +} + +uint64_t bt_field_path_get_item_count(const struct bt_field_path *field_path) +{ + BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); + return (uint64_t) field_path->items->len; +} + +const struct bt_field_path_item *bt_field_path_borrow_item_by_index_const( + const struct bt_field_path *field_path, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(field_path, "Field path"); + BT_ASSERT_PRE_VALID_INDEX(index, field_path->items->len); + return bt_field_path_borrow_item_by_index_inline(field_path, index); +} + +enum bt_field_path_item_type bt_field_path_item_get_type( + const struct bt_field_path_item *field_path_item) +{ + BT_ASSERT_PRE_NON_NULL(field_path_item, "Field path item"); + return field_path_item->type; +} + +uint64_t bt_field_path_item_index_get_index( + const struct bt_field_path_item *field_path_item) +{ + BT_ASSERT_PRE_NON_NULL(field_path_item, "Field path item"); + BT_ASSERT_PRE(field_path_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX, + "Field path item is not an index field path item: " + "addr=%p, type=%s", field_path_item, + bt_field_path_item_type_string(field_path_item->type)); + return field_path_item->index; +} + +void bt_field_path_get_ref(const struct bt_field_path *field_path) +{ + bt_object_get_ref(field_path); +} + +void bt_field_path_put_ref(const struct bt_field_path *field_path) +{ + bt_object_put_ref(field_path); +} diff --git a/src/lib/trace-ir/field-path.h b/src/lib/trace-ir/field-path.h new file mode 100644 index 00000000..506c0063 --- /dev/null +++ b/src/lib/trace-ir/field-path.h @@ -0,0 +1,90 @@ +#ifndef BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL +#define BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL + +/* + * Copyright 2016-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "lib/object.h" +#include +#include "common/assert.h" +#include + +struct bt_field_path_item { + enum bt_field_path_item_type type; + uint64_t index; +}; + +struct bt_field_path { + struct bt_object base; + enum bt_scope root; + + /* Array of `struct bt_field_path_item` (items) */ + GArray *items; +}; + +BT_HIDDEN +struct bt_field_path *bt_field_path_create(void); + +static inline +struct bt_field_path_item *bt_field_path_borrow_item_by_index_inline( + const struct bt_field_path *field_path, uint64_t index) +{ + BT_ASSERT(field_path); + BT_ASSERT(index < field_path->items->len); + return &g_array_index(field_path->items, struct bt_field_path_item, + index); +} + +static inline +void bt_field_path_append_item(struct bt_field_path *field_path, + struct bt_field_path_item *item) +{ + BT_ASSERT(field_path); + BT_ASSERT(item); + g_array_append_val(field_path->items, *item); +} + +static inline +void bt_field_path_remove_last_item(struct bt_field_path *field_path) +{ + BT_ASSERT(field_path); + BT_ASSERT(field_path->items->len > 0); + g_array_set_size(field_path->items, field_path->items->len - 1); +} + +static inline +const char *bt_field_path_item_type_string(enum bt_field_path_item_type type) +{ + switch (type) { + case BT_FIELD_PATH_ITEM_TYPE_INDEX: + return "BT_FIELD_PATH_ITEM_TYPE_INDEX"; + case BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT: + return "BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT"; + default: + return "(unknown)"; + } +}; + +#endif /* BABELTRACE_TRACE_IR_FIELD_PATH_INTERNAL */ diff --git a/src/lib/trace-ir/field-wrapper.c b/src/lib/trace-ir/field-wrapper.c new file mode 100644 index 00000000..778d8650 --- /dev/null +++ b/src/lib/trace-ir/field-wrapper.c @@ -0,0 +1,107 @@ +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "FIELD-WRAPPER" +#include "lib/lib-logging.h" + +#include "lib/object-pool.h" +#include "lib/object.h" +#include + +#include "field-wrapper.h" +#include "field.h" + +BT_HIDDEN +struct bt_field_wrapper *bt_field_wrapper_new(void *data) +{ + struct bt_field_wrapper *field_wrapper = + g_new0(struct bt_field_wrapper, 1); + + BT_LOGD_STR("Creating empty field wrapper object."); + + if (!field_wrapper) { + BT_LOGE("Failed to allocate one field wrapper."); + goto end; + } + + bt_object_init_unique(&field_wrapper->base); + BT_LOGD("Created empty field wrapper object: addr=%p", + field_wrapper); + +end: + return field_wrapper; +} + +BT_HIDDEN +void bt_field_wrapper_destroy(struct bt_field_wrapper *field_wrapper) +{ + BT_LOGD("Destroying field wrapper: addr=%p", field_wrapper); + + if (field_wrapper->field) { + BT_LOGD_STR("Destroying field."); + bt_field_destroy((void *) field_wrapper->field); + field_wrapper->field = NULL; + } + + BT_LOGD_STR("Putting stream class."); + g_free(field_wrapper); +} + +BT_HIDDEN +struct bt_field_wrapper *bt_field_wrapper_create( + struct bt_object_pool *pool, struct bt_field_class *fc) +{ + struct bt_field_wrapper *field_wrapper = NULL; + + BT_ASSERT(pool); + BT_ASSERT(fc); + field_wrapper = bt_object_pool_create_object(pool); + if (!field_wrapper) { + BT_LIB_LOGE("Cannot allocate one field wrapper from field wrapper pool: " + "%![pool-]+o", pool); + goto error; + } + + if (!field_wrapper->field) { + field_wrapper->field = (void *) bt_field_create(fc); + if (!field_wrapper->field) { + BT_LIB_LOGE("Cannot create field wrapper from field class: " + "%![fc-]+F", fc); + goto error; + } + + BT_LIB_LOGD("Created initial field wrapper object: " + "wrapper-addr=%p, %![field-]+f", field_wrapper, + field_wrapper->field); + } + + goto end; + +error: + if (field_wrapper) { + bt_field_wrapper_destroy(field_wrapper); + field_wrapper = NULL; + } + +end: + return field_wrapper; +} diff --git a/src/lib/trace-ir/field-wrapper.h b/src/lib/trace-ir/field-wrapper.h new file mode 100644 index 00000000..97728e0d --- /dev/null +++ b/src/lib/trace-ir/field-wrapper.h @@ -0,0 +1,48 @@ +#ifndef BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H +#define BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H + +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/object-pool.h" +#include "lib/object.h" + +#include "field.h" + +struct bt_field_wrapper { + struct bt_object base; + + /* Owned by this */ + struct bt_field *field; +}; + +BT_HIDDEN +struct bt_field_wrapper *bt_field_wrapper_new(void *data); + +BT_HIDDEN +void bt_field_wrapper_destroy(struct bt_field_wrapper *field); + +BT_HIDDEN +struct bt_field_wrapper *bt_field_wrapper_create( + struct bt_object_pool *pool, struct bt_field_class *fc); + +#endif /* BABELTRACE_TRACE_IR_FIELD_WRAPPER_INTERNAL_H */ diff --git a/src/lib/trace-ir/field.c b/src/lib/trace-ir/field.c new file mode 100644 index 00000000..4f75c344 --- /dev/null +++ b/src/lib/trace-ir/field.c @@ -0,0 +1,1180 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "FIELDS" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include "lib/object.h" +#include "compat/compiler.h" +#include "compat/fcntl.h" +#include "common/align.h" +#include "common/assert.h" +#include + +#include "field.h" +#include "field-class.h" + +static +void reset_single_field(struct bt_field *field); + +static +void reset_array_field(struct bt_field *field); + +static +void reset_structure_field(struct bt_field *field); + +static +void reset_variant_field(struct bt_field *field); + +static +void set_single_field_is_frozen(struct bt_field *field, bool is_frozen); + +static +void set_array_field_is_frozen(struct bt_field *field, bool is_frozen); + +static +void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen); + +static +void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen); + +static +bool single_field_is_set(const struct bt_field *field); + +static +bool array_field_is_set(const struct bt_field *field); + +static +bool structure_field_is_set(const struct bt_field *field); + +static +bool variant_field_is_set(const struct bt_field *field); + +static +struct bt_field_methods integer_field_methods = { + .set_is_frozen = set_single_field_is_frozen, + .is_set = single_field_is_set, + .reset = reset_single_field, +}; + +static +struct bt_field_methods real_field_methods = { + .set_is_frozen = set_single_field_is_frozen, + .is_set = single_field_is_set, + .reset = reset_single_field, +}; + +static +struct bt_field_methods string_field_methods = { + .set_is_frozen = set_single_field_is_frozen, + .is_set = single_field_is_set, + .reset = reset_single_field, +}; + +static +struct bt_field_methods structure_field_methods = { + .set_is_frozen = set_structure_field_is_frozen, + .is_set = structure_field_is_set, + .reset = reset_structure_field, +}; + +static +struct bt_field_methods array_field_methods = { + .set_is_frozen = set_array_field_is_frozen, + .is_set = array_field_is_set, + .reset = reset_array_field, +}; + +static +struct bt_field_methods variant_field_methods = { + .set_is_frozen = set_variant_field_is_frozen, + .is_set = variant_field_is_set, + .reset = reset_variant_field, +}; + +static +struct bt_field *create_integer_field(struct bt_field_class *); + +static +struct bt_field *create_real_field(struct bt_field_class *); + +static +struct bt_field *create_string_field(struct bt_field_class *); + +static +struct bt_field *create_structure_field(struct bt_field_class *); + +static +struct bt_field *create_static_array_field(struct bt_field_class *); + +static +struct bt_field *create_dynamic_array_field(struct bt_field_class *); + +static +struct bt_field *create_variant_field(struct bt_field_class *); + +static +struct bt_field *(* const field_create_funcs[])(struct bt_field_class *) = { + [BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER] = create_integer_field, + [BT_FIELD_CLASS_TYPE_SIGNED_INTEGER] = create_integer_field, + [BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION] = create_integer_field, + [BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION] = create_integer_field, + [BT_FIELD_CLASS_TYPE_REAL] = create_real_field, + [BT_FIELD_CLASS_TYPE_STRING] = create_string_field, + [BT_FIELD_CLASS_TYPE_STRUCTURE] = create_structure_field, + [BT_FIELD_CLASS_TYPE_STATIC_ARRAY] = create_static_array_field, + [BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY] = create_dynamic_array_field, + [BT_FIELD_CLASS_TYPE_VARIANT] = create_variant_field, +}; + +static +void destroy_integer_field(struct bt_field *field); + +static +void destroy_real_field(struct bt_field *field); + +static +void destroy_string_field(struct bt_field *field); + +static +void destroy_structure_field(struct bt_field *field); + +static +void destroy_array_field(struct bt_field *field); + +static +void destroy_variant_field(struct bt_field *field); + +static +void (* const field_destroy_funcs[])(struct bt_field *) = { + [BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER] = destroy_integer_field, + [BT_FIELD_CLASS_TYPE_SIGNED_INTEGER] = destroy_integer_field, + [BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION] = destroy_integer_field, + [BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION] = destroy_integer_field, + [BT_FIELD_CLASS_TYPE_REAL] = destroy_real_field, + [BT_FIELD_CLASS_TYPE_STRING] = destroy_string_field, + [BT_FIELD_CLASS_TYPE_STRUCTURE] = destroy_structure_field, + [BT_FIELD_CLASS_TYPE_STATIC_ARRAY] = destroy_array_field, + [BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY] = destroy_array_field, + [BT_FIELD_CLASS_TYPE_VARIANT] = destroy_variant_field, +}; + +struct bt_field_class *bt_field_borrow_class(const struct bt_field *field) +{ + BT_ASSERT_PRE_NON_NULL(field, "Field"); + return field->class; +} + +const struct bt_field_class *bt_field_borrow_class_const( + const struct bt_field *field) +{ + BT_ASSERT_PRE_NON_NULL(field, "Field"); + return field->class; +} + +enum bt_field_class_type bt_field_get_class_type(const struct bt_field *field) +{ + BT_ASSERT_PRE_NON_NULL(field, "Field"); + return field->class->type; +} + +BT_HIDDEN +struct bt_field *bt_field_create(struct bt_field_class *fc) +{ + struct bt_field *field = NULL; + + BT_ASSERT_PRE_NON_NULL(fc, "Field class"); + BT_ASSERT(bt_field_class_has_known_type(fc)); + field = field_create_funcs[fc->type](fc); + if (!field) { + BT_LIB_LOGE("Cannot create field object from field class: " + "%![fc-]+F", fc); + goto end; + } + +end: + return field; +} + +static inline +void init_field(struct bt_field *field, struct bt_field_class *fc, + struct bt_field_methods *methods) +{ + BT_ASSERT(field); + BT_ASSERT(fc); + bt_object_init_unique(&field->base); + field->methods = methods; + field->class = fc; + bt_object_get_no_null_check(fc); +} + +static +struct bt_field *create_integer_field(struct bt_field_class *fc) +{ + struct bt_field_integer *int_field; + + BT_LIB_LOGD("Creating integer field object: %![fc-]+F", fc); + int_field = g_new0(struct bt_field_integer, 1); + if (!int_field) { + BT_LOGE_STR("Failed to allocate one integer field."); + goto end; + } + + init_field((void *) int_field, fc, &integer_field_methods); + BT_LIB_LOGD("Created integer field object: %!+f", int_field); + +end: + return (void *) int_field; +} + +static +struct bt_field *create_real_field(struct bt_field_class *fc) +{ + struct bt_field_real *real_field; + + BT_LIB_LOGD("Creating real field object: %![fc-]+F", fc); + real_field = g_new0(struct bt_field_real, 1); + if (!real_field) { + BT_LOGE_STR("Failed to allocate one real field."); + goto end; + } + + init_field((void *) real_field, fc, &real_field_methods); + BT_LIB_LOGD("Created real field object: %!+f", real_field); + +end: + return (void *) real_field; +} + +static +struct bt_field *create_string_field(struct bt_field_class *fc) +{ + struct bt_field_string *string_field; + + BT_LIB_LOGD("Creating string field object: %![fc-]+F", fc); + string_field = g_new0(struct bt_field_string, 1); + if (!string_field) { + BT_LOGE_STR("Failed to allocate one string field."); + goto end; + } + + init_field((void *) string_field, fc, &string_field_methods); + string_field->buf = g_array_sized_new(FALSE, FALSE, + sizeof(char), 1); + if (!string_field->buf) { + BT_LOGE_STR("Failed to allocate a GArray."); + BT_OBJECT_PUT_REF_AND_RESET(string_field); + goto end; + } + + g_array_index(string_field->buf, char, 0) = '\0'; + BT_LIB_LOGD("Created string field object: %!+f", string_field); + +end: + return (void *) string_field; +} + +static inline +int create_fields_from_named_field_classes( + struct bt_field_class_named_field_class_container *fc, + GPtrArray **fields) +{ + int ret = 0; + uint64_t i; + + *fields = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_field_destroy); + if (!*fields) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + ret = -1; + goto end; + } + + g_ptr_array_set_size(*fields, fc->named_fcs->len); + + for (i = 0; i < fc->named_fcs->len; i++) { + struct bt_field *field; + struct bt_named_field_class *named_fc = + BT_FIELD_CLASS_NAMED_FC_AT_INDEX(fc, i); + + field = bt_field_create(named_fc->fc); + if (!field) { + BT_LIB_LOGE("Failed to create structure member or variant option field: " + "name=\"%s\", %![fc-]+F", + named_fc->name->str, named_fc->fc); + ret = -1; + goto end; + } + + g_ptr_array_index(*fields, i) = field; + } + +end: + return ret; +} + +static +struct bt_field *create_structure_field(struct bt_field_class *fc) +{ + struct bt_field_structure *struct_field; + + BT_LIB_LOGD("Creating structure field object: %![fc-]+F", fc); + struct_field = g_new0(struct bt_field_structure, 1); + if (!struct_field) { + BT_LOGE_STR("Failed to allocate one structure field."); + goto end; + } + + init_field((void *) struct_field, fc, &structure_field_methods); + + if (create_fields_from_named_field_classes((void *) fc, + &struct_field->fields)) { + BT_LIB_LOGE("Cannot create structure member fields: " + "%![fc-]+F", fc); + BT_OBJECT_PUT_REF_AND_RESET(struct_field); + goto end; + } + + BT_LIB_LOGD("Created structure field object: %!+f", struct_field); + +end: + return (void *) struct_field; +} + +static +struct bt_field *create_variant_field(struct bt_field_class *fc) +{ + struct bt_field_variant *var_field; + + BT_LIB_LOGD("Creating variant field object: %![fc-]+F", fc); + var_field = g_new0(struct bt_field_variant, 1); + if (!var_field) { + BT_LOGE_STR("Failed to allocate one variant field."); + goto end; + } + + init_field((void *) var_field, fc, &variant_field_methods); + + if (create_fields_from_named_field_classes((void *) fc, + &var_field->fields)) { + BT_LIB_LOGE("Cannot create variant member fields: " + "%![fc-]+F", fc); + BT_OBJECT_PUT_REF_AND_RESET(var_field); + goto end; + } + + BT_LIB_LOGD("Created variant field object: %!+f", var_field); + +end: + return (void *) var_field; +} + +static inline +int init_array_field_fields(struct bt_field_array *array_field) +{ + int ret = 0; + uint64_t i; + struct bt_field_class_array *array_fc; + + BT_ASSERT(array_field); + array_fc = (void *) array_field->common.class; + array_field->fields = g_ptr_array_sized_new(array_field->length); + if (!array_field->fields) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + ret = -1; + goto end; + } + + g_ptr_array_set_free_func(array_field->fields, + (GDestroyNotify) bt_field_destroy); + g_ptr_array_set_size(array_field->fields, array_field->length); + + for (i = 0; i < array_field->length; i++) { + array_field->fields->pdata[i] = bt_field_create( + array_fc->element_fc); + if (!array_field->fields->pdata[i]) { + BT_LIB_LOGE("Cannot create array field's element field: " + "index=%" PRIu64 ", %![fc-]+F", i, array_fc); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +static +struct bt_field *create_static_array_field(struct bt_field_class *fc) +{ + struct bt_field_class_static_array *array_fc = (void *) fc; + struct bt_field_array *array_field; + + BT_LIB_LOGD("Creating static array field object: %![fc-]+F", fc); + array_field = g_new0(struct bt_field_array, 1); + if (!array_field) { + BT_LOGE_STR("Failed to allocate one static array field."); + goto end; + } + + init_field((void *) array_field, fc, &array_field_methods); + array_field->length = array_fc->length; + + if (init_array_field_fields(array_field)) { + BT_LIB_LOGE("Cannot create static array fields: " + "%![fc-]+F", fc); + BT_OBJECT_PUT_REF_AND_RESET(array_field); + goto end; + } + + BT_LIB_LOGD("Created static array field object: %!+f", array_field); + +end: + return (void *) array_field; +} + +static +struct bt_field *create_dynamic_array_field(struct bt_field_class *fc) +{ + struct bt_field_array *array_field; + + BT_LIB_LOGD("Creating dynamic array field object: %![fc-]+F", fc); + array_field = g_new0(struct bt_field_array, 1); + if (!array_field) { + BT_LOGE_STR("Failed to allocate one dynamic array field."); + goto end; + } + + init_field((void *) array_field, fc, &array_field_methods); + + if (init_array_field_fields(array_field)) { + BT_LIB_LOGE("Cannot create dynamic array fields: " + "%![fc-]+F", fc); + BT_OBJECT_PUT_REF_AND_RESET(array_field); + goto end; + } + + BT_LIB_LOGD("Created dynamic array field object: %!+f", array_field); + +end: + return (void *) array_field; +} + +int64_t bt_field_signed_integer_get_value(const struct bt_field *field) +{ + const struct bt_field_integer *int_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(field, "Field"); + return int_field->value.i; +} + +void bt_field_signed_integer_set_value(struct bt_field *field, int64_t value) +{ + struct bt_field_integer *int_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(field, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE(bt_util_value_is_in_range_signed( + ((struct bt_field_class_integer *) field->class)->range, value), + "Value is out of bounds: value=%" PRId64 ", %![field-]+f, " + "%![fc-]+F", value, field, field->class); + int_field->value.i = value; + bt_field_set_single(field, true); +} + +uint64_t bt_field_unsigned_integer_get_value(const struct bt_field *field) +{ + const struct bt_field_integer *int_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(field, "Field"); + return int_field->value.u; +} + +void bt_field_unsigned_integer_set_value(struct bt_field *field, uint64_t value) +{ + struct bt_field_integer *int_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(field, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE(bt_util_value_is_in_range_unsigned( + ((struct bt_field_class_integer *) field->class)->range, value), + "Value is out of bounds: value=%" PRIu64 ", %![field-]+f, " + "%![fc-]+F", value, field, field->class); + int_field->value.u = value; + bt_field_set_single(field, true); +} + +double bt_field_real_get_value(const struct bt_field *field) +{ + const struct bt_field_real *real_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_REAL, "Field"); + return real_field->value; +} + +void bt_field_real_set_value(struct bt_field *field, double value) +{ + struct bt_field_real *real_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_REAL, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE( + !((struct bt_field_class_real *) field->class)->is_single_precision || + (double) (float) value == value, + "Invalid value for a single-precision real number: value=%f, " + "%![fc-]+F", value, field->class); + real_field->value = value; + bt_field_set_single(field, true); +} + +enum bt_field_status bt_field_unsigned_enumeration_get_mapping_labels( + const struct bt_field *field, + bt_field_class_enumeration_mapping_label_array *label_array, + uint64_t *count) +{ + const struct bt_field_integer *int_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); + BT_ASSERT_PRE_NON_NULL(label_array, "Count (output)"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, "Field"); + return (int) + bt_field_class_unsigned_enumeration_get_mapping_labels_by_value( + field->class, int_field->value.u, label_array, count); +} + +enum bt_field_status bt_field_signed_enumeration_get_mapping_labels( + const struct bt_field *field, + bt_field_class_enumeration_mapping_label_array *label_array, + uint64_t *count) +{ + const struct bt_field_integer *int_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_NON_NULL(label_array, "Label array (output)"); + BT_ASSERT_PRE_NON_NULL(label_array, "Count (output)"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, "Field"); + return (int) + bt_field_class_signed_enumeration_get_mapping_labels_by_value( + field->class, int_field->value.i, label_array, count); +} + +const char *bt_field_string_get_value(const struct bt_field *field) +{ + const struct bt_field_string *string_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, + "Field"); + return (const char *) string_field->buf->data; +} + +uint64_t bt_field_string_get_length(const struct bt_field *field) +{ + const struct bt_field_string *string_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_SET(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, + "Field"); + return string_field->length; +} + +static inline +void clear_string_field(struct bt_field *field) +{ + struct bt_field_string *string_field = (void *) field; + + BT_ASSERT(field); + string_field->length = 0; + bt_field_set_single(field, true); +} + +enum bt_field_status bt_field_string_set_value(struct bt_field *field, + const char *value) +{ + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_NON_NULL(value, "Value"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, BT_FIELD_CLASS_TYPE_STRING, + "Field"); + clear_string_field(field); + return bt_field_string_append_with_length(field, value, + (uint64_t) strlen(value)); +} + +enum bt_field_status bt_field_string_append(struct bt_field *field, const char *value) +{ + return bt_field_string_append_with_length(field, + value, (uint64_t) strlen(value)); +} + +enum bt_field_status bt_field_string_append_with_length(struct bt_field *field, + const char *value, uint64_t length) +{ + struct bt_field_string *string_field = (void *) field; + char *data; + uint64_t new_length; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_NON_NULL(value, "Value"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_STRING, "Field"); + + /* Make sure no null bytes are appended */ + BT_ASSERT_PRE(memchr(value, '\0', length) == NULL, + "String value to append contains a null character: " + "partial-value=\"%.32s\", length=%" PRIu64, value, length); + + new_length = length + string_field->length; + + if (unlikely(new_length + 1 > string_field->buf->len)) { + g_array_set_size(string_field->buf, new_length + 1); + } + + data = string_field->buf->data; + memcpy(data + string_field->length, value, length); + ((char *) string_field->buf->data)[new_length] = '\0'; + string_field->length = new_length; + bt_field_set_single(field, true); + return BT_FIELD_STATUS_OK; +} + +enum bt_field_status bt_field_string_clear(struct bt_field *field) +{ + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_STRING, "Field"); + clear_string_field(field); + return BT_FIELD_STATUS_OK; +} + +uint64_t bt_field_array_get_length(const struct bt_field *field) +{ + const struct bt_field_array *array_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_ARRAY(field, "Field"); + return array_field->length; +} + +enum bt_field_status bt_field_dynamic_array_set_length(struct bt_field *field, + uint64_t length) +{ + int ret = BT_FIELD_STATUS_OK; + struct bt_field_array *array_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + + if (unlikely(length > array_field->fields->len)) { + /* Make more room */ + struct bt_field_class_array *array_fc; + uint64_t cur_len = array_field->fields->len; + uint64_t i; + + g_ptr_array_set_size(array_field->fields, length); + array_fc = (void *) field->class; + + for (i = cur_len; i < array_field->fields->len; i++) { + struct bt_field *elem_field = bt_field_create( + array_fc->element_fc); + + if (!elem_field) { + BT_LIB_LOGE("Cannot create element field for " + "dynamic array field: " + "index=%" PRIu64 ", " + "%![array-field-]+f", i, field); + ret = BT_FIELD_STATUS_NOMEM; + goto end; + } + + BT_ASSERT(!array_field->fields->pdata[i]); + array_field->fields->pdata[i] = elem_field; + } + } + + array_field->length = length; + +end: + return ret; +} + +static inline +struct bt_field *borrow_array_field_element_field_by_index( + struct bt_field *field, uint64_t index) +{ + struct bt_field_array *array_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_IS_ARRAY(field, "Field"); + BT_ASSERT_PRE_VALID_INDEX(index, array_field->length); + return array_field->fields->pdata[index]; +} + +struct bt_field *bt_field_array_borrow_element_field_by_index( + struct bt_field *field, uint64_t index) +{ + return borrow_array_field_element_field_by_index(field, index); +} + +const struct bt_field * +bt_field_array_borrow_element_field_by_index_const( + const struct bt_field *field, uint64_t index) +{ + return borrow_array_field_element_field_by_index((void *) field, index); +} + +static inline +struct bt_field *borrow_structure_field_member_field_by_index( + struct bt_field *field, uint64_t index) +{ + struct bt_field_structure *struct_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_STRUCTURE, "Field"); + BT_ASSERT_PRE_VALID_INDEX(index, struct_field->fields->len); + return struct_field->fields->pdata[index]; +} + +struct bt_field *bt_field_structure_borrow_member_field_by_index( + struct bt_field *field, uint64_t index) +{ + return borrow_structure_field_member_field_by_index(field, + index); +} + +const struct bt_field * +bt_field_structure_borrow_member_field_by_index_const( + const struct bt_field *field, uint64_t index) +{ + return borrow_structure_field_member_field_by_index( + (void *) field, index); +} + +static inline +struct bt_field *borrow_structure_field_member_field_by_name( + struct bt_field *field, const char *name) +{ + struct bt_field *ret_field = NULL; + struct bt_field_class_structure *struct_fc; + struct bt_field_structure *struct_field = (void *) field; + gpointer orig_key; + gpointer index; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_NON_NULL(name, "Field name"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_STRUCTURE, "Field"); + struct_fc = (void *) field->class; + + if (!g_hash_table_lookup_extended(struct_fc->common.name_to_index, name, + &orig_key, &index)) { + goto end; + } + + ret_field = struct_field->fields->pdata[GPOINTER_TO_UINT(index)]; + BT_ASSERT(ret_field); + +end: + return ret_field; +} + +struct bt_field *bt_field_structure_borrow_member_field_by_name( + struct bt_field *field, const char *name) +{ + return borrow_structure_field_member_field_by_name(field, name); +} + +const struct bt_field *bt_field_structure_borrow_member_field_by_name_const( + const struct bt_field *field, const char *name) +{ + return borrow_structure_field_member_field_by_name( + (void *) field, name); +} + +static inline +struct bt_field *borrow_variant_field_selected_option_field( + struct bt_field *field) +{ + struct bt_field_variant *var_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_VARIANT, "Field"); + BT_ASSERT_PRE(var_field->selected_field, + "Variant field has no selected field: %!+f", field); + return var_field->selected_field; +} + +struct bt_field *bt_field_variant_borrow_selected_option_field( + struct bt_field *field) +{ + return borrow_variant_field_selected_option_field(field); +} + +const struct bt_field *bt_field_variant_borrow_selected_option_field_const( + const struct bt_field *field) +{ + return borrow_variant_field_selected_option_field((void *) field); +} + +enum bt_field_status bt_field_variant_select_option_field( + struct bt_field *field, uint64_t index) +{ + struct bt_field_variant *var_field = (void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_VARIANT, "Field"); + BT_ASSERT_PRE_FIELD_HOT(field, "Field"); + BT_ASSERT_PRE_VALID_INDEX(index, var_field->fields->len); + var_field->selected_field = var_field->fields->pdata[index]; + var_field->selected_index = index; + return BT_FIELD_STATUS_OK; +} + +uint64_t bt_field_variant_get_selected_option_field_index( + const struct bt_field *field) +{ + const struct bt_field_variant *var_field = (const void *) field; + + BT_ASSERT_PRE_NON_NULL(field, "Field"); + BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(field, + BT_FIELD_CLASS_TYPE_VARIANT, "Field"); + BT_ASSERT_PRE(var_field->selected_field, + "Variant field has no selected field: %!+f", field); + return var_field->selected_index; +} + +static inline +void bt_field_finalize(struct bt_field *field) +{ + BT_ASSERT(field); + BT_LOGD_STR("Putting field's class."); + BT_OBJECT_PUT_REF_AND_RESET(field->class); +} + +static +void destroy_integer_field(struct bt_field *field) +{ + BT_ASSERT(field); + BT_LIB_LOGD("Destroying integer field object: %!+f", field); + bt_field_finalize(field); + g_free(field); +} + +static +void destroy_real_field(struct bt_field *field) +{ + BT_ASSERT(field); + BT_LIB_LOGD("Destroying real field object: %!+f", field); + bt_field_finalize(field); + g_free(field); +} + +static +void destroy_structure_field(struct bt_field *field) +{ + struct bt_field_structure *struct_field = (void *) field; + + BT_ASSERT(field); + BT_LIB_LOGD("Destroying structure field object: %!+f", field); + bt_field_finalize(field); + + if (struct_field->fields) { + g_ptr_array_free(struct_field->fields, TRUE); + struct_field->fields = NULL; + } + + g_free(field); +} + +static +void destroy_variant_field(struct bt_field *field) +{ + struct bt_field_variant *var_field = (void *) field; + + BT_ASSERT(field); + BT_LIB_LOGD("Destroying variant field object: %!+f", field); + bt_field_finalize(field); + + if (var_field->fields) { + g_ptr_array_free(var_field->fields, TRUE); + var_field->fields = NULL; + } + + g_free(field); +} + +static +void destroy_array_field(struct bt_field *field) +{ + struct bt_field_array *array_field = (void *) field; + + BT_ASSERT(field); + BT_LIB_LOGD("Destroying array field object: %!+f", field); + bt_field_finalize(field); + + if (array_field->fields) { + g_ptr_array_free(array_field->fields, TRUE); + array_field->fields = NULL; + } + + g_free(field); +} + +static +void destroy_string_field(struct bt_field *field) +{ + struct bt_field_string *string_field = (void *) field; + + BT_ASSERT(field); + BT_LIB_LOGD("Destroying string field object: %!+f", field); + bt_field_finalize(field); + + if (string_field->buf) { + g_array_free(string_field->buf, TRUE); + string_field->buf = NULL; + } + + g_free(field); +} + +BT_HIDDEN +void bt_field_destroy(struct bt_field *field) +{ + BT_ASSERT(field); + BT_ASSERT(bt_field_class_has_known_type(field->class)); + field_destroy_funcs[field->class->type](field); +} + +static +void reset_single_field(struct bt_field *field) +{ + BT_ASSERT(field); + field->is_set = false; +} + +static +void reset_structure_field(struct bt_field *field) +{ + uint64_t i; + struct bt_field_structure *struct_field = (void *) field; + + BT_ASSERT(field); + + for (i = 0; i < struct_field->fields->len; i++) { + bt_field_reset(struct_field->fields->pdata[i]); + } +} + +static +void reset_variant_field(struct bt_field *field) +{ + uint64_t i; + struct bt_field_variant *var_field = (void *) field; + + BT_ASSERT(field); + + for (i = 0; i < var_field->fields->len; i++) { + bt_field_reset(var_field->fields->pdata[i]); + } +} + +static +void reset_array_field(struct bt_field *field) +{ + uint64_t i; + struct bt_field_array *array_field = (void *) field; + + BT_ASSERT(field); + + for (i = 0; i < array_field->fields->len; i++) { + bt_field_reset(array_field->fields->pdata[i]); + } +} + +static +void set_single_field_is_frozen(struct bt_field *field, bool is_frozen) +{ + field->frozen = is_frozen; +} + +static +void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen) +{ + uint64_t i; + struct bt_field_structure *struct_field = (void *) field; + + BT_LIB_LOGD("Setting structure field's frozen state: " + "%![field-]+f, is-frozen=%d", field, is_frozen); + + for (i = 0; i < struct_field->fields->len; i++) { + struct bt_field *member_field = struct_field->fields->pdata[i]; + + BT_LIB_LOGD("Setting structure field's member field's " + "frozen state: %![field-]+f, index=%" PRIu64, + member_field, i); + bt_field_set_is_frozen(member_field, is_frozen); + } + + set_single_field_is_frozen(field, is_frozen); +} + +static +void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen) +{ + uint64_t i; + struct bt_field_variant *var_field = (void *) field; + + BT_LIB_LOGD("Setting variant field's frozen state: " + "%![field-]+f, is-frozen=%d", field, is_frozen); + + for (i = 0; i < var_field->fields->len; i++) { + struct bt_field *option_field = var_field->fields->pdata[i]; + + BT_LIB_LOGD("Setting variant field's option field's " + "frozen state: %![field-]+f, index=%" PRIu64, + option_field, i); + bt_field_set_is_frozen(option_field, is_frozen); + } + + set_single_field_is_frozen(field, is_frozen); +} + +static +void set_array_field_is_frozen(struct bt_field *field, bool is_frozen) +{ + uint64_t i; + struct bt_field_array *array_field = (void *) field; + + BT_LIB_LOGD("Setting array field's frozen state: " + "%![field-]+f, is-frozen=%d", field, is_frozen); + + for (i = 0; i < array_field->fields->len; i++) { + struct bt_field *elem_field = array_field->fields->pdata[i]; + + BT_LIB_LOGD("Setting array field's element field's " + "frozen state: %![field-]+f, index=%" PRIu64, + elem_field, i); + bt_field_set_is_frozen(elem_field, is_frozen); + } + + set_single_field_is_frozen(field, is_frozen); +} + +BT_HIDDEN +void _bt_field_set_is_frozen(const struct bt_field *field, + bool is_frozen) +{ + BT_ASSERT(field); + BT_LIB_LOGD("Setting field object's frozen state: %!+f, is-frozen=%d", + field, is_frozen); + BT_ASSERT(field->methods->set_is_frozen); + field->methods->set_is_frozen((void *) field, is_frozen); +} + +static +bool single_field_is_set(const struct bt_field *field) +{ + BT_ASSERT(field); + return field->is_set; +} + +static +bool structure_field_is_set(const struct bt_field *field) +{ + bool is_set = true; + uint64_t i; + const struct bt_field_structure *struct_field = (const void *) field; + + BT_ASSERT(field); + + for (i = 0; i < struct_field->fields->len; i++) { + is_set = bt_field_is_set(struct_field->fields->pdata[i]); + if (!is_set) { + goto end; + } + } + +end: + return is_set; +} + +static +bool variant_field_is_set(const struct bt_field *field) +{ + const struct bt_field_variant *var_field = (const void *) field; + bool is_set = false; + + BT_ASSERT(field); + + if (var_field->selected_field) { + is_set = bt_field_is_set(var_field->selected_field); + } + + return is_set; +} + +static +bool array_field_is_set(const struct bt_field *field) +{ + bool is_set = true; + uint64_t i; + const struct bt_field_array *array_field = (const void *) field; + + BT_ASSERT(field); + + for (i = 0; i < array_field->length; i++) { + is_set = bt_field_is_set(array_field->fields->pdata[i]); + if (!is_set) { + goto end; + } + } + +end: + return is_set; +} diff --git a/src/lib/trace-ir/field.h b/src/lib/trace-ir/field.h new file mode 100644 index 00000000..3cd4bfe5 --- /dev/null +++ b/src/lib/trace-ir/field.h @@ -0,0 +1,204 @@ +#ifndef BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H +#define BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/assert-pre.h" +#include "common/common.h" +#include "lib/object.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include +#include + +#include "field-class.h" +#include "utils.h" + +#define BT_ASSERT_PRE_FIELD_HAS_CLASS_TYPE(_field, _cls_type, _name) \ + BT_ASSERT_PRE(((const struct bt_field *) (_field))->class->type == (_cls_type), \ + _name " has the wrong class type: expected-class-type=%s, " \ + "%![field-]+f", \ + bt_common_field_class_type_string(_cls_type), (_field)) + +#define BT_ASSERT_PRE_FIELD_IS_UNSIGNED_INT(_field, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, \ + _name " is not an unsigned integer field: %![field-]+f", \ + (_field)) + +#define BT_ASSERT_PRE_FIELD_IS_SIGNED_INT(_field, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, \ + _name " is not a signed integer field: %![field-]+f", \ + (_field)) + +#define BT_ASSERT_PRE_FIELD_IS_ARRAY(_field, _name) \ + BT_ASSERT_PRE( \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || \ + ((const struct bt_field *) (_field))->class->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY, \ + _name " is not an array field: %![field-]+f", (_field)) + +#define BT_ASSERT_PRE_FIELD_IS_SET(_field, _name) \ + BT_ASSERT_PRE(bt_field_is_set(_field), \ + _name " is not set: %!+f", (_field)) + +#define BT_ASSERT_PRE_FIELD_HOT(_field, _name) \ + BT_ASSERT_PRE_HOT((const struct bt_field *) (_field), (_name), \ + ": %!+f", (_field)) + +struct bt_field; + +typedef struct bt_field *(* bt_field_create_func)(struct bt_field_class *); +typedef void (*bt_field_method_set_is_frozen)(struct bt_field *, bool); +typedef bool (*bt_field_method_is_set)(const struct bt_field *); +typedef void (*bt_field_method_reset)(struct bt_field *); + +struct bt_field_methods { + bt_field_method_set_is_frozen set_is_frozen; + bt_field_method_is_set is_set; + bt_field_method_reset reset; +}; + +struct bt_field { + struct bt_object base; + + /* Owned by this */ + struct bt_field_class *class; + + /* Virtual table for slow path (dev mode) operations */ + struct bt_field_methods *methods; + + bool is_set; + bool frozen; +}; + +struct bt_field_integer { + struct bt_field common; + + union { + uint64_t u; + int64_t i; + } value; +}; + +struct bt_field_real { + struct bt_field common; + double value; +}; + +struct bt_field_structure { + struct bt_field common; + + /* Array of `struct bt_field *`, owned by this */ + GPtrArray *fields; +}; + +struct bt_field_variant { + struct bt_field common; + + /* Weak: belongs to `fields` below */ + struct bt_field *selected_field; + + /* Index of currently selected field */ + uint64_t selected_index; + + /* Array of `struct bt_field *`, owned by this */ + GPtrArray *fields; +}; + +struct bt_field_array { + struct bt_field common; + + /* Array of `struct bt_field *`, owned by this */ + GPtrArray *fields; + + /* Current effective length */ + uint64_t length; +}; + +struct bt_field_string { + struct bt_field common; + GArray *buf; + uint64_t length; +}; + +#ifdef BT_DEV_MODE +# define bt_field_set_is_frozen _bt_field_set_is_frozen +# define bt_field_is_set _bt_field_is_set +# define bt_field_reset _bt_field_reset +# define bt_field_set_single _bt_field_set_single +#else +# define bt_field_set_is_frozen(_field, _is_frozen) +# define bt_field_is_set(_field) (BT_FALSE) +# define bt_field_reset(_field) +# define bt_field_set_single(_field, _val) +#endif + +BT_HIDDEN +void _bt_field_set_is_frozen(const struct bt_field *field, bool is_frozen); + +static inline +void _bt_field_reset(const struct bt_field *field) +{ + BT_ASSERT(field); + BT_ASSERT(field->methods->reset); + field->methods->reset((void *) field); +} + +static inline +void _bt_field_set_single(struct bt_field *field, bool value) +{ + BT_ASSERT(field); + field->is_set = value; +} + +static inline +bt_bool _bt_field_is_set(const struct bt_field *field) +{ + bt_bool is_set = BT_FALSE; + + if (!field) { + goto end; + } + + BT_ASSERT(bt_field_class_has_known_type(field->class)); + BT_ASSERT(field->methods->is_set); + is_set = field->methods->is_set(field); + +end: + return is_set; +} + +BT_HIDDEN +struct bt_field *bt_field_create(struct bt_field_class *class); + +BT_HIDDEN +void bt_field_destroy(struct bt_field *field); + +#endif /* BABELTRACE_TRACE_IR_FIELDS_INTERNAL_H */ diff --git a/src/lib/trace-ir/packet-context-field.c b/src/lib/trace-ir/packet-context-field.c new file mode 100644 index 00000000..03219a2a --- /dev/null +++ b/src/lib/trace-ir/packet-context-field.c @@ -0,0 +1,93 @@ +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PACKET-CONTEXT-FIELD" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include + +#include "stream-class.h" +#include "field.h" +#include "field-wrapper.h" + +struct bt_field *bt_packet_context_field_borrow_field( + struct bt_packet_context_field *context_field) +{ + struct bt_field_wrapper *field_wrapper = (void *) context_field; + + BT_ASSERT_PRE_NON_NULL(field_wrapper, "Packet context field"); + return (void *) field_wrapper->field; +} + +void bt_packet_context_field_release( + struct bt_packet_context_field *context_field) +{ + struct bt_field_wrapper *field_wrapper = (void *) context_field; + + BT_ASSERT_PRE_NON_NULL(field_wrapper, "Packet context field"); + + /* + * Do not recycle because the pool could be destroyed at this + * point. This function is only called when there's an error + * anyway because the goal of a packet context field wrapper is + * to eventually move it to a packet with + * bt_packet_move_context() after creating it. + */ + bt_field_wrapper_destroy(field_wrapper); +} + +struct bt_packet_context_field *bt_packet_context_field_create( + struct bt_stream_class *stream_class) +{ + struct bt_field_wrapper *field_wrapper; + + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE(stream_class->frozen, + "Stream class is not part of a trace class: %!+S", + stream_class); + BT_ASSERT_PRE(stream_class->packet_context_fc, + "Stream class has no packet context field class: %!+S", + stream_class); + field_wrapper = bt_field_wrapper_create( + &stream_class->packet_context_field_pool, + (void *) stream_class->packet_context_fc); + if (!field_wrapper) { + BT_LIB_LOGE("Cannot allocate one packet context field from stream class: " + "%![sc-]+S", stream_class); + goto error; + } + + BT_ASSERT(field_wrapper->field); + bt_stream_class_freeze(stream_class); + goto end; + +error: + if (field_wrapper) { + bt_field_wrapper_destroy(field_wrapper); + field_wrapper = NULL; + } + +end: + return (void *) field_wrapper; +} diff --git a/src/lib/trace-ir/packet.c b/src/lib/trace-ir/packet.c new file mode 100644 index 00000000..585ed48b --- /dev/null +++ b/src/lib/trace-ir/packet.c @@ -0,0 +1,275 @@ +/* + * Copyright 2016-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PACKET" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "lib/object.h" +#include "common/assert.h" +#include + +#include "field.h" +#include "field-wrapper.h" +#include "packet.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" + +#define BT_ASSERT_PRE_PACKET_HOT(_packet) \ + BT_ASSERT_PRE_HOT((_packet), "Packet", ": %!+a", (_packet)) + +struct bt_stream *bt_packet_borrow_stream(struct bt_packet *packet) +{ + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + return packet->stream; +} + +const struct bt_stream *bt_packet_borrow_stream_const( + const struct bt_packet *packet) +{ + return bt_packet_borrow_stream((void *) packet); +} + +struct bt_field *bt_packet_borrow_context_field(struct bt_packet *packet) +{ + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + return packet->context_field ? packet->context_field->field : NULL; +} + +const struct bt_field *bt_packet_borrow_context_field_const( + const struct bt_packet *packet) +{ + return bt_packet_borrow_context_field((void *) packet); +} + +BT_HIDDEN +void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen) +{ + if (!packet) { + return; + } + + BT_LIB_LOGD("Setting packet's frozen state: %![packet-]+a, " + "is-frozen=%d", packet, is_frozen); + + if (packet->context_field) { + BT_LOGD_STR("Setting packet's context field's frozen state."); + bt_field_set_is_frozen(packet->context_field->field, + is_frozen); + } + + ((struct bt_packet *) packet)->frozen = is_frozen; +} + +static inline +void reset_packet(struct bt_packet *packet) +{ + BT_ASSERT(packet); + BT_LIB_LOGD("Resetting packet: %!+a", packet); + bt_packet_set_is_frozen(packet, false); + + if (packet->context_field) { + bt_field_set_is_frozen(packet->context_field->field, false); + bt_field_reset(packet->context_field->field); + } +} + +static +void recycle_context_field(struct bt_field_wrapper *context_field, + struct bt_stream_class *stream_class) +{ + BT_ASSERT(context_field); + BT_LIB_LOGD("Recycling packet context field: " + "addr=%p, %![sc-]+S, %![field-]+f", context_field, + stream_class, context_field->field); + bt_object_pool_recycle_object(&stream_class->packet_context_field_pool, + context_field); +} + +BT_HIDDEN +void bt_packet_recycle(struct bt_packet *packet) +{ + struct bt_stream *stream; + + BT_ASSERT(packet); + BT_LIB_LOGD("Recycling packet: %!+a", packet); + + /* + * Those are the important ordered steps: + * + * 1. Reset the packet object (put any permanent reference it + * has, unfreeze it and its fields in developer mode, etc.), + * but do NOT put its stream's reference. This stream + * contains the pool to which we're about to recycle this + * packet object, so we must guarantee its existence thanks + * to this existing reference. + * + * 2. Move the stream reference to our `stream` + * variable so that we can set the packet's stream member + * to NULL before recycling it. We CANNOT do this after + * we put the stream reference because this bt_object_put_ref() + * could destroy the stream, also destroying its + * packet pool, thus also destroying our packet object (this + * would result in an invalid write access). + * + * 3. Recycle the packet object. + * + * 4. Put our stream reference. + */ + reset_packet(packet); + stream = packet->stream; + BT_ASSERT(stream); + packet->stream = NULL; + bt_object_pool_recycle_object(&stream->packet_pool, packet); + bt_object_put_no_null_check(&stream->base); +} + +BT_HIDDEN +void bt_packet_destroy(struct bt_packet *packet) +{ + BT_LIB_LOGD("Destroying packet: %!+a", packet); + + if (packet->context_field) { + if (packet->stream) { + BT_LOGD_STR("Recycling packet's context field."); + recycle_context_field(packet->context_field, + packet->stream->class); + } else { + bt_field_wrapper_destroy(packet->context_field); + } + + packet->context_field = NULL; + } + + BT_LOGD_STR("Putting packet's stream."); + BT_OBJECT_PUT_REF_AND_RESET(packet->stream); + g_free(packet); +} + +BT_HIDDEN +struct bt_packet *bt_packet_new(struct bt_stream *stream) +{ + struct bt_packet *packet = NULL; + struct bt_trace_class *trace_class = NULL; + + BT_ASSERT(stream); + BT_LIB_LOGD("Creating packet object: %![stream-]+s", stream); + packet = g_new0(struct bt_packet, 1); + if (!packet) { + BT_LOGE_STR("Failed to allocate one packet object."); + goto error; + } + + bt_object_init_shared(&packet->base, + (bt_object_release_func) bt_packet_recycle); + packet->stream = stream; + bt_object_get_no_null_check(stream); + trace_class = bt_stream_class_borrow_trace_class_inline(stream->class); + BT_ASSERT(trace_class); + + if (stream->class->packet_context_fc) { + BT_LOGD_STR("Creating initial packet context field."); + packet->context_field = bt_field_wrapper_create( + &stream->class->packet_context_field_pool, + stream->class->packet_context_fc); + if (!packet->context_field) { + BT_LOGE_STR("Cannot create packet context field wrapper."); + goto error; + } + } + + BT_LIB_LOGD("Created packet object: %!+a", packet); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(packet); + +end: + return packet; +} + +struct bt_packet *bt_packet_create(const struct bt_stream *c_stream) +{ + struct bt_packet *packet = NULL; + struct bt_stream *stream = (void *) c_stream; + + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + packet = bt_object_pool_create_object(&stream->packet_pool); + if (unlikely(!packet)) { + BT_LIB_LOGE("Cannot allocate one packet from stream's packet pool: " + "%![stream-]+s", stream); + goto end; + } + + if (likely(!packet->stream)) { + packet->stream = stream; + bt_object_get_no_null_check_no_parent_check( + &packet->stream->base); + } + +end: + return (void *) packet; +} + +enum bt_packet_status bt_packet_move_context_field(struct bt_packet *packet, + struct bt_packet_context_field *context_field) +{ + struct bt_stream_class *stream_class; + struct bt_field_wrapper *field_wrapper = (void *) context_field; + + BT_ASSERT_PRE_NON_NULL(packet, "Packet"); + BT_ASSERT_PRE_NON_NULL(field_wrapper, "Context field"); + BT_ASSERT_PRE_HOT(packet, "Packet", ": %!+a", packet); + stream_class = packet->stream->class; + BT_ASSERT_PRE(stream_class->packet_context_fc, + "Stream class has no packet context field class: %!+S", + stream_class); + BT_ASSERT_PRE(field_wrapper->field->class == + stream_class->packet_context_fc, + "Unexpected packet context field's class: " + "%![fc-]+F, %![expected-fc-]+F", field_wrapper->field->class, + stream_class->packet_context_fc); + + /* Recycle current context field: always exists */ + BT_ASSERT(packet->context_field); + recycle_context_field(packet->context_field, stream_class); + + /* Move new field */ + packet->context_field = field_wrapper; + return BT_PACKET_STATUS_OK; +} + +void bt_packet_get_ref(const struct bt_packet *packet) +{ + bt_object_get_ref(packet); +} + +void bt_packet_put_ref(const struct bt_packet *packet) +{ + bt_object_put_ref(packet); +} diff --git a/src/lib/trace-ir/packet.h b/src/lib/trace-ir/packet.h new file mode 100644 index 00000000..7b19760c --- /dev/null +++ b/src/lib/trace-ir/packet.h @@ -0,0 +1,63 @@ +#ifndef BABELTRACE_TRACE_IR_PACKET_INTERNAL_H +#define BABELTRACE_TRACE_IR_PACKET_INTERNAL_H + +/* + * Copyright 2016-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/assert.h" +#include +#include +#include +#include +#include "lib/object.h" +#include "common/babeltrace.h" +#include "lib/property.h" + +#include "field-wrapper.h" + +struct bt_packet { + struct bt_object base; + struct bt_field_wrapper *context_field; + struct bt_stream *stream; + bool frozen; +}; + +BT_HIDDEN +void _bt_packet_set_is_frozen(const struct bt_packet *packet, bool is_frozen); + +#ifdef BT_DEV_MODE +# define bt_packet_set_is_frozen _bt_packet_set_is_frozen +#else +# define bt_packet_set_is_frozen(_packet, _is_frozen) +#endif /* BT_DEV_MODE */ + +BT_HIDDEN +struct bt_packet *bt_packet_new(struct bt_stream *stream); + +BT_HIDDEN +void bt_packet_recycle(struct bt_packet *packet); + +BT_HIDDEN +void bt_packet_destroy(struct bt_packet *packet); + +#endif /* BABELTRACE_TRACE_IR_PACKET_INTERNAL_H */ diff --git a/src/lib/trace-ir/resolve-field-path.c b/src/lib/trace-ir/resolve-field-path.c new file mode 100644 index 00000000..a7fe29b9 --- /dev/null +++ b/src/lib/trace-ir/resolve-field-path.c @@ -0,0 +1,593 @@ +/* + * Copyright 2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "RESOLVE-FIELD-PATH" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include "common/assert.h" +#include +#include +#include +#include +#include + +#include "field-class.h" +#include "field-path.h" +#include "resolve-field-path.h" + +static +bool find_field_class_recursive(struct bt_field_class *fc, + struct bt_field_class *tgt_fc, struct bt_field_path *field_path) +{ + bool found = false; + + if (tgt_fc == fc) { + found = true; + goto end; + } + + switch (fc->type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_field_class_named_field_class_container *container_fc = + (void *) fc; + uint64_t i; + + for (i = 0; i < container_fc->named_fcs->len; i++) { + struct bt_named_field_class *named_fc = + BT_FIELD_CLASS_NAMED_FC_AT_INDEX( + container_fc, i); + struct bt_field_path_item item = { + .type = BT_FIELD_PATH_ITEM_TYPE_INDEX, + .index = i, + }; + + bt_field_path_append_item(field_path, &item); + found = find_field_class_recursive(named_fc->fc, + tgt_fc, field_path); + if (found) { + goto end; + } + + bt_field_path_remove_last_item(field_path); + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + struct bt_field_class_array *array_fc = (void *) fc; + struct bt_field_path_item item = { + .type = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT, + .index = UINT64_C(-1), + }; + + bt_field_path_append_item(field_path, &item); + found = find_field_class_recursive(array_fc->element_fc, + tgt_fc, field_path); + if (found) { + goto end; + } + + bt_field_path_remove_last_item(field_path); + break; + } + default: + break; + } + +end: + return found; +} + +static +int find_field_class(struct bt_field_class *root_fc, + enum bt_scope root_scope, struct bt_field_class *tgt_fc, + struct bt_field_path **ret_field_path) +{ + int ret = 0; + struct bt_field_path *field_path = NULL; + + if (!root_fc) { + goto end; + } + + field_path = bt_field_path_create(); + if (!field_path) { + ret = -1; + goto end; + } + + field_path->root = root_scope; + if (!find_field_class_recursive(root_fc, tgt_fc, field_path)) { + /* Not found here */ + BT_OBJECT_PUT_REF_AND_RESET(field_path); + } + +end: + *ret_field_path = field_path; + return ret; +} + +static +struct bt_field_path *find_field_class_in_ctx(struct bt_field_class *fc, + struct bt_resolve_field_path_context *ctx) +{ + struct bt_field_path *field_path = NULL; + int ret; + + ret = find_field_class(ctx->packet_context, BT_SCOPE_PACKET_CONTEXT, + fc, &field_path); + if (ret || field_path) { + goto end; + } + + ret = find_field_class(ctx->event_common_context, + BT_SCOPE_EVENT_COMMON_CONTEXT, fc, &field_path); + if (ret || field_path) { + goto end; + } + + ret = find_field_class(ctx->event_specific_context, + BT_SCOPE_EVENT_SPECIFIC_CONTEXT, fc, &field_path); + if (ret || field_path) { + goto end; + } + + ret = find_field_class(ctx->event_payload, BT_SCOPE_EVENT_PAYLOAD, + fc, &field_path); + if (ret || field_path) { + goto end; + } + +end: + return field_path; +} + +BT_ASSERT_PRE_FUNC +static inline +bool target_is_before_source(struct bt_field_path *src_field_path, + struct bt_field_path *tgt_field_path) +{ + bool is_valid = true; + uint64_t src_i = 0, tgt_i = 0; + + if (tgt_field_path->root < src_field_path->root) { + goto end; + } + + if (tgt_field_path->root > src_field_path->root) { + is_valid = false; + goto end; + } + + BT_ASSERT(tgt_field_path->root == src_field_path->root); + + for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && + tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { + struct bt_field_path_item *src_fp_item = + bt_field_path_borrow_item_by_index_inline( + src_field_path, src_i); + struct bt_field_path_item *tgt_fp_item = + bt_field_path_borrow_item_by_index_inline( + tgt_field_path, tgt_i); + + if (src_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX && + tgt_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX) { + if (tgt_fp_item->index > src_fp_item->index) { + is_valid = false; + goto end; + } + } + + src_i++; + tgt_i++; + } + +end: + return is_valid; +} + +BT_ASSERT_PRE_FUNC +static inline +struct bt_field_class *borrow_root_field_class( + struct bt_resolve_field_path_context *ctx, enum bt_scope scope) +{ + switch (scope) { + case BT_SCOPE_PACKET_CONTEXT: + return ctx->packet_context; + case BT_SCOPE_EVENT_COMMON_CONTEXT: + return ctx->event_common_context; + case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: + return ctx->event_specific_context; + case BT_SCOPE_EVENT_PAYLOAD: + return ctx->event_payload; + default: + abort(); + } + + return NULL; +} + +BT_ASSERT_PRE_FUNC +static inline +struct bt_field_class *borrow_child_field_class( + struct bt_field_class *parent_fc, + struct bt_field_path_item *fp_item) +{ + struct bt_field_class *child_fc = NULL; + + switch (parent_fc->type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_named_field_class *named_fc; + + BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX); + named_fc = BT_FIELD_CLASS_NAMED_FC_AT_INDEX(parent_fc, + fp_item->index); + child_fc = named_fc->fc; + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + struct bt_field_class_array *array_fc = (void *) parent_fc; + + BT_ASSERT(fp_item->type == + BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); + child_fc = array_fc->element_fc; + break; + } + default: + break; + } + + return child_fc; +} + +BT_ASSERT_PRE_FUNC +static inline +bool target_field_path_in_different_scope_has_struct_fc_only( + struct bt_field_path *src_field_path, + struct bt_field_path *tgt_field_path, + struct bt_resolve_field_path_context *ctx) +{ + bool is_valid = true; + uint64_t i = 0; + struct bt_field_class *fc; + + if (src_field_path->root == tgt_field_path->root) { + goto end; + } + + fc = borrow_root_field_class(ctx, tgt_field_path->root); + + for (i = 0; i < tgt_field_path->items->len; i++) { + struct bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_inline( + tgt_field_path, i); + + if (fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || + fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY || + fc->type == BT_FIELD_CLASS_TYPE_VARIANT) { + is_valid = false; + goto end; + } + + BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX); + fc = borrow_child_field_class(fc, fp_item); + } + +end: + return is_valid; +} + +BT_ASSERT_PRE_FUNC +static inline +bool lca_is_structure_field_class(struct bt_field_path *src_field_path, + struct bt_field_path *tgt_field_path, + struct bt_resolve_field_path_context *ctx) +{ + bool is_valid = true; + struct bt_field_class *src_fc; + struct bt_field_class *tgt_fc; + struct bt_field_class *prev_fc = NULL; + uint64_t src_i = 0, tgt_i = 0; + + if (src_field_path->root != tgt_field_path->root) { + goto end; + } + + src_fc = borrow_root_field_class(ctx, src_field_path->root); + tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root); + BT_ASSERT(src_fc); + BT_ASSERT(tgt_fc); + + for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && + tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { + struct bt_field_path_item *src_fp_item = + bt_field_path_borrow_item_by_index_inline( + src_field_path, src_i); + struct bt_field_path_item *tgt_fp_item = + bt_field_path_borrow_item_by_index_inline( + tgt_field_path, tgt_i); + + if (src_fc != tgt_fc) { + if (!prev_fc) { + /* + * This is correct: the LCA is the root + * scope field class, which must be a + * structure field class. + */ + break; + } + + if (prev_fc->type != BT_FIELD_CLASS_TYPE_STRUCTURE) { + is_valid = false; + } + + break; + } + + prev_fc = src_fc; + src_fc = borrow_child_field_class(src_fc, src_fp_item); + tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); + } + +end: + return is_valid; +} + +BT_ASSERT_PRE_FUNC +static inline +bool lca_to_target_has_struct_fc_only(struct bt_field_path *src_field_path, + struct bt_field_path *tgt_field_path, + struct bt_resolve_field_path_context *ctx) +{ + bool is_valid = true; + struct bt_field_class *src_fc; + struct bt_field_class *tgt_fc; + uint64_t src_i = 0, tgt_i = 0; + + if (src_field_path->root != tgt_field_path->root) { + goto end; + } + + src_fc = borrow_root_field_class(ctx, src_field_path->root); + tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root); + BT_ASSERT(src_fc); + BT_ASSERT(tgt_fc); + BT_ASSERT(src_fc == tgt_fc); + + /* Find LCA */ + for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len && + tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) { + struct bt_field_path_item *src_fp_item = + bt_field_path_borrow_item_by_index_inline( + src_field_path, src_i); + struct bt_field_path_item *tgt_fp_item = + bt_field_path_borrow_item_by_index_inline( + tgt_field_path, tgt_i); + + if (src_i != tgt_i) { + /* Next field class is different: LCA is `tgt_fc` */ + break; + } + + src_fc = borrow_child_field_class(src_fc, src_fp_item); + tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); + } + + /* Only structure field classes to the target */ + for (; tgt_i < tgt_field_path->items->len; tgt_i++) { + struct bt_field_path_item *tgt_fp_item = + bt_field_path_borrow_item_by_index_inline( + tgt_field_path, tgt_i); + + if (tgt_fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY || + tgt_fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY || + tgt_fc->type == BT_FIELD_CLASS_TYPE_VARIANT) { + is_valid = false; + goto end; + } + + tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item); + } + +end: + return is_valid; +} + +BT_ASSERT_PRE_FUNC +static inline +bool field_path_is_valid(struct bt_field_class *src_fc, + struct bt_field_class *tgt_fc, + struct bt_resolve_field_path_context *ctx) +{ + bool is_valid = true; + struct bt_field_path *src_field_path = find_field_class_in_ctx( + src_fc, ctx); + struct bt_field_path *tgt_field_path = find_field_class_in_ctx( + tgt_fc, ctx); + + if (!src_field_path) { + BT_ASSERT_PRE_MSG("Cannot find requesting field class in " + "resolving context: %!+F", src_fc); + is_valid = false; + goto end; + } + + if (!tgt_field_path) { + BT_ASSERT_PRE_MSG("Cannot find target field class in " + "resolving context: %!+F", tgt_fc); + is_valid = false; + goto end; + } + + /* Target must be before source */ + if (!target_is_before_source(src_field_path, tgt_field_path)) { + BT_ASSERT_PRE_MSG("Target field class is located after " + "requesting field class: %![req-fc-]+F, %![tgt-fc-]+F", + src_fc, tgt_fc); + is_valid = false; + goto end; + } + + /* + * If target is in a different scope than source, there are no + * array or variant field classes on the way to the target. + */ + if (!target_field_path_in_different_scope_has_struct_fc_only( + src_field_path, tgt_field_path, ctx)) { + BT_ASSERT_PRE_MSG("Target field class is located in a " + "different scope than requesting field class, " + "but within an array or a variant field class: " + "%![req-fc-]+F, %![tgt-fc-]+F", + src_fc, tgt_fc); + is_valid = false; + goto end; + } + + /* Same scope: LCA must be a structure field class */ + if (!lca_is_structure_field_class(src_field_path, tgt_field_path, ctx)) { + BT_ASSERT_PRE_MSG("Lowest common ancestor of target and " + "requesting field classes is not a structure field class: " + "%![req-fc-]+F, %![tgt-fc-]+F", + src_fc, tgt_fc); + is_valid = false; + goto end; + } + + /* Same scope: path from LCA to target has no array/variant FTs */ + if (!lca_to_target_has_struct_fc_only(src_field_path, tgt_field_path, + ctx)) { + BT_ASSERT_PRE_MSG("Path from lowest common ancestor of target " + "and requesting field classes to target field class " + "contains an array or a variant field class: " + "%![req-fc-]+F, %![tgt-fc-]+F", src_fc, tgt_fc); + is_valid = false; + goto end; + } + +end: + bt_object_put_ref(src_field_path); + bt_object_put_ref(tgt_field_path); + return is_valid; +} + +static +struct bt_field_path *resolve_field_path(struct bt_field_class *src_fc, + struct bt_field_class *tgt_fc, + struct bt_resolve_field_path_context *ctx) +{ + BT_ASSERT_PRE(field_path_is_valid(src_fc, tgt_fc, ctx), + "Invalid target field class: %![req-fc-]+F, %![tgt-fc-]+F", + src_fc, tgt_fc); + return find_field_class_in_ctx(tgt_fc, ctx); +} + +BT_HIDDEN +int bt_resolve_field_paths(struct bt_field_class *fc, + struct bt_resolve_field_path_context *ctx) +{ + int ret = 0; + + BT_ASSERT(fc); + + /* Resolving part for dynamic array and variant field classes */ + switch (fc->type) { + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + struct bt_field_class_dynamic_array *dyn_array_fc = (void *) fc; + + if (dyn_array_fc->length_fc) { + BT_ASSERT(!dyn_array_fc->length_field_path); + dyn_array_fc->length_field_path = resolve_field_path( + fc, dyn_array_fc->length_fc, ctx); + if (!dyn_array_fc->length_field_path) { + ret = -1; + goto end; + } + } + + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_field_class_variant *var_fc = (void *) fc; + + if (var_fc->selector_fc) { + BT_ASSERT(!var_fc->selector_field_path); + var_fc->selector_field_path = + resolve_field_path(fc, + var_fc->selector_fc, ctx); + if (!var_fc->selector_field_path) { + ret = -1; + goto end; + } + } + } + default: + break; + } + + /* Recursive part */ + switch (fc->type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + case BT_FIELD_CLASS_TYPE_VARIANT: + { + struct bt_field_class_named_field_class_container *container_fc = + (void *) fc; + uint64_t i; + + for (i = 0; i < container_fc->named_fcs->len; i++) { + struct bt_named_field_class *named_fc = + BT_FIELD_CLASS_NAMED_FC_AT_INDEX( + container_fc, i); + + ret = bt_resolve_field_paths(named_fc->fc, ctx); + if (ret) { + goto end; + } + } + + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + struct bt_field_class_array *array_fc = (void *) fc; + + ret = bt_resolve_field_paths(array_fc->element_fc, ctx); + break; + } + default: + break; + } + +end: + return ret; +} diff --git a/src/lib/trace-ir/resolve-field-path.h b/src/lib/trace-ir/resolve-field-path.h new file mode 100644 index 00000000..48f14acf --- /dev/null +++ b/src/lib/trace-ir/resolve-field-path.h @@ -0,0 +1,45 @@ +#ifndef BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL +#define BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL + +/* + * Copyright 2016-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The Common Trace Format (CTF) Specification is available at + * http://www.efficios.com/ctf + */ + +#include "lib/object.h" +#include +#include +#include + +struct bt_resolve_field_path_context { + struct bt_field_class *packet_context; + struct bt_field_class *event_common_context; + struct bt_field_class *event_specific_context; + struct bt_field_class *event_payload; +}; + +BT_HIDDEN +int bt_resolve_field_paths(struct bt_field_class *field_class, + struct bt_resolve_field_path_context *ctx); + +#endif /* BABELTRACE_TRACE_IR_RESOLVE_FIELD_PATH_INTERNAL */ diff --git a/src/lib/trace-ir/stream-class.c b/src/lib/trace-ir/stream-class.c new file mode 100644 index 00000000..67b24aad --- /dev/null +++ b/src/lib/trace-ir/stream-class.c @@ -0,0 +1,593 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "STREAM-CLASS" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include "compat/compiler.h" +#include "common/align.h" +#include "compat/endian.h" +#include "common/assert.h" +#include "lib/property.h" +#include +#include +#include + +#include "clock-class.h" +#include "event-class.h" +#include "field-class.h" +#include "field.h" +#include "field-wrapper.h" +#include "resolve-field-path.h" +#include "stream-class.h" +#include "trace.h" +#include "utils.h" + +#define BT_ASSERT_PRE_STREAM_CLASS_HOT(_sc) \ + BT_ASSERT_PRE_HOT((_sc), "Stream class", ": %!+S", (_sc)) + +static +void destroy_stream_class(struct bt_object *obj) +{ + struct bt_stream_class *stream_class = (void *) obj; + + BT_LIB_LOGD("Destroying stream class: %!+S", stream_class); + BT_LOGD_STR("Putting default clock class."); + BT_OBJECT_PUT_REF_AND_RESET(stream_class->default_clock_class); + + if (stream_class->event_classes) { + BT_LOGD_STR("Destroying event classes."); + g_ptr_array_free(stream_class->event_classes, TRUE); + stream_class->event_classes = NULL; + } + + if (stream_class->name.str) { + g_string_free(stream_class->name.str, TRUE); + stream_class->name.str = NULL; + stream_class->name.value = NULL; + } + + BT_LOGD_STR("Putting packet context field class."); + BT_OBJECT_PUT_REF_AND_RESET(stream_class->packet_context_fc); + BT_LOGD_STR("Putting event common context field class."); + BT_OBJECT_PUT_REF_AND_RESET(stream_class->event_common_context_fc); + bt_object_pool_finalize(&stream_class->packet_context_field_pool); + g_free(stream_class); +} + +static +void free_field_wrapper(struct bt_field_wrapper *field_wrapper, + struct bt_stream_class *stream_class) +{ + bt_field_wrapper_destroy((void *) field_wrapper); +} + +BT_ASSERT_PRE_FUNC +static +bool stream_class_id_is_unique(const struct bt_trace_class *tc, uint64_t id) +{ + uint64_t i; + bool is_unique = true; + + for (i = 0; i < tc->stream_classes->len; i++) { + const struct bt_stream_class *sc = + tc->stream_classes->pdata[i]; + + if (sc->id == id) { + is_unique = false; + goto end; + } + } + +end: + return is_unique; +} + +static +struct bt_stream_class *create_stream_class_with_id( + struct bt_trace_class *tc, uint64_t id) +{ + struct bt_stream_class *stream_class = NULL; + int ret; + + BT_ASSERT(tc); + BT_ASSERT_PRE(stream_class_id_is_unique(tc, id), + "Duplicate stream class ID: %![tc-]+T, id=%" PRIu64, tc, id); + BT_LIB_LOGD("Creating stream class object: %![tc-]+T, id=%" PRIu64, + tc, id); + stream_class = g_new0(struct bt_stream_class, 1); + if (!stream_class) { + BT_LOGE_STR("Failed to allocate one stream class."); + goto error; + } + + bt_object_init_shared_with_parent(&stream_class->base, + destroy_stream_class); + + stream_class->name.str = g_string_new(NULL); + if (!stream_class->name.str) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = -1; + goto end; + } + + stream_class->id = id; + stream_class->assigns_automatic_event_class_id = true; + stream_class->assigns_automatic_stream_id = true; + stream_class->event_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!stream_class->event_classes) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + ret = bt_object_pool_initialize(&stream_class->packet_context_field_pool, + (bt_object_pool_new_object_func) bt_field_wrapper_new, + (bt_object_pool_destroy_object_func) free_field_wrapper, + stream_class); + if (ret) { + BT_LOGE("Failed to initialize packet context field pool: ret=%d", + ret); + goto error; + } + + bt_object_set_parent(&stream_class->base, &tc->base); + g_ptr_array_add(tc->stream_classes, stream_class); + bt_trace_class_freeze(tc); + BT_LIB_LOGD("Created stream class object: %!+S", stream_class); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(stream_class); + +end: + return stream_class; +} + +struct bt_stream_class *bt_stream_class_create(struct bt_trace_class *tc) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE(tc->assigns_automatic_stream_class_id, + "Trace class does not automatically assigns stream class IDs: " + "%![sc-]+T", tc); + return create_stream_class_with_id(tc, + (uint64_t) tc->stream_classes->len); +} + +struct bt_stream_class *bt_stream_class_create_with_id( + struct bt_trace_class *tc, uint64_t id) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE(!tc->assigns_automatic_stream_class_id, + "Trace class automatically assigns stream class IDs: " + "%![sc-]+T", tc); + return create_stream_class_with_id(tc, id); +} + +struct bt_trace_class *bt_stream_class_borrow_trace_class( + struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return bt_stream_class_borrow_trace_class_inline(stream_class); +} + +const struct bt_trace_class *bt_stream_class_borrow_trace_class_const( + const struct bt_stream_class *stream_class) +{ + return bt_stream_class_borrow_trace_class((void *) stream_class); +} + +const char *bt_stream_class_get_name(const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->name.value; +} + +enum bt_stream_class_status bt_stream_class_set_name( + struct bt_stream_class *stream_class, + const char *name) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + g_string_assign(stream_class->name.str, name); + stream_class->name.value = stream_class->name.str->str; + BT_LIB_LOGV("Set stream class's name: %!+S", stream_class); + return BT_STREAM_CLASS_STATUS_OK; +} + +uint64_t bt_stream_class_get_id(const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->id; +} + +uint64_t bt_stream_class_get_event_class_count( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (uint64_t) stream_class->event_classes->len; +} + +struct bt_event_class *bt_stream_class_borrow_event_class_by_index( + struct bt_stream_class *stream_class, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_VALID_INDEX(index, stream_class->event_classes->len); + return g_ptr_array_index(stream_class->event_classes, index); +} + +const struct bt_event_class * +bt_stream_class_borrow_event_class_by_index_const( + const struct bt_stream_class *stream_class, uint64_t index) +{ + return bt_stream_class_borrow_event_class_by_index( + (void *) stream_class, index); +} + +struct bt_event_class *bt_stream_class_borrow_event_class_by_id( + struct bt_stream_class *stream_class, uint64_t id) +{ + struct bt_event_class *event_class = NULL; + uint64_t i; + + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + + for (i = 0; i < stream_class->event_classes->len; i++) { + struct bt_event_class *event_class_candidate = + g_ptr_array_index(stream_class->event_classes, i); + + if (event_class_candidate->id == id) { + event_class = event_class_candidate; + goto end; + } + } + +end: + return event_class; +} + +const struct bt_event_class * +bt_stream_class_borrow_event_class_by_id_const( + const struct bt_stream_class *stream_class, uint64_t id) +{ + return bt_stream_class_borrow_event_class_by_id( + (void *) stream_class, id); +} + +const struct bt_field_class * +bt_stream_class_borrow_packet_context_field_class_const( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->packet_context_fc; +} + +struct bt_field_class * +bt_stream_class_borrow_packet_context_field_class( + struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->packet_context_fc; +} + +enum bt_stream_class_status bt_stream_class_set_packet_context_field_class( + struct bt_stream_class *stream_class, + struct bt_field_class *field_class) +{ + int ret; + struct bt_resolve_field_path_context resolve_ctx = { + .packet_context = field_class, + .event_common_context = NULL, + .event_specific_context = NULL, + .event_payload = NULL, + }; + + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(bt_field_class_get_type(field_class) == + BT_FIELD_CLASS_TYPE_STRUCTURE, + "Packet context field class is not a structure field class: %!+F", + field_class); + ret = bt_resolve_field_paths(field_class, &resolve_ctx); + if (ret) { + /* + * This is the only reason for which + * bt_resolve_field_paths() can fail: anything else + * would be because a precondition is not satisfied. + */ + ret = BT_STREAM_CLASS_STATUS_NOMEM; + goto end; + } + + bt_field_class_make_part_of_trace_class(field_class); + bt_object_put_ref(stream_class->packet_context_fc); + stream_class->packet_context_fc = field_class; + bt_object_get_no_null_check(stream_class->packet_context_fc); + bt_field_class_freeze(field_class); + BT_LIB_LOGV("Set stream class's packet context field class: %!+S", + stream_class); + +end: + return ret; +} + +const struct bt_field_class * +bt_stream_class_borrow_event_common_context_field_class_const( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->event_common_context_fc; +} + +struct bt_field_class * +bt_stream_class_borrow_event_common_context_field_class( + struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->event_common_context_fc; +} + +enum bt_stream_class_status +bt_stream_class_set_event_common_context_field_class( + struct bt_stream_class *stream_class, + struct bt_field_class *field_class) +{ + int ret; + struct bt_resolve_field_path_context resolve_ctx = { + .packet_context = NULL, + .event_common_context = field_class, + .event_specific_context = NULL, + .event_payload = NULL, + }; + + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(field_class, "Field class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(bt_field_class_get_type(field_class) == + BT_FIELD_CLASS_TYPE_STRUCTURE, + "Event common context field class is not a structure field class: %!+F", + field_class); + resolve_ctx.packet_context = stream_class->packet_context_fc; + ret = bt_resolve_field_paths(field_class, &resolve_ctx); + if (ret) { + /* + * This is the only reason for which + * bt_resolve_field_paths() can fail: anything else + * would be because a precondition is not satisfied. + */ + ret = BT_STREAM_CLASS_STATUS_NOMEM; + goto end; + } + + bt_field_class_make_part_of_trace_class(field_class); + bt_object_put_ref(stream_class->event_common_context_fc); + stream_class->event_common_context_fc = field_class; + bt_object_get_no_null_check(stream_class->event_common_context_fc); + bt_field_class_freeze(field_class); + BT_LIB_LOGV("Set stream class's event common context field class: %!+S", + stream_class); + +end: + return ret; +} + +BT_HIDDEN +void _bt_stream_class_freeze(const struct bt_stream_class *stream_class) +{ + /* The field classes and default clock class are already frozen */ + BT_ASSERT(stream_class); + BT_LIB_LOGD("Freezing stream class: %!+S", stream_class); + ((struct bt_stream_class *) stream_class)->frozen = true; +} + +enum bt_stream_class_status bt_stream_class_set_default_clock_class( + struct bt_stream_class *stream_class, + struct bt_clock_class *clock_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(clock_class, "Clock class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + bt_object_put_ref(stream_class->default_clock_class); + stream_class->default_clock_class = clock_class; + bt_object_get_no_null_check(stream_class->default_clock_class); + bt_clock_class_freeze(clock_class); + BT_LIB_LOGV("Set stream class's default clock class: %!+S", + stream_class); + return BT_STREAM_CLASS_STATUS_OK; +} + +struct bt_clock_class *bt_stream_class_borrow_default_clock_class( + struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->default_clock_class; +} + +const struct bt_clock_class *bt_stream_class_borrow_default_clock_class_const( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return stream_class->default_clock_class; +} + +bt_bool bt_stream_class_assigns_automatic_event_class_id( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->assigns_automatic_event_class_id; +} + +void bt_stream_class_set_assigns_automatic_event_class_id( + struct bt_stream_class *stream_class, + bt_bool value) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + stream_class->assigns_automatic_event_class_id = (bool) value; + BT_LIB_LOGV("Set stream class's automatic event class ID " + "assignment property: %!+S", stream_class); +} + +bt_bool bt_stream_class_packets_have_beginning_default_clock_snapshot( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->packets_have_beginning_default_clock_snapshot; +} + +void bt_stream_class_set_packets_have_beginning_default_clock_snapshot( + struct bt_stream_class *stream_class, bt_bool value) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(!value || stream_class->default_clock_class, + "Stream class has no default clock class: %!+S", stream_class); + stream_class->packets_have_beginning_default_clock_snapshot = + (bool) value; + BT_LIB_LOGV("Set stream class's \"packets have default beginning " + "clock snapshot\" property: %!+S", stream_class); +} + +bt_bool bt_stream_class_packets_have_end_default_clock_snapshot( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->packets_have_end_default_clock_snapshot; +} + +void bt_stream_class_set_packets_have_end_default_clock_snapshot( + struct bt_stream_class *stream_class, bt_bool value) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(!value || stream_class->default_clock_class, + "Stream class has no default clock class: %!+S", stream_class); + stream_class->packets_have_end_default_clock_snapshot = + (bool) value; + BT_LIB_LOGV("Set stream class's \"packets have default end " + "clock snapshot\" property: %!+S", stream_class); +} + +bt_bool bt_stream_class_assigns_automatic_stream_id( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->assigns_automatic_stream_id; +} + +void bt_stream_class_set_supports_discarded_events( + struct bt_stream_class *stream_class, + bt_bool supports_discarded_events, + bt_bool with_default_clock_snapshots) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(supports_discarded_events || + !with_default_clock_snapshots, + "Discarded events cannot have default clock snapshots when " + "not supported: %!+S", stream_class); + BT_ASSERT_PRE(!with_default_clock_snapshots || + stream_class->default_clock_class, + "Stream class has no default clock class: %!+S", stream_class); + stream_class->supports_discarded_events = + (bool) supports_discarded_events; + stream_class->discarded_events_have_default_clock_snapshots = + (bool) with_default_clock_snapshots; + BT_LIB_LOGV("Set stream class's discarded events support property: " + "%!+S", stream_class); +} + +bt_bool bt_stream_class_supports_discarded_events( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->supports_discarded_events; +} + +bt_bool bt_stream_class_discarded_events_have_default_clock_snapshots( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->discarded_events_have_default_clock_snapshots; +} + +void bt_stream_class_set_supports_discarded_packets( + struct bt_stream_class *stream_class, + bt_bool supports_discarded_packets, + bt_bool with_default_clock_snapshots) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + BT_ASSERT_PRE(supports_discarded_packets || + !with_default_clock_snapshots, + "Discarded packets cannot have default clock snapshots when " + "not supported: %!+S", stream_class); + BT_ASSERT_PRE(!with_default_clock_snapshots || + stream_class->default_clock_class, + "Stream class has no default clock class: %!+S", stream_class); + stream_class->supports_discarded_packets = + (bool) supports_discarded_packets; + stream_class->discarded_packets_have_default_clock_snapshots = + (bool) with_default_clock_snapshots; + BT_LIB_LOGV("Set stream class's discarded packets support property: " + "%!+S", stream_class); +} + +bt_bool bt_stream_class_supports_discarded_packets( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->supports_discarded_packets; +} + +bt_bool bt_stream_class_discarded_packets_have_default_clock_snapshots( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + return (bt_bool) stream_class->discarded_packets_have_default_clock_snapshots; +} + +void bt_stream_class_set_assigns_automatic_stream_id( + struct bt_stream_class *stream_class, + bt_bool value) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_STREAM_CLASS_HOT(stream_class); + stream_class->assigns_automatic_stream_id = (bool) value; + BT_LIB_LOGV("Set stream class's automatic stream ID " + "assignment property: %!+S", stream_class); +} + +void bt_stream_class_get_ref(const struct bt_stream_class *stream_class) +{ + bt_object_get_ref(stream_class); +} + +void bt_stream_class_put_ref(const struct bt_stream_class *stream_class) +{ + bt_object_put_ref(stream_class); +} diff --git a/src/lib/trace-ir/stream-class.h b/src/lib/trace-ir/stream-class.h new file mode 100644 index 00000000..1efa7707 --- /dev/null +++ b/src/lib/trace-ir/stream-class.h @@ -0,0 +1,88 @@ +#ifndef BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H +#define BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/assert.h" +#include "common/common.h" +#include +#include "lib/object.h" +#include "lib/object-pool.h" +#include "common/babeltrace.h" +#include +#include + +#include "field-class.h" +#include "utils.h" + +struct bt_stream_class { + struct bt_object base; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + uint64_t id; + bool assigns_automatic_event_class_id; + bool assigns_automatic_stream_id; + bool packets_have_beginning_default_clock_snapshot; + bool packets_have_end_default_clock_snapshot; + bool supports_discarded_events; + bool supports_discarded_packets; + bool discarded_events_have_default_clock_snapshots; + bool discarded_packets_have_default_clock_snapshots; + struct bt_field_class *packet_context_fc; + struct bt_field_class *event_common_context_fc; + struct bt_clock_class *default_clock_class; + + /* Array of `struct bt_event_class *` */ + GPtrArray *event_classes; + + /* Pool of `struct bt_field_wrapper *` */ + struct bt_object_pool packet_context_field_pool; + + bool frozen; +}; + +BT_HIDDEN +void _bt_stream_class_freeze(const struct bt_stream_class *stream_class); + +#ifdef BT_DEV_MODE +# define bt_stream_class_freeze _bt_stream_class_freeze +#else +# define bt_stream_class_freeze(_sc) +#endif + +static inline +struct bt_trace_class *bt_stream_class_borrow_trace_class_inline( + const struct bt_stream_class *stream_class) +{ + BT_ASSERT(stream_class); + return (void *) bt_object_borrow_parent(&stream_class->base); +} + +#endif /* BABELTRACE_TRACE_IR_STREAM_CLASS_INTERNAL_H */ diff --git a/src/lib/trace-ir/stream.c b/src/lib/trace-ir/stream.c new file mode 100644 index 00000000..4e017e4a --- /dev/null +++ b/src/lib/trace-ir/stream.c @@ -0,0 +1,243 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "STREAM" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include "compat/compiler.h" +#include "common/align.h" +#include "common/assert.h" +#include "lib/property.h" +#include +#include + +#include "packet.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" + +#define BT_ASSERT_PRE_STREAM_HOT(_stream) \ + BT_ASSERT_PRE_HOT((_stream), "Stream", ": %!+s", (_stream)) + +static +void destroy_stream(struct bt_object *obj) +{ + struct bt_stream *stream = (void *) obj; + + BT_LIB_LOGD("Destroying stream object: %!+s", stream); + + if (stream->name.str) { + g_string_free(stream->name.str, TRUE); + stream->name.str = NULL; + stream->name.value = NULL; + } + + BT_LOGD_STR("Putting stream's class."); + bt_object_put_ref(stream->class); + bt_object_pool_finalize(&stream->packet_pool); + g_free(stream); +} + +static +void bt_stream_free_packet(struct bt_packet *packet, struct bt_stream *stream) +{ + bt_packet_destroy(packet); +} + +BT_ASSERT_PRE_FUNC +static inline +bool stream_id_is_unique(struct bt_trace *trace, + struct bt_stream_class *stream_class, uint64_t id) +{ + uint64_t i; + bool is_unique = true; + + for (i = 0; i < trace->streams->len; i++) { + struct bt_stream *stream = trace->streams->pdata[i]; + + if (stream->class != stream_class) { + continue; + } + + if (stream->id == id) { + is_unique = false; + goto end; + } + } + +end: + return is_unique; +} + +static +struct bt_stream *create_stream_with_id(struct bt_stream_class *stream_class, + struct bt_trace *trace, uint64_t id) +{ + int ret; + struct bt_stream *stream; + + BT_ASSERT(stream_class); + BT_ASSERT(trace); + BT_ASSERT_PRE(trace->class == + bt_stream_class_borrow_trace_class_inline(stream_class), + "Trace's class is different from stream class's parent trace class: " + "%![sc-]+S, %![trace-]+t", stream_class, trace); + BT_ASSERT_PRE(stream_id_is_unique(trace, stream_class, id), + "Duplicate stream ID: %![trace-]+t, id=%" PRIu64, trace, id); + BT_LIB_LOGD("Creating stream object: %![trace-]+t, id=%" PRIu64, + trace, id); + stream = g_new0(struct bt_stream, 1); + if (!stream) { + BT_LOGE_STR("Failed to allocate one stream."); + goto error; + } + + bt_object_init_shared_with_parent(&stream->base, destroy_stream); + stream->name.str = g_string_new(NULL); + if (!stream->name.str) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + stream->id = id; + ret = bt_object_pool_initialize(&stream->packet_pool, + (bt_object_pool_new_object_func) bt_packet_new, + (bt_object_pool_destroy_object_func) bt_stream_free_packet, + stream); + if (ret) { + BT_LOGE("Failed to initialize packet pool: ret=%d", ret); + goto error; + } + + stream->class = stream_class; + bt_object_get_no_null_check(stream_class); + + /* bt_trace_add_stream() sets the parent trace, and freezes the trace */ + bt_trace_add_stream(trace, stream); + + bt_stream_class_freeze(stream_class); + BT_LIB_LOGD("Created stream object: %!+s", stream); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(stream); + +end: + return stream; +} + +struct bt_stream *bt_stream_create(struct bt_stream_class *stream_class, + struct bt_trace *trace) +{ + uint64_t id; + + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE(stream_class->assigns_automatic_stream_id, + "Stream class does not automatically assigns stream IDs: " + "%![sc-]+S", stream_class); + id = bt_trace_get_automatic_stream_id(trace, stream_class); + return create_stream_with_id(stream_class, trace, id); +} + +struct bt_stream *bt_stream_create_with_id(struct bt_stream_class *stream_class, + struct bt_trace *trace, uint64_t id) +{ + BT_ASSERT_PRE_NON_NULL(stream_class, "Stream class"); + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE(!stream_class->assigns_automatic_stream_id, + "Stream class automatically assigns stream IDs: " + "%![sc-]+S", stream_class); + return create_stream_with_id(stream_class, trace, id); +} + +struct bt_stream_class *bt_stream_borrow_class(struct bt_stream *stream) +{ + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + return stream->class; +} + +const struct bt_stream_class *bt_stream_borrow_class_const( + const struct bt_stream *stream) +{ + return bt_stream_borrow_class((void *) stream); +} + +struct bt_trace *bt_stream_borrow_trace(struct bt_stream *stream) +{ + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + return bt_stream_borrow_trace_inline(stream); +} + +const struct bt_trace *bt_stream_borrow_trace_const( + const struct bt_stream *stream) +{ + return bt_stream_borrow_trace((void *) stream); +} + +const char *bt_stream_get_name(const struct bt_stream *stream) +{ + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + return stream->name.value; +} + +enum bt_stream_status bt_stream_set_name(struct bt_stream *stream, + const char *name) +{ + BT_ASSERT_PRE_NON_NULL(stream, "Stream"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_STREAM_HOT(stream); + g_string_assign(stream->name.str, name); + stream->name.value = stream->name.str->str; + BT_LIB_LOGV("Set stream's name: %!+s", stream); + return BT_STREAM_STATUS_OK; +} + +uint64_t bt_stream_get_id(const struct bt_stream *stream) +{ + BT_ASSERT_PRE_NON_NULL(stream, "Stream class"); + return stream->id; +} + +BT_HIDDEN +void _bt_stream_freeze(const struct bt_stream *stream) +{ + BT_ASSERT(stream); + BT_LIB_LOGD("Freezing stream: %!+s", stream); + ((struct bt_stream *) stream)->frozen = true; +} + +void bt_stream_get_ref(const struct bt_stream *stream) +{ + bt_object_get_ref(stream); +} + +void bt_stream_put_ref(const struct bt_stream *stream) +{ + bt_object_put_ref(stream); +} diff --git a/src/lib/trace-ir/stream.h b/src/lib/trace-ir/stream.h new file mode 100644 index 00000000..96985a93 --- /dev/null +++ b/src/lib/trace-ir/stream.h @@ -0,0 +1,75 @@ +#ifndef BABELTRACE_TRACE_IR_STREAM_INTERNAL_H +#define BABELTRACE_TRACE_IR_STREAM_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2013, 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "lib/object.h" +#include "lib/object-pool.h" +#include "common/babeltrace.h" +#include + +#include "utils.h" + +struct bt_stream_class; +struct bt_stream; + +struct bt_stream { + struct bt_object base; + + /* Owned by this */ + struct bt_stream_class *class; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + uint64_t id; + + /* Pool of `struct bt_packet *` */ + struct bt_object_pool packet_pool; + + bool frozen; +}; + +BT_HIDDEN +void _bt_stream_freeze(const struct bt_stream *stream); + +#ifdef BT_DEV_MODE +# define bt_stream_freeze _bt_stream_freeze +#else +# define bt_stream_freeze(_stream) +#endif + +static inline +struct bt_trace *bt_stream_borrow_trace_inline(const struct bt_stream *stream) +{ + BT_ASSERT(stream); + return (void *) bt_object_borrow_parent(&stream->base); +} + +#endif /* BABELTRACE_TRACE_IR_STREAM_INTERNAL_H */ diff --git a/src/lib/trace-ir/trace-class.c b/src/lib/trace-ir/trace-class.c new file mode 100644 index 00000000..f05c7711 --- /dev/null +++ b/src/lib/trace-ir/trace-class.c @@ -0,0 +1,492 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "TRACE" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include "ctf-writer/functor.h" +#include "ctf-writer/clock.h" +#include "compat/compiler.h" +#include +#include +#include "lib/value.h" +#include +#include "compat/endian.h" +#include "common/assert.h" +#include "compat/glib.h" +#include +#include +#include +#include + +#include "attributes.h" +#include "clock-class.h" +#include "event-class.h" +#include "event.h" +#include "field-class.h" +#include "field-wrapper.h" +#include "resolve-field-path.h" +#include "stream-class.h" +#include "stream.h" +#include "trace.h" +#include "utils.h" + +struct bt_trace_class_destruction_listener_elem { + bt_trace_class_destruction_listener_func func; + void *data; +}; + +#define BT_ASSERT_PRE_TRACE_CLASS_HOT(_tc) \ + BT_ASSERT_PRE_HOT((_tc), "Trace class", ": %!+T", (_tc)) + +static +void destroy_trace_class(struct bt_object *obj) +{ + struct bt_trace_class *tc = (void *) obj; + + BT_LIB_LOGD("Destroying trace class object: %!+T", tc); + /* + * Call destruction listener functions so that everything else + * still exists in the trace class. + */ + if (tc->destruction_listeners) { + uint64_t i; + BT_LIB_LOGV("Calling trace class destruction listener(s): %!+T", tc); + + /* + * The trace class' reference count is 0 if we're here. Increment + * it to avoid a double-destroy (possibly infinitely recursive). + * This could happen for example if a destruction listener did + * bt_object_get_ref() (or anything that causes + * bt_object_get_ref() to be called) on the trace class (ref. + * count goes from 0 to 1), and then bt_object_put_ref(): the + * reference count would go from 1 to 0 again and this function + * would be called again. + */ + tc->base.ref_count++; + + /* Call all the trace class destruction listeners */ + for (i = 0; i < tc->destruction_listeners->len; i++) { + struct bt_trace_class_destruction_listener_elem elem = + g_array_index(tc->destruction_listeners, + struct bt_trace_class_destruction_listener_elem, i); + + if (elem.func) { + elem.func(tc, elem.data); + } + + /* + * The destruction listener should not have kept a + * reference to the trace class. + */ + BT_ASSERT_PRE(tc->base.ref_count == 1, "Destruction listener kept a reference to the trace class being destroyed: %![tc-]+T", tc); + } + g_array_free(tc->destruction_listeners, TRUE); + tc->destruction_listeners = NULL; + } + + if (tc->environment) { + BT_LOGD_STR("Destroying environment attributes."); + bt_attributes_destroy(tc->environment); + tc->environment = NULL; + } + + if (tc->name.str) { + g_string_free(tc->name.str, TRUE); + tc->name.str = NULL; + tc->name.value = NULL; + } + + if (tc->stream_classes) { + BT_LOGD_STR("Destroying stream classes."); + g_ptr_array_free(tc->stream_classes, TRUE); + tc->stream_classes = NULL; + } + + g_free(tc); +} + +struct bt_trace_class *bt_trace_class_create(bt_self_component *self_comp) +{ + struct bt_trace_class *tc = NULL; + + BT_ASSERT_PRE_NON_NULL(self_comp, "Self component"); + BT_LOGD_STR("Creating default trace class object."); + tc = g_new0(struct bt_trace_class, 1); + if (!tc) { + BT_LOGE_STR("Failed to allocate one trace class."); + goto error; + } + + bt_object_init_shared_with_parent(&tc->base, destroy_trace_class); + + tc->stream_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!tc->stream_classes) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + tc->name.str = g_string_new(NULL); + if (!tc->name.str) { + BT_LOGE_STR("Failed to allocate one GString."); + goto error; + } + + tc->environment = bt_attributes_create(); + if (!tc->environment) { + BT_LOGE_STR("Cannot create empty attributes object."); + goto error; + } + + tc->destruction_listeners = g_array_new(FALSE, TRUE, + sizeof(struct bt_trace_class_destruction_listener_elem)); + if (!tc->destruction_listeners) { + BT_LOGE_STR("Failed to allocate one GArray."); + goto error; + } + + tc->assigns_automatic_stream_class_id = true; + BT_LIB_LOGD("Created trace class object: %!+T", tc); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(tc); + +end: + return tc; +} + +const char *bt_trace_class_get_name(const struct bt_trace_class *tc) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + return tc->name.value; +} + +enum bt_trace_class_status bt_trace_class_set_name( + struct bt_trace_class *tc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); + g_string_assign(tc->name.str, name); + tc->name.value = tc->name.str->str; + BT_LIB_LOGV("Set trace class's name: %!+T", tc); + return BT_TRACE_CLASS_STATUS_OK; +} + +bt_uuid bt_trace_class_get_uuid(const struct bt_trace_class *tc) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + return tc->uuid.value; +} + +void bt_trace_class_set_uuid(struct bt_trace_class *tc, bt_uuid uuid) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(uuid, "UUID"); + BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); + memcpy(tc->uuid.uuid, uuid, BABELTRACE_UUID_LEN); + tc->uuid.value = tc->uuid.uuid; + BT_LIB_LOGV("Set trace class's UUID: %!+T", tc); +} + +enum bt_trace_class_status bt_trace_class_add_destruction_listener( + const struct bt_trace_class *_tc, + bt_trace_class_destruction_listener_func listener, + void *data, uint64_t *listener_id) +{ + struct bt_trace_class *tc = (void *) _tc; + uint64_t i; + struct bt_trace_class_destruction_listener_elem new_elem = { + .func = listener, + .data = data, + }; + + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(listener, "Listener"); + + /* Find the next available spot */ + for (i = 0; i < tc->destruction_listeners->len; i++) { + struct bt_trace_class_destruction_listener_elem elem = + g_array_index(tc->destruction_listeners, + struct bt_trace_class_destruction_listener_elem, i); + + if (!elem.func) { + break; + } + } + + if (i == tc->destruction_listeners->len) { + g_array_append_val(tc->destruction_listeners, new_elem); + } else { + g_array_insert_val(tc->destruction_listeners, i, new_elem); + } + + if (listener_id) { + *listener_id = i; + } + + BT_LIB_LOGV("Added trace class destruction listener: %![tc-]+T, " + "listener-id=%" PRIu64, tc, i); + return BT_TRACE_CLASS_STATUS_OK; +} + +BT_ASSERT_PRE_FUNC +static +bool has_listener_id(const struct bt_trace_class *tc, uint64_t listener_id) +{ + BT_ASSERT(listener_id < tc->destruction_listeners->len); + return (&g_array_index(tc->destruction_listeners, + struct bt_trace_class_destruction_listener_elem, + listener_id))->func != NULL; +} + +enum bt_trace_class_status bt_trace_class_remove_destruction_listener( + const struct bt_trace_class *_tc, uint64_t listener_id) +{ + struct bt_trace_class *tc = (void *) _tc; + struct bt_trace_class_destruction_listener_elem *elem; + + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE(has_listener_id(tc, listener_id), + "Trace class has no such trace class destruction listener ID: " + "%![tc-]+T, %" PRIu64, tc, listener_id); + elem = &g_array_index(tc->destruction_listeners, + struct bt_trace_class_destruction_listener_elem, + listener_id); + BT_ASSERT(elem->func); + + elem->func = NULL; + elem->data = NULL; + BT_LIB_LOGV("Removed trace class destruction listener: " + "%![tc-]+T, listener-id=%" PRIu64, + tc, listener_id); + return BT_TRACE_CLASS_STATUS_OK; +} + +BT_ASSERT_FUNC +static +bool trace_has_environment_entry(const struct bt_trace_class *tc, const char *name) +{ + BT_ASSERT(tc); + + return bt_attributes_borrow_field_value_by_name( + tc->environment, name) != NULL; +} + +static +enum bt_trace_class_status set_environment_entry(struct bt_trace_class *tc, + const char *name, struct bt_value *value) +{ + int ret; + + BT_ASSERT(tc); + BT_ASSERT(name); + BT_ASSERT(value); + BT_ASSERT_PRE(!tc->frozen || + !trace_has_environment_entry(tc, name), + "Trace class is frozen: cannot replace environment entry: " + "%![tc-]+T, entry-name=\"%s\"", tc, name); + ret = bt_attributes_set_field_value(tc->environment, name, + value); + if (ret) { + ret = BT_TRACE_CLASS_STATUS_NOMEM; + BT_LIB_LOGE("Cannot set trace class's environment entry: " + "%![tc-]+T, entry-name=\"%s\"", tc, name); + } else { + bt_value_freeze(value); + BT_LIB_LOGV("Set trace class's environment entry: " + "%![tc-]+T, entry-name=\"%s\"", tc, name); + } + + return ret; +} + +enum bt_trace_class_status bt_trace_class_set_environment_entry_string( + struct bt_trace_class *tc, const char *name, const char *value) +{ + int ret; + struct bt_value *value_obj; + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(value, "Value"); + value_obj = bt_value_string_create_init(value); + if (!value_obj) { + BT_LOGE_STR("Cannot create a string value object."); + ret = -1; + goto end; + } + + /* set_environment_entry() logs errors */ + ret = set_environment_entry(tc, name, value_obj); + +end: + bt_object_put_ref(value_obj); + return ret; +} + +enum bt_trace_class_status bt_trace_class_set_environment_entry_integer( + struct bt_trace_class *tc, const char *name, int64_t value) +{ + int ret; + struct bt_value *value_obj; + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + value_obj = bt_value_signed_integer_create_init(value); + if (!value_obj) { + BT_LOGE_STR("Cannot create an integer value object."); + ret = BT_TRACE_CLASS_STATUS_NOMEM; + goto end; + } + + /* set_environment_entry() logs errors */ + ret = set_environment_entry(tc, name, value_obj); + +end: + bt_object_put_ref(value_obj); + return ret; +} + +uint64_t bt_trace_class_get_environment_entry_count(const struct bt_trace_class *tc) +{ + int64_t ret; + + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + ret = bt_attributes_get_count(tc->environment); + BT_ASSERT(ret >= 0); + return (uint64_t) ret; +} + +void bt_trace_class_borrow_environment_entry_by_index_const( + const struct bt_trace_class *tc, uint64_t index, + const char **name, const struct bt_value **value) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_NON_NULL(value, "Value"); + BT_ASSERT_PRE_VALID_INDEX(index, + bt_attributes_get_count(tc->environment)); + *value = bt_attributes_borrow_field_value(tc->environment, index); + BT_ASSERT(*value); + *name = bt_attributes_get_field_name(tc->environment, index); + BT_ASSERT(*name); +} + +const struct bt_value *bt_trace_class_borrow_environment_entry_value_by_name_const( + const struct bt_trace_class *tc, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + return bt_attributes_borrow_field_value_by_name(tc->environment, + name); +} + +uint64_t bt_trace_class_get_stream_class_count(const struct bt_trace_class *tc) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + return (uint64_t) tc->stream_classes->len; +} + +struct bt_stream_class *bt_trace_class_borrow_stream_class_by_index( + struct bt_trace_class *tc, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_VALID_INDEX(index, tc->stream_classes->len); + return g_ptr_array_index(tc->stream_classes, index); +} + +const struct bt_stream_class * +bt_trace_class_borrow_stream_class_by_index_const( + const struct bt_trace_class *tc, uint64_t index) +{ + return bt_trace_class_borrow_stream_class_by_index( + (void *) tc, index); +} + +struct bt_stream_class *bt_trace_class_borrow_stream_class_by_id( + struct bt_trace_class *tc, uint64_t id) +{ + struct bt_stream_class *stream_class = NULL; + uint64_t i; + + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + + for (i = 0; i < tc->stream_classes->len; i++) { + struct bt_stream_class *stream_class_candidate = + g_ptr_array_index(tc->stream_classes, i); + + if (stream_class_candidate->id == id) { + stream_class = stream_class_candidate; + goto end; + } + } + +end: + return stream_class; +} + +const struct bt_stream_class * +bt_trace_class_borrow_stream_class_by_id_const( + const struct bt_trace_class *tc, uint64_t id) +{ + return bt_trace_class_borrow_stream_class_by_id((void *) tc, id); +} + +BT_HIDDEN +void _bt_trace_class_freeze(const struct bt_trace_class *tc) +{ + BT_ASSERT(tc); + BT_LIB_LOGD("Freezing trace class: %!+T", tc); + ((struct bt_trace_class *) tc)->frozen = true; +} + +bt_bool bt_trace_class_assigns_automatic_stream_class_id(const struct bt_trace_class *tc) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + return (bt_bool) tc->assigns_automatic_stream_class_id; +} + +void bt_trace_class_set_assigns_automatic_stream_class_id(struct bt_trace_class *tc, + bt_bool value) +{ + BT_ASSERT_PRE_NON_NULL(tc, "Trace class"); + BT_ASSERT_PRE_TRACE_CLASS_HOT(tc); + tc->assigns_automatic_stream_class_id = (bool) value; + BT_LIB_LOGV("Set trace class's automatic stream class ID " + "assignment property: %!+T", tc); +} + +void bt_trace_class_get_ref(const struct bt_trace_class *trace_class) +{ + bt_object_get_ref(trace_class); +} + +void bt_trace_class_put_ref(const struct bt_trace_class *trace_class) +{ + bt_object_put_ref(trace_class); +} diff --git a/src/lib/trace-ir/trace-class.h b/src/lib/trace-ir/trace-class.h new file mode 100644 index 00000000..7c8add5c --- /dev/null +++ b/src/lib/trace-ir/trace-class.h @@ -0,0 +1,80 @@ +#ifndef BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H +#define BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/assert-pre.h" +#include +#include +#include +#include "lib/object.h" +#include "lib/object-pool.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include "compat/uuid.h" + +#include "stream-class.h" +#include "attributes.h" +#include "clock-class.h" + +struct bt_trace_class { + struct bt_object base; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + struct { + uint8_t uuid[BABELTRACE_UUID_LEN]; + + /* NULL or `uuid` above */ + bt_uuid value; + } uuid; + + struct bt_value *environment; + + /* Array of `struct bt_stream_class *` */ + GPtrArray *stream_classes; + + bool assigns_automatic_stream_class_id; + GArray *destruction_listeners; + bool frozen; +}; + +BT_HIDDEN +void _bt_trace_class_freeze(const struct bt_trace_class *trace_class); + +#ifdef BT_DEV_MODE +# define bt_trace_class_freeze _bt_trace_class_freeze +#else +# define bt_trace_class_freeze(_tc) +#endif + +#endif /* BABELTRACE_TRACE_IR_TRACE_CLASS_INTERNAL_H */ diff --git a/src/lib/trace-ir/trace.c b/src/lib/trace-ir/trace.c new file mode 100644 index 00000000..6dadb10d --- /dev/null +++ b/src/lib/trace-ir/trace.c @@ -0,0 +1,394 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "TRACE" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include "ctf-writer/functor.h" +#include "ctf-writer/clock.h" +#include "compat/compiler.h" +#include +#include +#include "lib/value.h" +#include +#include "compat/endian.h" +#include "common/assert.h" +#include "compat/glib.h" +#include +#include +#include +#include + +#include "attributes.h" +#include "clock-class.h" +#include "event-class.h" +#include "event.h" +#include "field-class.h" +#include "field-wrapper.h" +#include "resolve-field-path.h" +#include "stream-class.h" +#include "stream.h" +#include "trace-class.h" +#include "trace.h" +#include "utils.h" + +struct bt_trace_destruction_listener_elem { + bt_trace_destruction_listener_func func; + void *data; +}; + +#define BT_ASSERT_PRE_TRACE_HOT(_trace) \ + BT_ASSERT_PRE_HOT((_trace), "Trace", ": %!+t", (_trace)) + +static +void destroy_trace(struct bt_object *obj) +{ + struct bt_trace *trace = (void *) obj; + + BT_LIB_LOGD("Destroying trace object: %!+t", trace); + + /* + * Call destruction listener functions so that everything else + * still exists in the trace. + */ + if (trace->destruction_listeners) { + uint64_t i; + BT_LIB_LOGV("Calling trace destruction listener(s): %!+t", trace); + + /* + * The trace's reference count is 0 if we're here. Increment + * it to avoid a double-destroy (possibly infinitely recursive). + * This could happen for example if a destruction listener did + * bt_object_get_ref() (or anything that causes + * bt_object_get_ref() to be called) on the trace (ref. + * count goes from 0 to 1), and then bt_object_put_ref(): the + * reference count would go from 1 to 0 again and this function + * would be called again. + */ + trace->base.ref_count++; + + /* Call all the trace destruction listeners */ + for (i = 0; i < trace->destruction_listeners->len; i++) { + struct bt_trace_destruction_listener_elem elem = + g_array_index(trace->destruction_listeners, + struct bt_trace_destruction_listener_elem, i); + + if (elem.func) { + elem.func(trace, elem.data); + } + + /* + * The destruction listener should not have kept a + * reference to the trace. + */ + BT_ASSERT_PRE(trace->base.ref_count == 1, "Destruction listener kept a reference to the trace being destroyed: %![trace-]+t", trace); + } + g_array_free(trace->destruction_listeners, TRUE); + trace->destruction_listeners = NULL; + } + + if (trace->name.str) { + g_string_free(trace->name.str, TRUE); + trace->name.str = NULL; + trace->name.value = NULL; + } + + if (trace->streams) { + BT_LOGD_STR("Destroying streams."); + g_ptr_array_free(trace->streams, TRUE); + trace->streams = NULL; + } + + if (trace->stream_classes_stream_count) { + g_hash_table_destroy(trace->stream_classes_stream_count); + trace->stream_classes_stream_count = NULL; + } + + BT_LOGD_STR("Putting trace's class."); + bt_object_put_ref(trace->class); + trace->class = NULL; + g_free(trace); +} + +struct bt_trace *bt_trace_create(struct bt_trace_class *tc) +{ + struct bt_trace *trace = NULL; + + BT_LIB_LOGD("Creating trace object: %![tc-]+T", tc); + trace = g_new0(struct bt_trace, 1); + if (!trace) { + BT_LOGE_STR("Failed to allocate one trace."); + goto error; + } + + bt_object_init_shared(&trace->base, destroy_trace); + trace->streams = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_try_spec_release); + if (!trace->streams) { + BT_LOGE_STR("Failed to allocate one GPtrArray."); + goto error; + } + + trace->stream_classes_stream_count = g_hash_table_new(g_direct_hash, + g_direct_equal); + if (!trace->stream_classes_stream_count) { + BT_LOGE_STR("Failed to allocate one GHashTable."); + goto error; + } + + trace->name.str = g_string_new(NULL); + if (!trace->name.str) { + BT_LOGE_STR("Failed to allocate one GString."); + goto error; + } + + trace->destruction_listeners = g_array_new(FALSE, TRUE, + sizeof(struct bt_trace_destruction_listener_elem)); + if (!trace->destruction_listeners) { + BT_LOGE_STR("Failed to allocate one GArray."); + goto error; + } + + trace->class = tc; + bt_object_get_no_null_check(trace->class); + BT_LIB_LOGD("Created trace object: %!+t", trace); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(trace); + +end: + return trace; +} + +const char *bt_trace_get_name(const struct bt_trace *trace) +{ + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->name.value; +} + +enum bt_trace_status bt_trace_set_name(struct bt_trace *trace, const char *name) +{ + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE_NON_NULL(name, "Name"); + BT_ASSERT_PRE_TRACE_HOT(trace); + g_string_assign(trace->name.str, name); + trace->name.value = trace->name.str->str; + BT_LIB_LOGV("Set trace's name: %!+t", trace); + return BT_TRACE_STATUS_OK; +} + +uint64_t bt_trace_get_stream_count(const struct bt_trace *trace) +{ + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + return (uint64_t) trace->streams->len; +} + +struct bt_stream *bt_trace_borrow_stream_by_index( + struct bt_trace *trace, uint64_t index) +{ + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE_VALID_INDEX(index, trace->streams->len); + return g_ptr_array_index(trace->streams, index); +} + +const struct bt_stream *bt_trace_borrow_stream_by_index_const( + const struct bt_trace *trace, uint64_t index) +{ + return bt_trace_borrow_stream_by_index((void *) trace, index); +} + +struct bt_stream *bt_trace_borrow_stream_by_id(struct bt_trace *trace, + uint64_t id) +{ + struct bt_stream *stream = NULL; + uint64_t i; + + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + + for (i = 0; i < trace->streams->len; i++) { + struct bt_stream *stream_candidate = + g_ptr_array_index(trace->streams, i); + + if (stream_candidate->id == id) { + stream = stream_candidate; + goto end; + } + } + +end: + return stream; +} + +const struct bt_stream *bt_trace_borrow_stream_by_id_const( + const struct bt_trace *trace, uint64_t id) +{ + return bt_trace_borrow_stream_by_id((void *) trace, id); +} + +enum bt_trace_status bt_trace_add_destruction_listener( + const struct bt_trace *c_trace, + bt_trace_destruction_listener_func listener, + void *data, uint64_t *listener_id) +{ + struct bt_trace *trace = (void *) c_trace; + uint64_t i; + struct bt_trace_destruction_listener_elem new_elem = { + .func = listener, + .data = data, + }; + + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE_NON_NULL(listener, "Listener"); + + /* Find the next available spot */ + for (i = 0; i < trace->destruction_listeners->len; i++) { + struct bt_trace_destruction_listener_elem elem = + g_array_index(trace->destruction_listeners, + struct bt_trace_destruction_listener_elem, i); + + if (!elem.func) { + break; + } + } + + if (i == trace->destruction_listeners->len) { + g_array_append_val(trace->destruction_listeners, new_elem); + } else { + g_array_insert_val(trace->destruction_listeners, i, new_elem); + } + + if (listener_id) { + *listener_id = i; + } + + BT_LIB_LOGV("Added destruction listener: " "%![trace-]+t, " + "listener-id=%" PRIu64, trace, i); + return BT_TRACE_STATUS_OK; +} + +BT_ASSERT_PRE_FUNC +static +bool has_listener_id(const struct bt_trace *trace, uint64_t listener_id) +{ + BT_ASSERT(listener_id < trace->destruction_listeners->len); + return (&g_array_index(trace->destruction_listeners, + struct bt_trace_destruction_listener_elem, + listener_id))->func != NULL; +} + +enum bt_trace_status bt_trace_remove_destruction_listener( + const struct bt_trace *c_trace, uint64_t listener_id) +{ + struct bt_trace *trace = (void *) c_trace; + struct bt_trace_destruction_listener_elem *elem; + + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + BT_ASSERT_PRE(has_listener_id(trace, listener_id), + "Trace has no such trace destruction listener ID: " + "%![trace-]+t, %" PRIu64, trace, listener_id); + elem = &g_array_index(trace->destruction_listeners, + struct bt_trace_destruction_listener_elem, + listener_id); + BT_ASSERT(elem->func); + + elem->func = NULL; + elem->data = NULL; + BT_LIB_LOGV("Removed \"trace destruction listener: " + "%![trace-]+t, listener-id=%" PRIu64, + trace, listener_id); + return BT_TRACE_STATUS_OK; +} + +BT_HIDDEN +void _bt_trace_freeze(const struct bt_trace *trace) +{ + BT_ASSERT(trace); + BT_LIB_LOGD("Freezing trace's class: %!+T", trace->class); + bt_trace_class_freeze(trace->class); + BT_LIB_LOGD("Freezing trace: %!+t", trace); + ((struct bt_trace *) trace)->frozen = true; +} + +BT_HIDDEN +void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream) +{ + guint count = 0; + + bt_object_set_parent(&stream->base, &trace->base); + g_ptr_array_add(trace->streams, stream); + bt_trace_freeze(trace); + + if (bt_g_hash_table_contains(trace->stream_classes_stream_count, + stream->class)) { + count = GPOINTER_TO_UINT(g_hash_table_lookup( + trace->stream_classes_stream_count, stream->class)); + } + + g_hash_table_insert(trace->stream_classes_stream_count, + stream->class, GUINT_TO_POINTER(count + 1)); +} + +BT_HIDDEN +uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace, + const struct bt_stream_class *stream_class) +{ + gpointer orig_key; + gpointer value; + uint64_t id = 0; + + BT_ASSERT(stream_class); + BT_ASSERT(trace); + if (g_hash_table_lookup_extended(trace->stream_classes_stream_count, + stream_class, &orig_key, &value)) { + id = (uint64_t) GPOINTER_TO_UINT(value); + } + + return id; +} + +struct bt_trace_class *bt_trace_borrow_class(struct bt_trace *trace) +{ + BT_ASSERT_PRE_NON_NULL(trace, "Trace"); + return trace->class; +} + +const struct bt_trace_class *bt_trace_borrow_class_const( + const struct bt_trace *trace) +{ + return bt_trace_borrow_class((void *) trace); +} + +void bt_trace_get_ref(const struct bt_trace *trace) +{ + bt_object_get_ref(trace); +} + +void bt_trace_put_ref(const struct bt_trace *trace) +{ + bt_object_put_ref(trace); +} diff --git a/src/lib/trace-ir/trace.h b/src/lib/trace-ir/trace.h new file mode 100644 index 00000000..3a12fd78 --- /dev/null +++ b/src/lib/trace-ir/trace.h @@ -0,0 +1,88 @@ +#ifndef BABELTRACE_TRACE_IR_TRACE_INTERNAL_H +#define BABELTRACE_TRACE_IR_TRACE_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2014 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/assert-pre.h" +#include +#include +#include +#include "lib/object.h" +#include "lib/object-pool.h" +#include "common/babeltrace.h" +#include +#include +#include +#include +#include "compat/uuid.h" + +#include "attributes.h" +#include "clock-class.h" +#include "stream-class.h" +#include "trace-class.h" + +struct bt_trace { + struct bt_object base; + + /* Owned by this */ + struct bt_trace_class *class; + + struct { + GString *str; + + /* NULL or `str->str` above */ + const char *value; + } name; + + /* Array of `struct bt_stream *` */ + GPtrArray *streams; + + /* + * Stream class (weak, owned by owned trace class) to number of + * instantiated streams, used to automatically assign stream IDs + * per stream class within this trace. + */ + GHashTable *stream_classes_stream_count; + + GArray *destruction_listeners; + bool frozen; +}; + +BT_HIDDEN +void _bt_trace_freeze(const struct bt_trace *trace); + +#ifdef BT_DEV_MODE +# define bt_trace_freeze _bt_trace_freeze +#else +# define bt_trace_freeze(_trace) +#endif + +BT_HIDDEN +void bt_trace_add_stream(struct bt_trace *trace, struct bt_stream *stream); + +BT_HIDDEN +uint64_t bt_trace_get_automatic_stream_id(const struct bt_trace *trace, + const struct bt_stream_class *stream_class); + +#endif /* BABELTRACE_TRACE_IR_TRACE_INTERNAL_H */ diff --git a/src/lib/trace-ir/utils.c b/src/lib/trace-ir/utils.c new file mode 100644 index 00000000..3b446e01 --- /dev/null +++ b/src/lib/trace-ir/utils.c @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2018 Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "TRACE-IR-UTILS" +#include "lib/lib-logging.h" + +#include +#include +#include +#include "common/assert.h" + +#include "field-class.h" diff --git a/src/lib/trace-ir/utils.h b/src/lib/trace-ir/utils.h new file mode 100644 index 00000000..d68bd54b --- /dev/null +++ b/src/lib/trace-ir/utils.h @@ -0,0 +1,176 @@ +#ifndef BABELTRACE_TRACE_IR_UTILS_INTERNAL_H +#define BABELTRACE_TRACE_IR_UTILS_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include + +#include "clock-class.h" + +struct search_query { + gpointer value; + int found; +}; + +static inline +uint64_t bt_util_ns_from_value(uint64_t frequency, uint64_t value_cycles) +{ + uint64_t ns; + + if (frequency == UINT64_C(1000000000)) { + ns = value_cycles; + } else { + double dblres = ((1e9 * (double) value_cycles) / (double) frequency); + + if (dblres >= (double) UINT64_MAX) { + /* Overflows uint64_t */ + ns = UINT64_C(-1); + } else { + ns = (uint64_t) dblres; + } + } + + return ns; +} + +static inline +bool bt_util_get_base_offset_ns(int64_t offset_seconds, uint64_t offset_cycles, + uint64_t frequency, int64_t *base_offset_ns) +{ + bool overflows = false; + uint64_t offset_cycles_ns; + + BT_ASSERT(base_offset_ns); + + /* Initialize nanosecond timestamp to clock's offset in seconds */ + if (offset_seconds <= (INT64_MIN / INT64_C(1000000000) - 1) || + offset_seconds >= (INT64_MAX / INT64_C(1000000000)) - 1) { + /* + * Overflow: offset in seconds converted to nanoseconds + * is outside the int64_t range. We also subtract 1 here + * to leave "space" for the offset in cycles converted + * to nanoseconds (which is always less than 1 second by + * contract). + */ + overflows = true; + goto end; + } + + /* Offset (seconds) to nanoseconds */ + *base_offset_ns = offset_seconds * INT64_C(1000000000); + + /* Add offset in cycles */ + BT_ASSERT(offset_cycles < frequency); + offset_cycles_ns = bt_util_ns_from_value(frequency, + offset_cycles); + BT_ASSERT(offset_cycles_ns < 1000000000); + *base_offset_ns += (int64_t) offset_cycles_ns; + +end: + return overflows; +} + +static inline +int bt_util_ns_from_origin_inline(int64_t base_offset_ns, + int64_t offset_seconds, uint64_t offset_cycles, + uint64_t frequency, uint64_t value, int64_t *ns_from_origin) +{ + int ret = 0; + uint64_t value_ns_unsigned; + int64_t value_ns_signed; + + /* Initialize to clock class's base offset */ + *ns_from_origin = base_offset_ns; + + /* Add given value in cycles */ + value_ns_unsigned = bt_util_ns_from_value(frequency, value); + if (value_ns_unsigned >= (uint64_t) INT64_MAX) { + /* + * FIXME: `value_ns_unsigned` could be greater than + * `INT64_MAX` in fact: in this case, we need to + * subtract `INT64_MAX` from `value_ns_unsigned`, make + * sure that the difference is less than `INT64_MAX`, + * and try to add them one after the other to + * `*ns_from_origin`. + */ + ret = -1; + goto end; + } + + value_ns_signed = (int64_t) value_ns_unsigned; + BT_ASSERT(value_ns_signed >= 0); + + if (*ns_from_origin <= 0) { + goto add_value; + } + + if (value_ns_signed > INT64_MAX - *ns_from_origin) { + ret = -1; + goto end; + } + +add_value: + *ns_from_origin += value_ns_signed; + +end: + return ret; +} + +static inline +int bt_util_ns_from_origin_clock_class(const struct bt_clock_class *clock_class, + uint64_t value, int64_t *ns_from_origin) +{ + int ret = 0; + + if (clock_class->base_offset.overflows) { + ret = -1; + goto end; + } + + ret = bt_util_ns_from_origin_inline(clock_class->base_offset.value_ns, + clock_class->offset_seconds, clock_class->offset_cycles, + clock_class->frequency, value, ns_from_origin); + +end: + return ret; +} + +static inline +bool bt_util_value_is_in_range_signed(uint64_t size, int64_t value) +{ + int64_t min_value = UINT64_C(-1) << (size - 1); + int64_t max_value = (UINT64_C(1) << (size - 1)) - 1; + return value >= min_value && value <= max_value; +} + +static inline +bool bt_util_value_is_in_range_unsigned(unsigned int size, uint64_t value) +{ + uint64_t max_value = (size == 64) ? UINT64_MAX : + (UINT64_C(1) << size) - 1; + return value <= max_value; +} + +#endif /* BABELTRACE_TRACE_IR_UTILS_INTERNAL_H */ diff --git a/src/lib/util.c b/src/lib/util.c new file mode 100644 index 00000000..b17fe2f3 --- /dev/null +++ b/src/lib/util.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "UTIL" +#include "lib/lib-logging.h" + +#include "lib/assert-pre.h" +#include +#include +#include +#include +#include +#include "lib/trace-ir/utils.h" + +bt_util_status bt_util_clock_cycles_to_ns_from_origin(uint64_t cycles, + uint64_t frequency, int64_t offset_seconds, + uint64_t offset_cycles, int64_t *ns) +{ + bool overflows; + int64_t base_offset_ns; + bt_util_status status = BT_UTIL_STATUS_OK; + int ret; + + BT_ASSERT_PRE_NON_NULL(ns, "Nanoseconds (output)"); + BT_ASSERT_PRE(frequency != UINT64_C(-1) && frequency != 0, + "Invalid frequency: freq=%" PRIu64, frequency); + BT_ASSERT_PRE(offset_cycles < frequency, + "Offset (cycles) is greater than frequency: " + "offset-cycles=%" PRIu64 ", freq=%" PRIu64, + offset_cycles, frequency); + + overflows = bt_util_get_base_offset_ns(offset_seconds, offset_cycles, + frequency, &base_offset_ns); + if (overflows) { + status = BT_UTIL_STATUS_OVERFLOW; + goto end; + } + + ret = bt_util_ns_from_origin_inline(base_offset_ns, + offset_seconds, offset_cycles, + frequency, cycles, ns); + if (ret) { + status = BT_UTIL_STATUS_OVERFLOW; + } + +end: + return status; +} diff --git a/src/lib/value.c b/src/lib/value.c new file mode 100644 index 00000000..ca1687fa --- /dev/null +++ b/src/lib/value.c @@ -0,0 +1,1369 @@ +/* + * Copyright (c) 2015-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "VALUES" +#include "lib/lib-logging.h" + +#include +#include +#include +#include +#include "compat/compiler.h" +#include "common/common.h" +#include +#include +#include "compat/glib.h" +#include +#include "lib/assert-pre.h" +#include "lib/value.h" +#include "common/assert.h" + +#define BT_VALUE_TO_BOOL(_base) ((struct bt_value_bool *) (_base)) +#define BT_VALUE_TO_INTEGER(_base) ((struct bt_value_integer *) (_base)) +#define BT_VALUE_TO_REAL(_base) ((struct bt_value_real *) (_base)) +#define BT_VALUE_TO_STRING(_base) ((struct bt_value_string *) (_base)) +#define BT_VALUE_TO_ARRAY(_base) ((struct bt_value_array *) (_base)) +#define BT_VALUE_TO_MAP(_base) ((struct bt_value_map *) (_base)) + +#define BT_ASSERT_PRE_VALUE_IS_TYPE(_value, _type) \ + BT_ASSERT_PRE(((struct bt_value *) (_value))->type == (_type), \ + "Value has the wrong type ID: expected-type=%s, " \ + "%![value-]+v", bt_common_value_type_string(_type), \ + (_value)) + +#define BT_ASSERT_PRE_VALUE_HOT(_value, _name) \ + BT_ASSERT_PRE_HOT(((struct bt_value *) (_value)), (_name), \ + ": %!+v", (_value)) + +#define BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(_index, _count) \ + BT_ASSERT_PRE((_index) < (_count), \ + "Index is out of bound: " \ + "index=%" PRIu64 ", count=%u", (_index), (_count)); + +static +void bt_value_null_instance_release_func(struct bt_object *obj) +{ + BT_LOGW("Releasing the null value singleton: addr=%p", obj); +} + +static +struct bt_value bt_value_null_instance = { + .base = { + .is_shared = true, + .ref_count = 1, + .release_func = bt_value_null_instance_release_func, + .spec_release_func = NULL, + .parent_is_owner_listener_func = NULL, + .parent = NULL, + }, + .type = BT_VALUE_TYPE_NULL, + .frozen = BT_TRUE, +}; + +struct bt_value *const bt_value_null = &bt_value_null_instance; + +static +void bt_value_destroy(struct bt_object *obj); + +static +void bt_value_string_destroy(struct bt_value *object) +{ + g_string_free(BT_VALUE_TO_STRING(object)->gstr, TRUE); + BT_VALUE_TO_STRING(object)->gstr = NULL; +} + +static +void bt_value_array_destroy(struct bt_value *object) +{ + /* + * Pointer array's registered value destructor will take care + * of putting each contained object. + */ + g_ptr_array_free(BT_VALUE_TO_ARRAY(object)->garray, TRUE); + BT_VALUE_TO_ARRAY(object)->garray = NULL; +} + +static +void bt_value_map_destroy(struct bt_value *object) +{ + /* + * Hash table's registered value destructor will take care of + * putting each contained object. Keys are GQuarks and cannot + * be destroyed anyway. + */ + g_hash_table_destroy(BT_VALUE_TO_MAP(object)->ght); + BT_VALUE_TO_MAP(object)->ght = NULL; +} + +static +void (* const destroy_funcs[])(struct bt_value *) = { + [BT_VALUE_TYPE_NULL] = NULL, + [BT_VALUE_TYPE_BOOL] = NULL, + [BT_VALUE_TYPE_UNSIGNED_INTEGER] = NULL, + [BT_VALUE_TYPE_SIGNED_INTEGER] = NULL, + [BT_VALUE_TYPE_REAL] = NULL, + [BT_VALUE_TYPE_STRING] = bt_value_string_destroy, + [BT_VALUE_TYPE_ARRAY] = bt_value_array_destroy, + [BT_VALUE_TYPE_MAP] = bt_value_map_destroy, +}; + +static +struct bt_value *bt_value_null_copy(const struct bt_value *null_obj) +{ + return (void *) bt_value_null; +} + +static +struct bt_value *bt_value_bool_copy(const struct bt_value *bool_obj) +{ + return bt_value_bool_create_init( + BT_VALUE_TO_BOOL(bool_obj)->value); +} + +static inline +struct bt_value *bt_value_integer_create_init(enum bt_value_type type, + uint64_t uval); + +static +struct bt_value *bt_value_integer_copy( + const struct bt_value *integer_obj) +{ + return bt_value_integer_create_init(integer_obj->type, + BT_VALUE_TO_INTEGER(integer_obj)->value.u); +} + +static +struct bt_value *bt_value_real_copy(const struct bt_value *real_obj) +{ + return bt_value_real_create_init( + BT_VALUE_TO_REAL(real_obj)->value); +} + +static +struct bt_value *bt_value_string_copy(const struct bt_value *string_obj) +{ + return bt_value_string_create_init( + BT_VALUE_TO_STRING(string_obj)->gstr->str); +} + +static +struct bt_value *bt_value_array_copy(const struct bt_value *array_obj) +{ + int i; + int ret; + struct bt_value *copy_obj; + struct bt_value_array *typed_array_obj; + + BT_LOGD("Copying array value: addr=%p", array_obj); + typed_array_obj = BT_VALUE_TO_ARRAY(array_obj); + copy_obj = bt_value_array_create(); + if (!copy_obj) { + BT_LOGE_STR("Cannot create empty array value."); + goto end; + } + + for (i = 0; i < typed_array_obj->garray->len; ++i) { + struct bt_value *element_obj_copy = NULL; + const struct bt_value *element_obj = + bt_value_array_borrow_element_by_index_const( + array_obj, i); + + BT_ASSERT(element_obj); + BT_LOGD("Copying array value's element: element-addr=%p, " + "index=%d", element_obj, i); + ret = bt_value_copy(element_obj, &element_obj_copy); + if (ret) { + BT_LOGE("Cannot copy array value's element: " + "array-addr=%p, index=%d", + array_obj, i); + BT_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + + BT_ASSERT(element_obj_copy); + ret = bt_value_array_append_element(copy_obj, + (void *) element_obj_copy); + BT_OBJECT_PUT_REF_AND_RESET(element_obj_copy); + if (ret) { + BT_LOGE("Cannot append to array value: addr=%p", + array_obj); + BT_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + } + + BT_LOGD("Copied array value: original-addr=%p, copy-addr=%p", + array_obj, copy_obj); + +end: + return copy_obj; +} + +static +struct bt_value *bt_value_map_copy(const struct bt_value *map_obj) +{ + int ret; + GHashTableIter iter; + gpointer key, element_obj; + struct bt_value *copy_obj; + struct bt_value *element_obj_copy = NULL; + struct bt_value_map *typed_map_obj; + + BT_LOGD("Copying map value: addr=%p", map_obj); + typed_map_obj = BT_VALUE_TO_MAP(map_obj); + copy_obj = bt_value_map_create(); + if (!copy_obj) { + goto end; + } + + g_hash_table_iter_init(&iter, typed_map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + BT_ASSERT(key_str); + BT_LOGD("Copying map value's element: element-addr=%p, " + "key=\"%s\"", element_obj, key_str); + ret = bt_value_copy(element_obj, &element_obj_copy); + if (ret) { + BT_LOGE("Cannot copy map value's element: " + "map-addr=%p, key=\"%s\"", + map_obj, key_str); + BT_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + + BT_ASSERT(element_obj_copy); + ret = bt_value_map_insert_entry(copy_obj, key_str, + (void *) element_obj_copy); + BT_OBJECT_PUT_REF_AND_RESET(element_obj_copy); + if (ret) { + BT_LOGE("Cannot insert into map value: addr=%p, key=\"%s\"", + map_obj, key_str); + BT_OBJECT_PUT_REF_AND_RESET(copy_obj); + goto end; + } + } + + BT_LOGD("Copied map value: addr=%p", map_obj); + +end: + return copy_obj; +} + +static +struct bt_value *(* const copy_funcs[])(const struct bt_value *) = { + [BT_VALUE_TYPE_NULL] = bt_value_null_copy, + [BT_VALUE_TYPE_BOOL] = bt_value_bool_copy, + [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_integer_copy, + [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_integer_copy, + [BT_VALUE_TYPE_REAL] = bt_value_real_copy, + [BT_VALUE_TYPE_STRING] = bt_value_string_copy, + [BT_VALUE_TYPE_ARRAY] = bt_value_array_copy, + [BT_VALUE_TYPE_MAP] = bt_value_map_copy, +}; + +static +bt_bool bt_value_null_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + /* + * Always BT_TRUE since bt_value_compare() already checks if both + * object_a and object_b have the same type, and in the case of + * null value objects, they're always the same if it is so. + */ + return BT_TRUE; +} + +static +bt_bool bt_value_bool_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + if (BT_VALUE_TO_BOOL(object_a)->value != + BT_VALUE_TO_BOOL(object_b)->value) { + BT_LOGV("Boolean value objects are different: " + "bool-a-val=%d, bool-b-val=%d", + BT_VALUE_TO_BOOL(object_a)->value, + BT_VALUE_TO_BOOL(object_b)->value); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_value_integer_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + if (BT_VALUE_TO_INTEGER(object_a)->value.u != + BT_VALUE_TO_INTEGER(object_b)->value.u) { + if (object_a->type == BT_VALUE_TYPE_UNSIGNED_INTEGER) { + BT_LOGV("Unsigned integer value objects are different: " + "int-a-val=%" PRIu64 ", int-b-val=%" PRIu64, + BT_VALUE_TO_INTEGER(object_a)->value.u, + BT_VALUE_TO_INTEGER(object_b)->value.u); + } else { + BT_LOGV("Signed integer value objects are different: " + "int-a-val=%" PRId64 ", int-b-val=%" PRId64, + BT_VALUE_TO_INTEGER(object_a)->value.i, + BT_VALUE_TO_INTEGER(object_b)->value.i); + } + + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_value_real_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + if (BT_VALUE_TO_REAL(object_a)->value != + BT_VALUE_TO_REAL(object_b)->value) { + BT_LOGV("Real number value objects are different: " + "real-a-val=%f, real-b-val=%f", + BT_VALUE_TO_REAL(object_a)->value, + BT_VALUE_TO_REAL(object_b)->value); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_value_string_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + if (strcmp(BT_VALUE_TO_STRING(object_a)->gstr->str, + BT_VALUE_TO_STRING(object_b)->gstr->str) != 0) { + BT_LOGV("String value objects are different: " + "string-a-val=\"%s\", string-b-val=\"%s\"", + BT_VALUE_TO_STRING(object_a)->gstr->str, + BT_VALUE_TO_STRING(object_b)->gstr->str); + return BT_FALSE; + } + + return BT_TRUE; +} + +static +bt_bool bt_value_array_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + int i; + bt_bool ret = BT_TRUE; + const struct bt_value_array *array_obj_a = + BT_VALUE_TO_ARRAY(object_a); + + if (bt_value_array_get_size(object_a) != + bt_value_array_get_size(object_b)) { + BT_LOGV("Array values are different: size mismatch " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-size=%" PRId64 ", value-b-size=%" PRId64, + object_a, object_b, + bt_value_array_get_size(object_a), + bt_value_array_get_size(object_b)); + ret = BT_FALSE; + goto end; + } + + for (i = 0; i < array_obj_a->garray->len; ++i) { + const struct bt_value *element_obj_a; + const struct bt_value *element_obj_b; + + element_obj_a = bt_value_array_borrow_element_by_index_const( + object_a, i); + element_obj_b = bt_value_array_borrow_element_by_index_const( + object_b, i); + + if (!bt_value_compare(element_obj_a, element_obj_b)) { + BT_LOGV("Array values's elements are different: " + "value-a-addr=%p, value-b-addr=%p, index=%d", + element_obj_a, element_obj_b, i); + ret = BT_FALSE; + goto end; + } + } + +end: + return ret; +} + +static +bt_bool bt_value_map_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + bt_bool ret = BT_TRUE; + GHashTableIter iter; + gpointer key, element_obj_a; + const struct bt_value_map *map_obj_a = BT_VALUE_TO_MAP(object_a); + + if (bt_value_map_get_size(object_a) != + bt_value_map_get_size(object_b)) { + BT_LOGV("Map values are different: size mismatch " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-size=%" PRId64 ", value-b-size=%" PRId64, + object_a, object_b, + bt_value_map_get_size(object_a), + bt_value_map_get_size(object_b)); + ret = BT_FALSE; + goto end; + } + + g_hash_table_iter_init(&iter, map_obj_a->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj_a)) { + const struct bt_value *element_obj_b; + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + element_obj_b = bt_value_map_borrow_entry_value_const(object_b, + key_str); + + if (!bt_value_compare(element_obj_a, element_obj_b)) { + BT_LOGV("Map values's elements are different: " + "value-a-addr=%p, value-b-addr=%p, key=\"%s\"", + element_obj_a, element_obj_b, key_str); + ret = BT_FALSE; + goto end; + } + } + +end: + return ret; +} + +static +bt_bool (* const compare_funcs[])(const struct bt_value *, + const struct bt_value *) = { + [BT_VALUE_TYPE_NULL] = bt_value_null_compare, + [BT_VALUE_TYPE_BOOL] = bt_value_bool_compare, + [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_integer_compare, + [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_integer_compare, + [BT_VALUE_TYPE_REAL] = bt_value_real_compare, + [BT_VALUE_TYPE_STRING] = bt_value_string_compare, + [BT_VALUE_TYPE_ARRAY] = bt_value_array_compare, + [BT_VALUE_TYPE_MAP] = bt_value_map_compare, +}; + +static +void bt_value_null_freeze(struct bt_value *object) +{ +} + +static +void bt_value_generic_freeze(struct bt_value *object) +{ + object->frozen = BT_TRUE; +} + +static +void bt_value_array_freeze(struct bt_value *object) +{ + int i; + struct bt_value_array *typed_array_obj = + BT_VALUE_TO_ARRAY(object); + + for (i = 0; i < typed_array_obj->garray->len; ++i) { + bt_value_freeze(g_ptr_array_index(typed_array_obj->garray, i)); + } + + bt_value_generic_freeze(object); +} + +static +void bt_value_map_freeze(struct bt_value *object) +{ + GHashTableIter iter; + gpointer key, element_obj; + const struct bt_value_map *map_obj = BT_VALUE_TO_MAP(object); + + g_hash_table_iter_init(&iter, map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + bt_value_freeze(element_obj); + } + + bt_value_generic_freeze(object); +} + +static +void (* const freeze_funcs[])(struct bt_value *) = { + [BT_VALUE_TYPE_NULL] = bt_value_null_freeze, + [BT_VALUE_TYPE_BOOL] = bt_value_generic_freeze, + [BT_VALUE_TYPE_UNSIGNED_INTEGER] = bt_value_generic_freeze, + [BT_VALUE_TYPE_SIGNED_INTEGER] = bt_value_generic_freeze, + [BT_VALUE_TYPE_REAL] = bt_value_generic_freeze, + [BT_VALUE_TYPE_STRING] = bt_value_generic_freeze, + [BT_VALUE_TYPE_ARRAY] = bt_value_array_freeze, + [BT_VALUE_TYPE_MAP] = bt_value_map_freeze, +}; + +static +void bt_value_destroy(struct bt_object *obj) +{ + struct bt_value *value; + + value = container_of(obj, struct bt_value, base); + BT_LOGD("Destroying value: addr=%p", value); + + if (bt_value_is_null(value)) { + BT_LOGD_STR("Not destroying the null value singleton."); + return; + } + + if (destroy_funcs[value->type]) { + destroy_funcs[value->type](value); + } + + g_free(value); +} + +BT_HIDDEN +enum bt_value_status _bt_value_freeze(const struct bt_value *c_object) +{ + const struct bt_value *object = (void *) c_object; + enum bt_value_status ret = BT_VALUE_STATUS_OK; + + BT_ASSERT(object); + + if (object->frozen) { + goto end; + } + + BT_LOGD("Freezing value: addr=%p", object); + freeze_funcs[object->type]((void *) object); + +end: + return ret; +} + +enum bt_value_type bt_value_get_type(const struct bt_value *object) +{ + BT_ASSERT_PRE_NON_NULL(object, "Value object"); + return object->type; +} + +static +struct bt_value bt_value_create_base(enum bt_value_type type) +{ + struct bt_value value; + + value.type = type; + value.frozen = BT_FALSE; + bt_object_init_shared(&value.base, bt_value_destroy); + return value; +} + +struct bt_value *bt_value_bool_create_init(bt_bool val) +{ + struct bt_value_bool *bool_obj; + + BT_LOGD("Creating boolean value object: val=%d", val); + bool_obj = g_new0(struct bt_value_bool, 1); + if (!bool_obj) { + BT_LOGE_STR("Failed to allocate one boolean value object."); + goto end; + } + + bool_obj->base = bt_value_create_base(BT_VALUE_TYPE_BOOL); + bool_obj->value = val; + BT_LOGD("Created boolean value object: addr=%p", bool_obj); + +end: + return (void *) bool_obj; +} + +struct bt_value *bt_value_bool_create(void) +{ + return bt_value_bool_create_init(BT_FALSE); +} + +static inline +struct bt_value *bt_value_integer_create_init(enum bt_value_type type, + uint64_t uval) +{ + struct bt_value_integer *integer_obj; + + BT_ASSERT(type == BT_VALUE_TYPE_UNSIGNED_INTEGER || + type == BT_VALUE_TYPE_SIGNED_INTEGER); + + if (type == BT_VALUE_TYPE_UNSIGNED_INTEGER) { + BT_LOGD("Creating unsigned integer value object: val=%" PRIu64, + uval); + } else { + BT_LOGD("Creating signed integer value object: val=%" PRId64, + (int64_t) uval); + } + + integer_obj = g_new0(struct bt_value_integer, 1); + if (!integer_obj) { + BT_LOGE_STR("Failed to allocate one integer value object."); + goto end; + } + + integer_obj->base = bt_value_create_base(type); + integer_obj->value.u = uval; + BT_LOGD("Created %ssigned integer value object: addr=%p", + type == BT_VALUE_TYPE_UNSIGNED_INTEGER ? "un" : "", + integer_obj); + +end: + return (void *) integer_obj; +} + +struct bt_value *bt_value_unsigned_integer_create_init(uint64_t val) +{ + return bt_value_integer_create_init(BT_VALUE_TYPE_UNSIGNED_INTEGER, + val); +} + +struct bt_value *bt_value_unsigned_integer_create(void) +{ + return bt_value_unsigned_integer_create_init(0); +} + +struct bt_value *bt_value_signed_integer_create_init(int64_t val) +{ + return bt_value_integer_create_init(BT_VALUE_TYPE_SIGNED_INTEGER, + (uint64_t) val); +} + +struct bt_value *bt_value_signed_integer_create(void) +{ + return bt_value_signed_integer_create_init(0); +} + +struct bt_value *bt_value_real_create_init(double val) +{ + struct bt_value_real *real_obj; + + BT_LOGD("Creating real number value object: val=%f", val); + real_obj = g_new0(struct bt_value_real, 1); + if (!real_obj) { + BT_LOGE_STR("Failed to allocate one real number value object."); + goto end; + } + + real_obj->base = bt_value_create_base(BT_VALUE_TYPE_REAL); + real_obj->value = val; + BT_LOGD("Created real number value object: addr=%p", + real_obj); + +end: + return (void *) real_obj; +} + +struct bt_value *bt_value_real_create(void) +{ + return bt_value_real_create_init(0.); +} + +struct bt_value *bt_value_string_create_init(const char *val) +{ + struct bt_value_string *string_obj = NULL; + + if (!val) { + BT_LOGW_STR("Invalid parameter: value is NULL."); + goto end; + } + + BT_LOGD("Creating string value object: val-len=%zu", strlen(val)); + string_obj = g_new0(struct bt_value_string, 1); + if (!string_obj) { + BT_LOGE_STR("Failed to allocate one string object."); + goto end; + } + + string_obj->base = bt_value_create_base(BT_VALUE_TYPE_STRING); + string_obj->gstr = g_string_new(val); + if (!string_obj->gstr) { + BT_LOGE_STR("Failed to allocate a GString."); + g_free(string_obj); + string_obj = NULL; + goto end; + } + + BT_LOGD("Created string value object: addr=%p", + string_obj); + +end: + return (void *) string_obj; +} + +struct bt_value *bt_value_string_create(void) +{ + return bt_value_string_create_init(""); +} + +struct bt_value *bt_value_array_create(void) +{ + struct bt_value_array *array_obj; + + BT_LOGD_STR("Creating empty array value object."); + array_obj = g_new0(struct bt_value_array, 1); + if (!array_obj) { + BT_LOGE_STR("Failed to allocate one array object."); + goto end; + } + + array_obj->base = bt_value_create_base(BT_VALUE_TYPE_ARRAY); + array_obj->garray = bt_g_ptr_array_new_full(0, + (GDestroyNotify) bt_object_put_ref); + if (!array_obj->garray) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + g_free(array_obj); + array_obj = NULL; + goto end; + } + + BT_LOGD("Created array value object: addr=%p", + array_obj); + +end: + return (void *) array_obj; +} + +struct bt_value *bt_value_map_create(void) +{ + struct bt_value_map *map_obj; + + BT_LOGD_STR("Creating empty map value object."); + map_obj = g_new0(struct bt_value_map, 1); + if (!map_obj) { + BT_LOGE_STR("Failed to allocate one map object."); + goto end; + } + + map_obj->base = bt_value_create_base(BT_VALUE_TYPE_MAP); + map_obj->ght = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) bt_object_put_ref); + if (!map_obj->ght) { + BT_LOGE_STR("Failed to allocate a GHashTable."); + g_free(map_obj); + map_obj = NULL; + goto end; + } + + BT_LOGD("Created map value object: addr=%p", + map_obj); + +end: + return (void *) map_obj; +} + +bt_bool bt_value_bool_get(const struct bt_value *bool_obj) +{ + BT_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_VALUE_TYPE_BOOL); + return BT_VALUE_TO_BOOL(bool_obj)->value; +} + +void bt_value_bool_set(struct bt_value *bool_obj, bt_bool val) +{ + BT_ASSERT_PRE_NON_NULL(bool_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(bool_obj, BT_VALUE_TYPE_BOOL); + BT_ASSERT_PRE_VALUE_HOT(bool_obj, "Value object"); + BT_VALUE_TO_BOOL(bool_obj)->value = val; + BT_LOGV("Set boolean value's raw value: value-addr=%p, value=%d", + bool_obj, val); +} + +uint64_t bt_value_unsigned_integer_get(const struct bt_value *integer_obj) +{ + BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, + BT_VALUE_TYPE_UNSIGNED_INTEGER); + return BT_VALUE_TO_INTEGER(integer_obj)->value.u; +} + +int64_t bt_value_signed_integer_get(const struct bt_value *integer_obj) +{ + BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, + BT_VALUE_TYPE_SIGNED_INTEGER); + return BT_VALUE_TO_INTEGER(integer_obj)->value.i; +} + +static inline +void bt_value_integer_set(struct bt_value *integer_obj, + enum bt_value_type expected_type, uint64_t uval) +{ + BT_ASSERT_PRE_NON_NULL(integer_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(integer_obj, expected_type); + BT_ASSERT_PRE_VALUE_HOT(integer_obj, "Value object"); + BT_VALUE_TO_INTEGER(integer_obj)->value.u = uval; +} + +void bt_value_unsigned_integer_set(struct bt_value *integer_obj, + uint64_t val) +{ + bt_value_integer_set(integer_obj, BT_VALUE_TYPE_UNSIGNED_INTEGER, val); + BT_LOGV("Set unsigned integer value's raw value: " + "value-addr=%p, value=%" PRIu64, integer_obj, val); +} + +void bt_value_signed_integer_set(struct bt_value *integer_obj, + int64_t val) +{ + bt_value_integer_set(integer_obj, BT_VALUE_TYPE_SIGNED_INTEGER, + (uint64_t) val); + BT_LOGV("Set signed integer value's raw value: " + "value-addr=%p, value=%" PRId64, integer_obj, val); +} + +double bt_value_real_get(const struct bt_value *real_obj) +{ + BT_ASSERT_PRE_NON_NULL(real_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_VALUE_TYPE_REAL); + return BT_VALUE_TO_REAL(real_obj)->value; +} + +void bt_value_real_set(struct bt_value *real_obj, double val) +{ + BT_ASSERT_PRE_NON_NULL(real_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(real_obj, BT_VALUE_TYPE_REAL); + BT_ASSERT_PRE_VALUE_HOT(real_obj, "Value object"); + BT_VALUE_TO_REAL(real_obj)->value = val; + BT_LOGV("Set real number value's raw value: value-addr=%p, value=%f", + real_obj, val); +} + +const char *bt_value_string_get(const struct bt_value *string_obj) +{ + BT_ASSERT_PRE_NON_NULL(string_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_VALUE_TYPE_STRING); + return BT_VALUE_TO_STRING(string_obj)->gstr->str; +} + +enum bt_value_status bt_value_string_set( + struct bt_value *string_obj, const char *val) +{ + BT_ASSERT_PRE_NON_NULL(string_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(string_obj, BT_VALUE_TYPE_STRING); + BT_ASSERT_PRE_VALUE_HOT(string_obj, "Value object"); + g_string_assign(BT_VALUE_TO_STRING(string_obj)->gstr, val); + BT_LOGV("Set string value's raw value: value-addr=%p, raw-value-addr=%p", + string_obj, val); + return BT_VALUE_STATUS_OK; +} + +uint64_t bt_value_array_get_size(const struct bt_value *array_obj) +{ + BT_ASSERT_PRE_NON_NULL(array_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); + return (uint64_t) BT_VALUE_TO_ARRAY(array_obj)->garray->len; +} + +struct bt_value *bt_value_array_borrow_element_by_index( + struct bt_value *array_obj, uint64_t index) +{ + struct bt_value_array *typed_array_obj = + BT_VALUE_TO_ARRAY(array_obj); + + BT_ASSERT_PRE_NON_NULL(array_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); + BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, + typed_array_obj->garray->len); + return g_ptr_array_index(typed_array_obj->garray, index); +} + +const struct bt_value *bt_value_array_borrow_element_by_index_const( + const struct bt_value *array_obj, + uint64_t index) +{ + return bt_value_array_borrow_element_by_index( + (void *) array_obj, index); +} + +enum bt_value_status bt_value_array_append_element( + struct bt_value *array_obj, + struct bt_value *element_obj) +{ + struct bt_value_array *typed_array_obj = + BT_VALUE_TO_ARRAY(array_obj); + + BT_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); + BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); + BT_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); + g_ptr_array_add(typed_array_obj->garray, element_obj); + bt_object_get_ref(element_obj); + BT_LOGV("Appended element to array value: array-value-addr=%p, " + "element-value-addr=%p, new-size=%u", + array_obj, element_obj, typed_array_obj->garray->len); + return BT_VALUE_STATUS_OK; +} + +enum bt_value_status bt_value_array_append_bool_element( + struct bt_value *array_obj, bt_bool val) +{ + enum bt_value_status ret; + struct bt_value *bool_obj = NULL; + + bool_obj = bt_value_bool_create_init(val); + ret = bt_value_array_append_element(array_obj, + (void *) bool_obj); + bt_object_put_ref(bool_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_unsigned_integer_element( + struct bt_value *array_obj, uint64_t val) +{ + enum bt_value_status ret; + struct bt_value *integer_obj = NULL; + + integer_obj = bt_value_unsigned_integer_create_init(val); + ret = bt_value_array_append_element(array_obj, + (void *) integer_obj); + bt_object_put_ref(integer_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_signed_integer_element( + struct bt_value *array_obj, int64_t val) +{ + enum bt_value_status ret; + struct bt_value *integer_obj = NULL; + + integer_obj = bt_value_signed_integer_create_init(val); + ret = bt_value_array_append_element(array_obj, + (void *) integer_obj); + bt_object_put_ref(integer_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_real_element( + struct bt_value *array_obj, double val) +{ + enum bt_value_status ret; + struct bt_value *real_obj = NULL; + + real_obj = bt_value_real_create_init(val); + ret = bt_value_array_append_element(array_obj, + (void *) real_obj); + bt_object_put_ref(real_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_string_element( + struct bt_value *array_obj, const char *val) +{ + enum bt_value_status ret; + struct bt_value *string_obj = NULL; + + string_obj = bt_value_string_create_init(val); + ret = bt_value_array_append_element(array_obj, + (void *) string_obj); + bt_object_put_ref(string_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_empty_array_element( + struct bt_value *array_obj) +{ + enum bt_value_status ret; + struct bt_value *empty_array_obj = NULL; + + empty_array_obj = bt_value_array_create(); + ret = bt_value_array_append_element(array_obj, + (void *) empty_array_obj); + bt_object_put_ref(empty_array_obj); + return ret; +} + +enum bt_value_status bt_value_array_append_empty_map_element( + struct bt_value *array_obj) +{ + enum bt_value_status ret; + struct bt_value *map_obj = NULL; + + map_obj = bt_value_map_create(); + ret = bt_value_array_append_element(array_obj, + (void *) map_obj); + bt_object_put_ref(map_obj); + return ret; +} + +enum bt_value_status bt_value_array_set_element_by_index( + struct bt_value *array_obj, uint64_t index, + struct bt_value *element_obj) +{ + struct bt_value_array *typed_array_obj = + BT_VALUE_TO_ARRAY(array_obj); + + BT_ASSERT_PRE_NON_NULL(array_obj, "Array value object"); + BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(array_obj, BT_VALUE_TYPE_ARRAY); + BT_ASSERT_PRE_VALUE_HOT(array_obj, "Array value object"); + BT_ASSERT_PRE_VALUE_INDEX_IN_BOUNDS(index, + typed_array_obj->garray->len); + bt_object_put_ref(g_ptr_array_index(typed_array_obj->garray, index)); + g_ptr_array_index(typed_array_obj->garray, index) = element_obj; + bt_object_get_ref(element_obj); + BT_LOGV("Set array value's element: array-value-addr=%p, " + "index=%" PRIu64 ", element-value-addr=%p", + array_obj, index, element_obj); + return BT_VALUE_STATUS_OK; +} + +uint64_t bt_value_map_get_size(const struct bt_value *map_obj) +{ + BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); + return (uint64_t) g_hash_table_size(BT_VALUE_TO_MAP(map_obj)->ght); +} + +struct bt_value *bt_value_map_borrow_entry_value(struct bt_value *map_obj, + const char *key) +{ + BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_ASSERT_PRE_NON_NULL(key, "Key"); + BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); + return g_hash_table_lookup(BT_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key))); +} + +const struct bt_value *bt_value_map_borrow_entry_value_const( + const struct bt_value *map_obj, const char *key) +{ + return bt_value_map_borrow_entry_value((void *) map_obj, key); +} + +bt_bool bt_value_map_has_entry(const struct bt_value *map_obj, const char *key) +{ + BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_ASSERT_PRE_NON_NULL(key, "Key"); + BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); + return bt_g_hash_table_contains(BT_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key))); +} + +enum bt_value_status bt_value_map_insert_entry( + struct bt_value *map_obj, + const char *key, struct bt_value *element_obj) +{ + BT_ASSERT_PRE_NON_NULL(map_obj, "Map value object"); + BT_ASSERT_PRE_NON_NULL(key, "Key"); + BT_ASSERT_PRE_NON_NULL(element_obj, "Element value object"); + BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); + BT_ASSERT_PRE_VALUE_HOT(map_obj, "Map value object"); + g_hash_table_insert(BT_VALUE_TO_MAP(map_obj)->ght, + GUINT_TO_POINTER(g_quark_from_string(key)), element_obj); + bt_object_get_ref(element_obj); + BT_LOGV("Inserted value into map value: map-value-addr=%p, " + "key=\"%s\", element-value-addr=%p", + map_obj, key, element_obj); + return BT_VALUE_STATUS_OK; +} + +enum bt_value_status bt_value_map_insert_bool_entry( + struct bt_value *map_obj, const char *key, bt_bool val) +{ + enum bt_value_status ret; + struct bt_value *bool_obj = NULL; + + bool_obj = bt_value_bool_create_init(val); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) bool_obj); + bt_object_put_ref(bool_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_unsigned_integer_entry( + struct bt_value *map_obj, const char *key, uint64_t val) +{ + enum bt_value_status ret; + struct bt_value *integer_obj = NULL; + + integer_obj = bt_value_unsigned_integer_create_init(val); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) integer_obj); + bt_object_put_ref(integer_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_signed_integer_entry( + struct bt_value *map_obj, const char *key, int64_t val) +{ + enum bt_value_status ret; + struct bt_value *integer_obj = NULL; + + integer_obj = bt_value_signed_integer_create_init(val); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) integer_obj); + bt_object_put_ref(integer_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_real_entry( + struct bt_value *map_obj, const char *key, double val) +{ + enum bt_value_status ret; + struct bt_value *real_obj = NULL; + + real_obj = bt_value_real_create_init(val); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) real_obj); + bt_object_put_ref(real_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_string_entry( + struct bt_value *map_obj, const char *key, + const char *val) +{ + enum bt_value_status ret; + struct bt_value *string_obj = NULL; + + string_obj = bt_value_string_create_init(val); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) string_obj); + bt_object_put_ref(string_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_empty_array_entry( + struct bt_value *map_obj, const char *key) +{ + enum bt_value_status ret; + struct bt_value *array_obj = NULL; + + array_obj = bt_value_array_create(); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) array_obj); + bt_object_put_ref(array_obj); + return ret; +} + +enum bt_value_status bt_value_map_insert_empty_map_entry( + struct bt_value *map_obj, const char *key) +{ + enum bt_value_status ret; + struct bt_value *empty_map_obj = NULL; + + empty_map_obj = bt_value_map_create(); + ret = bt_value_map_insert_entry(map_obj, key, + (void *) empty_map_obj); + bt_object_put_ref(empty_map_obj); + return ret; +} + +enum bt_value_status bt_value_map_foreach_entry(struct bt_value *map_obj, + bt_value_map_foreach_entry_func func, void *data) +{ + enum bt_value_status ret = BT_VALUE_STATUS_OK; + gpointer key, element_obj; + GHashTableIter iter; + struct bt_value_map *typed_map_obj = BT_VALUE_TO_MAP(map_obj); + + BT_ASSERT_PRE_NON_NULL(map_obj, "Value object"); + BT_ASSERT_PRE_NON_NULL(func, "Callback"); + BT_ASSERT_PRE_VALUE_IS_TYPE(map_obj, BT_VALUE_TYPE_MAP); + g_hash_table_iter_init(&iter, typed_map_obj->ght); + + while (g_hash_table_iter_next(&iter, &key, &element_obj)) { + const char *key_str = g_quark_to_string(GPOINTER_TO_UINT(key)); + + if (!func(key_str, element_obj, data)) { + BT_LOGV("User canceled the loop: key=\"%s\", " + "value-addr=%p, data=%p", + key_str, element_obj, data); + ret = BT_VALUE_STATUS_CANCELED; + break; + } + } + + return ret; +} + +enum bt_value_status bt_value_map_foreach_entry_const( + const struct bt_value *map_obj, + bt_value_map_foreach_entry_const_func func, void *data) +{ + return bt_value_map_foreach_entry((void *) map_obj, + (bt_value_map_foreach_entry_func) func, data); +} + +struct extend_map_element_data { + struct bt_value *extended_obj; + enum bt_value_status status; +}; + +static +bt_bool extend_map_element(const char *key, + const struct bt_value *extension_obj_elem, void *data) +{ + bt_bool ret = BT_TRUE; + struct extend_map_element_data *extend_data = data; + struct bt_value *extension_obj_elem_copy = NULL; + + /* Copy object which is to replace the current one */ + extend_data->status = bt_value_copy(extension_obj_elem, + &extension_obj_elem_copy); + if (extend_data->status) { + BT_LOGE("Cannot copy map element: addr=%p", + extension_obj_elem); + goto error; + } + + BT_ASSERT(extension_obj_elem_copy); + + /* Replace in extended object */ + extend_data->status = bt_value_map_insert_entry( + extend_data->extended_obj, key, + (void *) extension_obj_elem_copy); + if (extend_data->status) { + BT_LOGE("Cannot replace value in extended value: key=\"%s\", " + "extended-value-addr=%p, element-value-addr=%p", + key, extend_data->extended_obj, + extension_obj_elem_copy); + goto error; + } + + goto end; + +error: + BT_ASSERT(extend_data->status != BT_VALUE_STATUS_OK); + ret = BT_FALSE; + +end: + BT_OBJECT_PUT_REF_AND_RESET(extension_obj_elem_copy); + return ret; +} + +enum bt_value_status bt_value_map_extend( + const struct bt_value *base_map_obj, + const struct bt_value *extension_obj, + struct bt_value **extended_map_obj) +{ + struct extend_map_element_data extend_data = { + .extended_obj = NULL, + .status = BT_VALUE_STATUS_OK, + }; + + BT_ASSERT_PRE_NON_NULL(base_map_obj, "Base value object"); + BT_ASSERT_PRE_NON_NULL(extension_obj, "Extension value object"); + BT_ASSERT_PRE_NON_NULL(extended_map_obj, + "Extended value object (output)"); + BT_ASSERT_PRE_VALUE_IS_TYPE(base_map_obj, BT_VALUE_TYPE_MAP); + BT_ASSERT_PRE_VALUE_IS_TYPE(extension_obj, BT_VALUE_TYPE_MAP); + BT_LOGD("Extending map value: base-value-addr=%p, extension-value-addr=%p", + base_map_obj, extension_obj); + *extended_map_obj = NULL; + + /* Create copy of base map object to start with */ + extend_data.status = bt_value_copy(base_map_obj, extended_map_obj); + if (extend_data.status) { + BT_LOGE("Cannot copy base value: base-value-addr=%p", + base_map_obj); + goto error; + } + + BT_ASSERT(extended_map_obj); + + /* + * For each key in the extension map object, replace this key + * in the copied map object. + */ + extend_data.extended_obj = *extended_map_obj; + + if (bt_value_map_foreach_entry_const(extension_obj, extend_map_element, + &extend_data)) { + BT_LOGE("Cannot iterate on the extension object's elements: " + "extension-value-addr=%p", extension_obj); + goto error; + } + + if (extend_data.status) { + BT_LOGE("Failed to successfully iterate on the extension object's elements: " + "extension-value-addr=%p", extension_obj); + goto error; + } + + BT_LOGD("Extended map value: extended-value-addr=%p", + *extended_map_obj); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(*extended_map_obj); + *extended_map_obj = NULL; + +end: + return extend_data.status; +} + +enum bt_value_status bt_value_copy(const struct bt_value *object, + struct bt_value **copy_obj) +{ + enum bt_value_status status = BT_VALUE_STATUS_OK; + + BT_ASSERT_PRE_NON_NULL(object, "Value object"); + BT_ASSERT_PRE_NON_NULL(copy_obj, "Value object copy (output)"); + BT_LOGD("Copying value object: addr=%p", object); + *copy_obj = copy_funcs[object->type](object); + if (*copy_obj) { + BT_LOGD("Copied value object: copy-value-addr=%p", + copy_obj); + } else { + status = BT_VALUE_STATUS_NOMEM; + *copy_obj = NULL; + BT_LOGE_STR("Failed to copy value object."); + } + + return status; +} + +bt_bool bt_value_compare(const struct bt_value *object_a, + const struct bt_value *object_b) +{ + bt_bool ret = BT_FALSE; + + BT_ASSERT_PRE_NON_NULL(object_a, "Value object A"); + BT_ASSERT_PRE_NON_NULL(object_b, "Value object B"); + + if (object_a->type != object_b->type) { + BT_LOGV("Values are different: type mismatch: " + "value-a-addr=%p, value-b-addr=%p, " + "value-a-type=%s, value-b-type=%s", + object_a, object_b, + bt_common_value_type_string(object_a->type), + bt_common_value_type_string(object_b->type)); + goto end; + } + + ret = compare_funcs[object_a->type](object_a, object_b); + +end: + return ret; +} + +void bt_value_get_ref(const struct bt_value *value) +{ + bt_object_get_ref(value); +} + +void bt_value_put_ref(const struct bt_value *value) +{ + bt_object_put_ref(value); +} diff --git a/src/lib/value.h b/src/lib/value.h new file mode 100644 index 00000000..aa741881 --- /dev/null +++ b/src/lib/value.h @@ -0,0 +1,80 @@ +#ifndef BABELTRACE_VALUES_INTERNAL_H +#define BABELTRACE_VALUES_INTERNAL_H + +/* + * Copyright (c) 2015-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "lib/object.h" +#include +#include +#include +#include + +struct bt_value { + struct bt_object base; + enum bt_value_type type; + bt_bool frozen; +}; + +struct bt_value_bool { + struct bt_value base; + bt_bool value; +}; + +struct bt_value_integer { + struct bt_value base; + union { + uint64_t i; + int64_t u; + } value; +}; + +struct bt_value_real { + struct bt_value base; + double value; +}; + +struct bt_value_string { + struct bt_value base; + GString *gstr; +}; + +struct bt_value_array { + struct bt_value base; + GPtrArray *garray; +}; + +struct bt_value_map { + struct bt_value base; + GHashTable *ght; +}; + +BT_HIDDEN +enum bt_value_status _bt_value_freeze(const struct bt_value *object); + +#ifdef BT_DEV_MODE +# define bt_value_freeze _bt_value_freeze +#else +# define bt_value_freeze(_value) +#endif /* BT_DEV_MODE */ + +#endif /* BABELTRACE_VALUES_INTERNAL_H */ diff --git a/src/logging/LICENSE b/src/logging/LICENSE new file mode 100644 index 00000000..5569c1d5 --- /dev/null +++ b/src/logging/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 wonder-mice + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/logging/Makefile.am b/src/logging/Makefile.am new file mode 100644 index 00000000..fd6c6b64 --- /dev/null +++ b/src/logging/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libbabeltrace2-logging.la + +libbabeltrace2_logging_la_SOURCES = log.c log.h diff --git a/src/logging/log.c b/src/logging/log.c new file mode 100644 index 00000000..d8c80402 --- /dev/null +++ b/src/logging/log.c @@ -0,0 +1,1419 @@ +/* + * This is zf_log.c, modified with Babeltrace prefixes. + * See . + * See LICENSE. + */ + +#include "common/babeltrace.h" +#include "common/common.h" +#include +#include "common/assert.h" + +#ifdef __CYGWIN__ +extern unsigned long pthread_getsequence_np(pthread_t *); +#endif + +/* When defined, Android log (android/log.h) will be used by default instead of + * stderr (ignored on non-Android platforms). Date, time, pid and tid (context) + * will be provided by Android log. Android log features will be used to output + * log level and tag. + */ +#ifdef BT_LOG_USE_ANDROID_LOG + #undef BT_LOG_USE_ANDROID_LOG + #if defined(__ANDROID__) + #define BT_LOG_USE_ANDROID_LOG 1 + #else + #define BT_LOG_USE_ANDROID_LOG 0 + #endif +#else + #define BT_LOG_USE_ANDROID_LOG 0 +#endif +/* When defined, NSLog (uses Apple System Log) will be used instead of stderr + * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be + * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on + * non-public CFLog() function. Both use Apple System Log internally, but it's + * easier to call CFLog() from C than NSLog(). Current implementation doesn't + * support "%@" format specifier. + */ +#ifdef BT_LOG_USE_NSLOG + #undef BT_LOG_USE_NSLOG + #if defined(__APPLE__) && defined(__MACH__) + #define BT_LOG_USE_NSLOG 1 + #else + #define BT_LOG_USE_NSLOG 0 + #endif +#else + #define BT_LOG_USE_NSLOG 0 +#endif +/* When defined, OutputDebugString() will be used instead of stderr (ignored on + * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with + * UTF-8 data. + */ +#ifdef BT_LOG_USE_DEBUGSTRING + #undef BT_LOG_USE_DEBUGSTRING + #if defined(_WIN32) || defined(_WIN64) + #define BT_LOG_USE_DEBUGSTRING 1 + #else + #define BT_LOG_USE_DEBUGSTRING 0 + #endif +#else + #define BT_LOG_USE_DEBUGSTRING 0 +#endif +/* When defined, bt_log library will not contain definition of tag prefix + * variable. In that case it must be defined elsewhere using + * BT_LOG_DEFINE_TAG_PREFIX macro, for example: + * + * BT_LOG_DEFINE_TAG_PREFIX = "ProcessName"; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef BT_LOG_EXTERN_TAG_PREFIX + #undef BT_LOG_EXTERN_TAG_PREFIX + #define BT_LOG_EXTERN_TAG_PREFIX 1 +#else + #define BT_LOG_EXTERN_TAG_PREFIX 0 +#endif +/* When defined, bt_log library will not contain definition of global format + * variable. In that case it must be defined elsewhere using + * BT_LOG_DEFINE_GLOBAL_FORMAT macro, for example: + * + * BT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef BT_LOG_EXTERN_GLOBAL_FORMAT + #undef BT_LOG_EXTERN_GLOBAL_FORMAT + #define BT_LOG_EXTERN_GLOBAL_FORMAT 1 +#else + #define BT_LOG_EXTERN_GLOBAL_FORMAT 0 +#endif +/* When defined, bt_log library will not contain definition of global output + * variable. In that case it must be defined elsewhere using + * BT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example: + * + * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback}; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT + #undef BT_LOG_EXTERN_GLOBAL_OUTPUT + #define BT_LOG_EXTERN_GLOBAL_OUTPUT 1 +#else + #define BT_LOG_EXTERN_GLOBAL_OUTPUT 0 +#endif +/* When defined, bt_log library will not contain definition of global output + * level variable. In that case it must be defined elsewhere using + * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example: + * + * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_WARN; + * + * This allows to specify custom value for static initialization and avoid + * overhead of setting this value in runtime. + */ +#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + #undef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1 +#else + #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0 +#endif +/* When defined, implementation will prefer smaller code size over speed. + * Very rough estimate is that code will be up to 2x smaller and up to 2x + * slower. Disabled by default. + */ +#ifdef BT_LOG_OPTIMIZE_SIZE + #undef BT_LOG_OPTIMIZE_SIZE + #define BT_LOG_OPTIMIZE_SIZE 1 +#else + #define BT_LOG_OPTIMIZE_SIZE 0 +#endif +/* Size of the log line buffer. The buffer is a globally allocated per thread. + */ +#ifndef BT_LOG_BUF_SZ + #define BT_LOG_BUF_SZ (4 * 4096) +#endif +/* Default number of bytes in one line of memory output. For large values + * BT_LOG_BUF_SZ also must be increased. + */ +#ifndef BT_LOG_MEM_WIDTH + #define BT_LOG_MEM_WIDTH 32 +#endif +/* String to put in the end of each log line (can be empty). Its value used by + * stderr output callback. Its size used as a default value for BT_LOG_EOL_SZ. + */ +#ifndef BT_LOG_EOL + #define BT_LOG_EOL "\n" +#endif +/* Default delimiter that separates parts of log message. Can NOT contain '%' + * or '\0'. + * + * Log message format specifications can override (or ignore) this value. For + * more details see BT_LOG_MESSAGE_CTX_FORMAT, BT_LOG_MESSAGE_SRC_FORMAT and + * BT_LOG_MESSAGE_TAG_FORMAT. + */ +#ifndef BT_LOG_DEF_DELIMITER + #define BT_LOG_DEF_DELIMITER " " +#endif +/* Specifies log message context format. Log message context includes date, + * time, process id, thread id and message's log level. Custom information can + * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, + * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * Must be defined as a tuple, for example: + * + * #define BT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, S(" > ")) + * + * In that case, resulting log message will be: + * + * 2016.12.22 > TAG function@filename.c:line Message text + * + * Note, that tag, source location and message text are not impacted by + * this setting. See BT_LOG_MESSAGE_TAG_FORMAT and BT_LOG_MESSAGE_SRC_FORMAT. + * + * If message context must be visually separated from the rest of the message, + * it must be reflected in context format (notice trailing S(" > ") in the + * example above). + * + * S(str) adds constant string str. String can NOT contain '%' or '\0'. + * + * F_INIT(statements) adds initialization statement(s) that will be evaluated + * once for each log message. All statements are evaluated in specified order. + * Several F_INIT() fields can be used in every log message format + * specification. Fields, like F_UINT(width, value), are allowed to use results + * of initialization statements. If statement introduces variables (or other + * names, like structures) they must be prefixed with "f_". Statements must be + * enclosed into additional "()". Example: + * + * #define BT_LOG_MESSAGE_CTX_FORMAT \ + * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \ + * YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_UINT(5, f_ru.ru_nsignals), \ + * S(" ")) + * + * F_UINT(width, value) adds unsigned integer value extended with up to width + * spaces (for alignment purposes). Value can be any expression that evaluates + * to unsigned integer. If expression contains non-standard functions, they + * must be declared with F_INIT(). Example: + * + * #define BT_LOG_MESSAGE_CTX_FORMAT \ + * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \ + * F_INIT(( unsigned tickcount(); )), \ + * F_UINT(5, tickcount()), \ + * S(" ")) + * + * Other log message format specifications follow same rules, but have a + * different set of supported fields. + */ +#ifndef BT_LOG_MESSAGE_CTX_FORMAT + #define BT_LOG_MESSAGE_CTX_FORMAT \ + (MONTH, S("-"), DAY, S(BT_LOG_DEF_DELIMITER), \ + HOUR, S(":"), MINUTE, S(":"), SECOND, S("."), MILLISECOND, S(BT_LOG_DEF_DELIMITER), \ + PID, S(BT_LOG_DEF_DELIMITER), TID, S(BT_LOG_DEF_DELIMITER), \ + LEVEL, S(BT_LOG_DEF_DELIMITER)) +#endif +/* Example: + */ +/* Specifies log message tag format. It includes tag prefix and tag. Custom + * information can be added as well. Supported fields: + * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements), + * F_UINT(width, value). + * + * TAG(prefix_delimiter, tag_delimiter) adds following string to log message: + * + * PREFIXTAG + * + * Prefix delimiter will be used only when prefix is not empty. Tag delimiter + * will be used only when prefixed tag is not empty. Example: + * + * #define BT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] ")) + * + * See BT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef BT_LOG_MESSAGE_TAG_FORMAT + #define BT_LOG_MESSAGE_TAG_FORMAT \ + (TAG(".", BT_LOG_DEF_DELIMITER)) +#endif +/* Specifies log message source location format. It includes function name, + * file name and file line. Custom information can be added as well. Supported + * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements), + * F_UINT(width, value). + * + * See BT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#ifndef BT_LOG_MESSAGE_SRC_FORMAT + #define BT_LOG_MESSAGE_SRC_FORMAT \ + (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(BT_LOG_DEF_DELIMITER)) +#endif +/* Fields that can be used in log message format specifications (see above). + * Mentioning them here explicitly, so we know that nobody else defined them + * before us. See BT_LOG_MESSAGE_CTX_FORMAT for details. + */ +#define YEAR YEAR +#define MONTH MONTH +#define DAY DAY +#define MINUTE MINUTE +#define SECOND SECOND +#define MILLISECOND MILLISECOND +#define PID PID +#define TID TID +#define LEVEL LEVEL +#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim) +#define FUNCTION FUNCTION +#define FILENAME FILENAME +#define FILELINE FILELINE +#define S(str) S(str) +#define F_INIT(statements) F_INIT(statements) +#define F_UINT(width, value) F_UINT(width, value) +/* Number of bytes to reserve for EOL in the log line buffer (must be >0). + * Must be larger than or equal to length of BT_LOG_EOL with terminating null. + */ +#ifndef BT_LOG_EOL_SZ + #define BT_LOG_EOL_SZ sizeof(BT_LOG_EOL) +#endif +/* Compile instrumented version of the library to facilitate unit testing. + */ +#ifndef BT_LOG_INSTRUMENTED + #define BT_LOG_INSTRUMENTED 0 +#endif + +#if defined(__linux__) + #if !defined(__ANDROID__) && !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif +#endif +#if defined(__MINGW32__) + #ifdef __STRICT_ANSI__ + #undef __STRICT_ANSI__ + #endif +#endif +#include "common/assert.h" +#include +#include +#include +#include +#include +#include +#include + +#define BT_LOG_OUTPUT_LEVEL dummy + +#include "log.h" +#include + +#if defined(_WIN32) || defined(_WIN64) + #include +#else + #include + #include + #if defined(__linux__) + #include + #elif (defined(__sun__) || defined(__CYGWIN__)) + /* Solaris and Cygwin have no sys/syslimits.h */ + #else + #include + #endif +#endif + +#if defined(__linux__) + #include + #include + #if !defined(__ANDROID__) + #include + #endif +#endif +#if defined(__MACH__) + #include +#endif + +#define INLINE _BT_LOG_INLINE +#define VAR_UNUSED(var) (void)var +#define RETVAL_UNUSED(expr) do { while(expr) break; } while(0) +#define STATIC_ASSERT(name, cond) \ + typedef char assert_##name[(cond)? 1: -1] +#define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) +#ifndef _countof + #define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) +#endif + +#if BT_LOG_INSTRUMENTED + #define INSTRUMENTED_CONST +#else + #define INSTRUMENTED_CONST const +#endif + +#define _PP_PASTE_2(a, b) a ## b +#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) + +#define _PP_PASTE_3(a, b, c) a ## b ## c +#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) + +/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ + * as a single token and requires additional expansion to realize that it's + * actually a list. If not for it, there would be no need in this extra + * expansion. + */ +#define _PP_ID(x) x +#define _PP_NARGS_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,...) _24 +#define _PP_NARGS(...) _PP_ID(_PP_NARGS_N(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) + +/* There is a more efficient way to implement this, but it requires + * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't + * have one. + */ +#define _PP_HEAD__(x, ...) x +#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) +#define _PP_HEAD(xs) _PP_HEAD_ xs +#define _PP_TAIL_(x, ...) (__VA_ARGS__) +#define _PP_TAIL(xs) _PP_TAIL_ xs +#define _PP_UNTUPLE_(...) __VA_ARGS__ +#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs + +/* Apply function macro to each element in tuple. Output is not + * enforced to be a tuple. + */ +#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) +#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) +#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) +#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) +#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) +#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) +#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) +#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) +#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) +#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) +#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) +#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) +#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) +#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) +#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) +#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) +#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) +#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) +#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) +#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) +#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) +#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) +#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) +#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs) + +/* Apply function macro to each element in tuple in reverse order. + * Output is not enforced to be a tuple. + */ +#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) +#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) +#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs) + +/* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible + * fields must be mentioned here. Not counting F_INIT() here because it's + * somewhat special and is handled spearatly (at least for now). + */ +#define _BT_LOG_MESSAGE_FORMAT_MASK__ (0<<0) +#define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR (1<<1) +#define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH (1<<2) +#define _BT_LOG_MESSAGE_FORMAT_MASK__DAY (1<<3) +#define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR (1<<4) +#define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1<<5) +#define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND (1<<6) +#define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1<<7) +#define _BT_LOG_MESSAGE_FORMAT_MASK__PID (1<<8) +#define _BT_LOG_MESSAGE_FORMAT_MASK__TID (1<<9) +#define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1<<10) +#define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1<<11) +#define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1<<12) +#define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1<<13) +#define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1<<14) +#define _BT_LOG_MESSAGE_FORMAT_MASK__S(s) (1<<15) +#define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16) +#define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17) +#define _BT_LOG_MESSAGE_FORMAT_MASK(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field) + +/* Logical "or" of masks of fields used in specified format specification. + */ +#define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \ + (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format)) + +/* Expands to expressions that evaluates to true if field is used in + * specified format specification. Example: + * + * #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT) + * ... + * #endif + */ +#define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ + (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format)) + +/* Same, but checks all supported format specifications. + */ +#define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ + (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT)) + +#define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \ + (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT)) + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ + #define memccpy _memccpy +#endif + +#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || \ + (defined(__MINGW64__) && !defined(__USE_MINGW_ANSI_STDIO)) + #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) + static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) + { + const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); + return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */ + } + #if BT_LOG_OPTIMIZE_SIZE + #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) + static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) + { + va_list va; + va_start(va, fmt); + const int n = fake_vsnprintf(s, sz, fmt, va); + va_end(va); + return n; + } + #endif +#endif + +typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); +typedef void (*pid_cb)(int *const pid, int *const tid); +typedef void (*buffer_cb)(bt_log_message *msg, char *buf); + +typedef struct src_location +{ + const char *const func; + const char *const file; + const unsigned line; +} +src_location; + +typedef struct mem_block +{ + const void *const d; + const unsigned d_sz; +} +mem_block; + +static void time_callback(struct tm *const tm, unsigned *const usec); +static void pid_callback(int *const pid, int *const tid); +static void buffer_callback(bt_log_message *msg, char *buf); + +STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ); +STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ); +static const char c_hex[] = "0123456789abcdef"; + +static __thread char logging_buf[4 * 4096]; + +static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ; +static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; +static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; +static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; + +#if BT_LOG_USE_ANDROID_LOG + #include + + static INLINE int android_lvl(const int lvl) + { + switch (lvl) + { + case BT_LOG_VERBOSE: + return ANDROID_LOG_VERBOSE; + case BT_LOG_DEBUG: + return ANDROID_LOG_DEBUG; + case BT_LOG_INFO: + return ANDROID_LOG_INFO; + case BT_LOG_WARN: + return ANDROID_LOG_WARN; + case BT_LOG_ERROR: + return ANDROID_LOG_ERROR; + case BT_LOG_FATAL: + return ANDROID_LOG_FATAL; + default: + ASSERT_UNREACHABLE("Bad log level"); + return ANDROID_LOG_UNKNOWN; + } + } + + static void out_android_callback(const bt_log_message *const msg, void *arg) + { + VAR_UNUSED(arg); + *msg->p = 0; + const char *tag = msg->p; + if (msg->tag_e != msg->tag_b) + { + tag = msg->tag_b; + *msg->tag_e = 0; + } + __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b); + } + + enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; + #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback +#endif + +#if BT_LOG_USE_NSLOG + #include + CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); + + static INLINE int apple_lvl(const int lvl) + { + switch (lvl) + { + case BT_LOG_VERBOSE: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; + case BT_LOG_DEBUG: + return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; + case BT_LOG_INFO: + return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */; + case BT_LOG_WARN: + return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */; + case BT_LOG_ERROR: + return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */; + case BT_LOG_FATAL: + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; + default: + ASSERT_UNREACHABLE("Bad log level"); + return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; + } + } + + static void out_nslog_callback(const bt_log_message *const msg, void *arg) + { + VAR_UNUSED(arg); + *msg->p = 0; + CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); + } + + enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; + #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback +#endif + +#if BT_LOG_USE_DEBUGSTRING + #include + + static void out_debugstring_callback(const bt_log_message *const msg, void *arg) + { + VAR_UNUSED(arg); + msg->p[0] = '\n'; + msg->p[1] = '\0'; + OutputDebugStringA(msg->buf); + } + + enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD }; + #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback +#endif + +BT_HIDDEN +void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg) +{ + VAR_UNUSED(arg); + const size_t eol_len = sizeof(BT_LOG_EOL) - 1; + memcpy(msg->p, BT_LOG_EOL, eol_len); +#if defined(_WIN32) || defined(_WIN64) + /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and + without FILE_WRITE_DATA */ + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, + (DWORD)(msg->p - msg->buf + eol_len), 0, 0); +#else + /* write() is atomic for buffers less than or equal to PIPE_BUF. */ + RETVAL_UNUSED(write(STDERR_FILENO, msg->buf, + (size_t)(msg->p - msg->buf) + eol_len)); +#endif +} + +static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR}; + +#if !BT_LOG_EXTERN_TAG_PREFIX + BT_LOG_DEFINE_TAG_PREFIX = 0; +#endif + +#if !BT_LOG_EXTERN_GLOBAL_FORMAT + BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH}; +#endif + +#if !BT_LOG_EXTERN_GLOBAL_OUTPUT + #if BT_LOG_USE_ANDROID_LOG + BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; + #elif BT_LOG_USE_NSLOG + BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; + #elif BT_LOG_USE_DEBUGSTRING + BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; + #else + BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR}; + #endif +#endif + +#if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; +#endif + +BT_HIDDEN +const bt_log_spec _bt_log_stderr_spec = +{ + BT_LOG_GLOBAL_FORMAT, + &out_stderr, +}; + +static const bt_log_spec global_spec = +{ + BT_LOG_GLOBAL_FORMAT, + BT_LOG_GLOBAL_OUTPUT, +}; + +#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT) +static char lvl_char(const int lvl) +{ + switch (lvl) + { + case BT_LOG_VERBOSE: + return 'V'; + case BT_LOG_DEBUG: + return 'D'; + case BT_LOG_INFO: + return 'I'; + case BT_LOG_WARN: + return 'W'; + case BT_LOG_ERROR: + return 'E'; + case BT_LOG_FATAL: + return 'F'; + default: + ASSERT_UNREACHABLE("Bad log level"); + return '?'; + } +} +#endif + +#define GCCVER_LESS(MAJOR, MINOR, PATCH) \ + (__GNUC__ < MAJOR || \ + (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ + (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH)))) + +#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0) + #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) + #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) + #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) + #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) + #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) + /* Note: will not store old value of *vp in *ep (non-standard behaviour) */ + #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ + __sync_bool_compare_and_swap(vp, *(ep), d) +#endif + +#if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) +#define TCACHE +#define TCACHE_STALE (0x40000000) +#define TCACHE_FLUID (0x40000000 | 0x80000000) +static unsigned g_tcache_mode = TCACHE_STALE; +static struct timeval g_tcache_tv = {0, 0}; +static struct tm g_tcache_tm = {0}; + +static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm) +{ + unsigned mode; + mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); + if (0 == (mode & TCACHE_FLUID)) + { + mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); + if (0 == (mode & TCACHE_FLUID)) + { + if (g_tcache_tv.tv_sec == tv->tv_sec) + { + *tm = g_tcache_tm; + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + return !0; + } + __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); + } + __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); + } + return 0; +} + +static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm) +{ + unsigned stale = TCACHE_STALE; + if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, + 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) + { + g_tcache_tv = *tv; + g_tcache_tm = *tm; + __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); + } +} +#endif + +static void time_callback(struct tm *const tm, unsigned *const msec) +{ +#if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED + VAR_UNUSED(tm); + VAR_UNUSED(msec); +#else + #if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + GetLocalTime(&st); + tm->tm_year = st.wYear; + tm->tm_mon = st.wMonth; + tm->tm_mday = st.wDay; + tm->tm_wday = st.wDayOfWeek; + tm->tm_hour = st.wHour; + tm->tm_min = st.wMinute; + tm->tm_sec = st.wSecond; + *msec = st.wMilliseconds; + #else + struct timeval tv; + gettimeofday(&tv, 0); + #ifndef TCACHE + localtime_r(&tv.tv_sec, tm); + #else + if (!tcache_get(&tv, tm)) + { + localtime_r(&tv.tv_sec, tm); + tcache_set(&tv, tm); + } + #endif + *msec = (unsigned)tv.tv_usec / 1000; + #endif +#endif +} + +static void pid_callback(int *const pid, int *const tid) +{ +#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(pid); +#else + #if defined(_WIN32) || defined(_WIN64) + *pid = GetCurrentProcessId(); + #else + *pid = getpid(); + #endif +#endif + +#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(tid); +#else + #if defined(_WIN32) || defined(_WIN64) + *tid = GetCurrentThreadId(); + #elif defined(__CYGWIN__) + pthread_t thr = pthread_self(); + *tid = (int)pthread_getsequence_np(&thr); + #elif defined(__sun__) + *tid = (int)pthread_self(); + #elif defined(__ANDROID__) + *tid = gettid(); + #elif defined(__linux__) + *tid = syscall(SYS_gettid); + #elif defined(__MACH__) + *tid = (int)pthread_mach_thread_np(pthread_self()); + #else + #define Platform not supported + #endif +#endif +} + +static void buffer_callback(bt_log_message *msg, char *buf) +{ + msg->e = (msg->p = msg->buf = buf) + g_buf_sz; +} + +#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) +static const char *funcname(const char *func) +{ + return func? func: ""; +} +#endif + +#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) +static const char *filename(const char *file) +{ + const char *f = file; + for (const char *p = file; 0 != *p; ++p) + { + if ('/' == *p || '\\' == *p) + { + f = p + 1; + } + } + return f; +} +#endif + +static INLINE size_t nprintf_size(bt_log_message *const msg) +{ + // *nprintf() always puts 0 in the end when input buffer is not empty. This + // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which + // leaves space for one more character. Some put_xxx() functions don't use + // *nprintf() and could use that last character. In that case log line will + // have multiple (two) half-written parts which is confusing. To workaround + // that we allow *nprintf() to write its 0 in the eol area (which is always + // not empty). + return (size_t)(msg->e - msg->p + 1); +} + +static INLINE void put_nprintf(bt_log_message *const msg, const int n) +{ + if (0 < n) + { + msg->p = n < msg->e - msg->p? msg->p + n: msg->e; + } +} + +static INLINE char *put_padding_r(const unsigned w, const char wc, + char *p, char *e) +{ + for (char *const b = e - w; b < p; *--p = wc) {} + return p; +} + +static char *put_integer_r(unsigned v, const int sign, + const unsigned w, const char wc, char *const e) +{ + static const char _signs[] = {'-', '0', '+'}; + const char *const signs = _signs + 1; + char *p = e; + do { *--p = '0' + v % 10; } while (0 != (v /= 10)); + if (0 == sign) return put_padding_r(w, wc, p, e); + if ('0' != wc) + { + *--p = signs[sign]; + return put_padding_r(w, wc, p, e); + } + p = put_padding_r(w, wc, p, e + 1); + *--p = signs[sign]; + return p; +} + +static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc, + char *const e) +{ + return put_integer_r(v, 0, w, wc, e); +} + +static INLINE char *put_int_r(const int v, const unsigned w, const char wc, + char *const e) +{ + return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e) + : put_integer_r((unsigned)-v, -1, w, wc, e); +} + +static INLINE char *put_stringn(const char *const s_p, const char *const s_e, + char *const p, char *const e) +{ + const ptrdiff_t m = e - p; + ptrdiff_t n = s_e - s_p; + if (n > m) + { + n = m; + } + memcpy(p, s_p, n); + return p + n; +} + +static INLINE char *put_string(const char *s, char *p, char *const e) +{ + const ptrdiff_t n = e - p; + char *const c = (char *)memccpy(p, s, '\0', n); + return 0 != c? c - 1: e; +} + +static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, + char *const p, char *const e) +{ + char buf[16]; + char *const se = buf + _countof(buf); + char *sp = put_uint_r(v, w, wc, se); + return put_stringn(sp, se, p, e); +} + +#define PUT_CSTR_R(p, STR) \ + do { \ + for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ + *--(p) = (STR)[i]; \ + } \ + } _BT_LOG_ONCE + +#define PUT_CSTR_CHECKED(p, e, STR) \ + do { \ + for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ + *(p)++ = (STR)[i]; \ + } \ + } _BT_LOG_ONCE + +/* F_INIT field support. + */ +#define _BT_LOG_MESSAGE_FORMAT_INIT__ +#define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR +#define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH +#define _BT_LOG_MESSAGE_FORMAT_INIT__DAY +#define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR +#define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE +#define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND +#define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND +#define _BT_LOG_MESSAGE_FORMAT_INIT__PID +#define _BT_LOG_MESSAGE_FORMAT_INIT__TID +#define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL +#define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) +#define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION +#define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME +#define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE +#define _BT_LOG_MESSAGE_FORMAT_INIT__S(s) +#define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); +#define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) +#define _BT_LOG_MESSAGE_FORMAT_INIT(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field) + +/* Implements generation of printf-like format string for log message + * format specification. + */ +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) + +/* Implements generation of printf-like format parameters for log message + * format specification. + */ +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR ,(unsigned)(tm.tm_year + 1900) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH ,(unsigned)(tm.tm_mon + 1) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY ,(unsigned)tm.tm_mday +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR ,(unsigned)tm.tm_hour +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE ,(unsigned)tm.tm_min +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND ,(unsigned)tm.tm_sec +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND ,(unsigned)msec +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID ,pid +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID ,tid +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL ,(char)lvl_char(msg->lvl) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION ,funcname(src->func) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME ,filename(src->file) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE ,src->line +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v +#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) + +/* Implements generation of put_xxx_t statements for log message specification. + */ +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__ +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR p = put_uint_r(tm.tm_year + 1900, 4, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND p = put_uint_r(msec, 3, '0', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) +#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p); +#define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) + +static void put_ctx(bt_log_message *const msg) +{ + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT) +#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT) + VAR_UNUSED(msg); +#else + #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED + struct tm tm; + unsigned msec; + g_time_cb(&tm, &msec); + #endif + #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \ + _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) + int pid, tid; + g_pid_cb(&pid, &tid); + #endif + + #if BT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT) + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT)); + put_nprintf(msg, n); + #else + char buf[64]; + char *const e = buf + sizeof(buf); + char *p = e; + _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT) + msg->p = put_stringn(p, e, msg->p, msg->e); + #endif +#endif +} + +#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ + do { \ + const char *ch; \ + msg->tag_b = msg->p; \ + if (0 != (ch = _bt_log_tag_prefix)) { \ + for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ + } \ + if (0 != (ch = tag) && 0 != tag[0]) { \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ + } \ + for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ + } \ + msg->tag_e = msg->p; \ + if (msg->tag_b != msg->p) { \ + PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ + } \ + } _BT_LOG_ONCE + +/* Implements simple put statements for log message specification. + */ +#define _BT_LOG_MESSAGE_FORMAT_PUT__ +#define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED +#define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) PUT_TAG(msg, tag, pd, td); +#define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION msg->p = put_string(funcname(src->func), msg->p, msg->e); +#define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME msg->p = put_string(filename(src->file), msg->p, msg->e); +#define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); +#define _BT_LOG_MESSAGE_FORMAT_PUT__S(s) PUT_CSTR_CHECKED(msg->p, msg->e, s); +#define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) +#define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e); +#define _BT_LOG_MESSAGE_FORMAT_PUT(field) \ + _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field) + +static void put_tag(bt_log_message *const msg, const char *const tag) +{ + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT) +#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(tag); +#endif +#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT) + VAR_UNUSED(msg); +#else + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT) +#endif +} + +static void put_src(bt_log_message *const msg, const src_location *const src) +{ + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT) +#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \ + !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \ + !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(src); +#endif +#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT) + VAR_UNUSED(msg); +#else + #if BT_LOG_OPTIMIZE_SIZE + int n; + n = snprintf(msg->p, nprintf_size(msg), + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT) + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT)); + put_nprintf(msg, n); + #else + _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT) + #endif +#endif +} + +static void put_msg(bt_log_message *const msg, + const char *const fmt, va_list va) +{ + int n; + msg->msg_b = msg->p; + n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); + put_nprintf(msg, n); +} + +static void output_mem(const bt_log_spec *log, bt_log_message *const msg, + const mem_block *const mem) +{ + if (0 == mem->d || 0 == mem->d_sz) + { + return; + } + const unsigned char *mem_p = (const unsigned char *)mem->d; + const unsigned char *const mem_e = mem_p + mem->d_sz; + const unsigned char *mem_cut; + const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; + char *const hex_b = msg->msg_b; + char *const ascii_b = hex_b + 2 * mem_width + 2; + char *const ascii_e = ascii_b + mem_width; + if (msg->e < ascii_e) + { + return; + } + while (mem_p != mem_e) + { + char *hex = hex_b; + char *ascii = ascii_b; + for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e; + mem_cut != mem_p; ++mem_p) + { + const unsigned char ch = *mem_p; + *hex++ = c_hex[(0xf0 & ch) >> 4]; + *hex++ = c_hex[(0x0f & ch)]; + *ascii++ = isprint(ch)? (char)ch: '?'; + } + while (hex != ascii_b) + { + *hex++ = ' '; + } + msg->p = ascii; + log->output->callback(msg, log->output->arg); + } +} + +BT_HIDDEN +void bt_log_set_tag_prefix(const char *const prefix) +{ + _bt_log_tag_prefix = prefix; +} + +BT_HIDDEN +void bt_log_set_mem_width(const unsigned w) +{ + _bt_log_global_format.mem_width = w; +} + +BT_HIDDEN +void bt_log_set_output_level(const int lvl) +{ + _bt_log_global_output_lvl = lvl; +} + +BT_HIDDEN +void bt_log_set_output_v(const unsigned mask, void *const arg, + const bt_log_output_cb callback) +{ + _bt_log_global_output.mask = mask; + _bt_log_global_output.arg = arg; + _bt_log_global_output.callback = callback; +} + +static void _bt_log_write_imp( + const bt_log_spec *log, + const src_location *const src, const mem_block *const mem, + const int lvl, const char *const tag, const char *const fmt, va_list va) +{ + bt_log_message msg; + char *buf = logging_buf; + const unsigned mask = log->output->mask; + msg.lvl = lvl; + msg.tag = tag; + g_buffer_cb(&msg, buf); + const char *rst_color_p = bt_common_color_reset(); + const char *rst_color_e = rst_color_p + strlen(rst_color_p); + const char *color_p = ""; + const char *color_e = color_p; + + switch (lvl) { + case BT_LOG_INFO: + color_p = bt_common_color_fg_blue(); + color_e = color_p + strlen(color_p); + break; + case BT_LOG_WARN: + color_p = bt_common_color_fg_yellow(); + color_e = color_p + strlen(color_p); + break; + case BT_LOG_ERROR: + case BT_LOG_FATAL: + color_p = bt_common_color_fg_red(); + color_e = color_p + strlen(color_p); + break; + default: + break; + } + + msg.p = put_stringn(color_p, color_e, msg.p, msg.e); + + if (BT_LOG_PUT_CTX & mask) + { + put_ctx(&msg); + } + if (BT_LOG_PUT_TAG & mask) + { + put_tag(&msg, tag); + } + if (0 != src && BT_LOG_PUT_SRC & mask) + { + put_src(&msg, src); + } + if (BT_LOG_PUT_MSG & mask) + { + put_msg(&msg, fmt, va); + } + msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e); + log->output->callback(&msg, log->output->arg); + if (0 != mem && BT_LOG_PUT_MSG & mask) + { + output_mem(log, &msg, mem); + } +} + +BT_HIDDEN +void _bt_log_write_d( + const char *const func, const char *const file, const unsigned line, + const int lvl, const char *const tag, + const char *const fmt, ...) +{ + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_aux_d( + const char *const func, const char *const file, const unsigned line, + const bt_log_spec *const log, const int lvl, const char *const tag, + const char *const fmt, ...) +{ + const src_location src = {func, file, line}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write(const int lvl, const char *const tag, + const char *const fmt, ...) +{ + va_list va; + va_start(va, fmt); + _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_aux( + const bt_log_spec *const log, const int lvl, const char *const tag, + const char *const fmt, ...) +{ + va_list va; + va_start(va, fmt); + _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_mem_d( + const char *const func, const char *const file, const unsigned line, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) +{ + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_mem_aux_d( + const char *const func, const char *const file, const unsigned line, + const bt_log_spec *const log, const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) +{ + const src_location src = {func, file, line}; + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_mem(const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) +{ + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); + va_end(va); +} + +BT_HIDDEN +void _bt_log_write_mem_aux( + const bt_log_spec *const log, const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) +{ + const mem_block mem = {d, d_sz}; + va_list va; + va_start(va, fmt); + _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); + va_end(va); +} diff --git a/src/logging/log.h b/src/logging/log.h new file mode 100644 index 00000000..44f778a0 --- /dev/null +++ b/src/logging/log.h @@ -0,0 +1,1054 @@ +/* + * This is zf_log.h, modified with Babeltrace prefixes. + * See . + * See logging/LICENSE in the Babeltrace source tree. + */ + +#pragma once + +#ifndef BABELTRACE_LOGGING_INTERNAL_H +#define BABELTRACE_LOGGING_INTERNAL_H + +#include +#include +#include +#include "common/babeltrace.h" + +/* To detect incompatible changes you can define BT_LOG_VERSION_REQUIRED to be + * the current value of BT_LOG_VERSION before including this file (or via + * compiler command line): + * + * #define BT_LOG_VERSION_REQUIRED 4 + * #include "logging.h" + * + * Compilation will fail when included file has different version. + */ +#define BT_LOG_VERSION 4 +#if defined(BT_LOG_VERSION_REQUIRED) + #if BT_LOG_VERSION_REQUIRED != BT_LOG_VERSION + #error different bt_log version required + #endif +#endif + +/* Log level guideline: + * - BT_LOG_FATAL - happened something impossible and absolutely unexpected. + * Process can't continue and must be terminated. + * Example: division by zero, unexpected modifications from other thread. + * - BT_LOG_ERROR - happened something possible, but highly unexpected. The + * process is able to recover and continue execution. + * Example: out of memory (could also be FATAL if not handled properly). + * - BT_LOG_WARN - happened something that *usually* should not happen and + * significantly changes application behavior for some period of time. + * Example: configuration file not found, auth error. + * - BT_LOG_INFO - happened significant life cycle event or major state + * transition. + * Example: app started, user logged in. + * - BT_LOG_DEBUG - minimal set of events that could help to reconstruct the + * execution path. Usually disabled in release builds. + * - BT_LOG_VERBOSE - all other events. Usually disabled in release builds. + * + * *Ideally*, log file of debugged, well tested, production ready application + * should be empty or very small. Choosing a right log level is as important as + * providing short and self descriptive log message. + */ +#define BT_LOG_VERBOSE BT_LOGGING_LEVEL_VERBOSE +#define BT_LOG_DEBUG BT_LOGGING_LEVEL_DEBUG +#define BT_LOG_INFO BT_LOGGING_LEVEL_INFO +#define BT_LOG_WARN BT_LOGGING_LEVEL_WARN +#define BT_LOG_ERROR BT_LOGGING_LEVEL_ERROR +#define BT_LOG_FATAL BT_LOGGING_LEVEL_FATAL +#define BT_LOG_NONE BT_LOGGING_LEVEL_NONE + +/* "Current" log level is a compile time check and has no runtime overhead. Log + * level that is below current log level it said to be "disabled". Otherwise, + * it's "enabled". Log messages that are disabled has no runtime overhead - they + * are converted to no-op by preprocessor and then eliminated by compiler. + * Current log level is configured per compilation module (.c/.cpp/.m file) by + * defining BT_LOG_DEF_LEVEL or BT_LOG_LEVEL. BT_LOG_LEVEL has higer priority + * and when defined overrides value provided by BT_LOG_DEF_LEVEL. + * + * Common practice is to define default current log level with BT_LOG_DEF_LEVEL + * in build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire + * project or target: + * + * CC_ARGS := -DBT_LOG_DEF_LEVEL=BT_LOG_INFO + * + * And when necessary to override it with BT_LOG_LEVEL in .c/.cpp/.m files + * before including bt_log.h: + * + * #define BT_LOG_LEVEL BT_LOG_VERBOSE + * #include "logging.h" + * + * If both BT_LOG_DEF_LEVEL and BT_LOG_LEVEL are undefined, then BT_LOG_INFO + * will be used for release builds (NDEBUG is defined) and BT_LOG_DEBUG + * otherwise (NDEBUG is not defined). + */ +#if defined(BT_LOG_LEVEL) + #define _BT_LOG_LEVEL BT_LOG_LEVEL +#elif defined(BT_LOG_DEF_LEVEL) + #define _BT_LOG_LEVEL BT_LOG_DEF_LEVEL +#else + #ifdef NDEBUG + #define _BT_LOG_LEVEL BT_LOG_INFO + #else + #define _BT_LOG_LEVEL BT_LOG_DEBUG + #endif +#endif + +/* "Output" log level is a runtime check. When log level is below output log + * level it said to be "turned off" (or just "off" for short). Otherwise it's + * "turned on" (or just "on"). Log levels that were "disabled" (see + * BT_LOG_LEVEL and BT_LOG_DEF_LEVEL) can't be "turned on", but "enabled" log + * levels could be "turned off". Only messages with log level which is + * "turned on" will reach output facility. All other messages will be ignored + * (and their arguments will not be evaluated). Output log level is a global + * property and configured per process using bt_log_set_output_level() function + * which can be called at any time. + * + * Though in some cases it could be useful to configure output log level per + * compilation module or per library. There are two ways to achieve that: + * - Define BT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired output + * log level. + * - Copy bt_log.h and bt_log.c files into your library and build it with + * BT_LOG_LIBRARY_PREFIX defined to library specific prefix. See + * BT_LOG_LIBRARY_PREFIX for more details. + * + * When defined, BT_LOG_OUTPUT_LEVEL must evaluate to integral value that + * corresponds to desired output log level. Use it only when compilation module + * is required to have output log level which is different from global output + * log level set by bt_log_set_output_level() function. For other cases, + * consider defining BT_LOG_LEVEL or using bt_log_set_output_level() function. + * + * Example: + * + * #define BT_LOG_OUTPUT_LEVEL g_module_log_level + * #include "logging.h" + * static int g_module_log_level = BT_LOG_INFO; + * static void foo() { + * BT_LOGI("Will check g_module_log_level for output log level"); + * } + * void debug_log(bool on) { + * g_module_log_level = on? BT_LOG_DEBUG: BT_LOG_INFO; + * } + * + * Note on performance. This expression will be evaluated each time message is + * logged (except when message log level is "disabled" - see BT_LOG_LEVEL for + * details). Keep this expression as simple as possible, otherwise it will not + * only add runtime overhead, but also will increase size of call site (which + * will result in larger executable). The prefered way is to use integer + * variable (as in example above). If structure must be used, log_level field + * must be the first field in this structure: + * + * #define BT_LOG_OUTPUT_LEVEL (g_config.log_level) + * #include "logging.h" + * struct config { + * int log_level; + * unsigned other_field; + * [...] + * }; + * static config g_config = {BT_LOG_INFO, 0, ...}; + * + * This allows compiler to generate more compact load instruction (no need to + * specify offset since it's zero). Calling a function to get output log level + * is generaly a bad idea, since it will increase call site size and runtime + * overhead even further. + */ +#if defined(BT_LOG_OUTPUT_LEVEL) + #define _BT_LOG_OUTPUT_LEVEL BT_LOG_OUTPUT_LEVEL +#else + /* + * We disallow this to make sure Babeltrace modules always + * have their own local log level. + */ + #error No log level symbol specified: please define BT_LOG_OUTPUT_LEVEL before including this header. +#endif + +/* "Tag" is a compound string that could be associated with a log message. It + * consists of tag prefix and tag (both are optional). + * + * Tag prefix is a global property and configured per process using + * bt_log_set_tag_prefix() function. Tag prefix identifies context in which + * component or module is running (e.g. process name). For example, the same + * library could be used in both client and server processes that work on the + * same machine. Tag prefix could be used to easily distinguish between them. + * For more details about tag prefix see bt_log_set_tag_prefix() function. Tag + * prefix + * + * Tag identifies component or module. It is configured per compilation module + * (.c/.cpp/.m file) by defining BT_LOG_TAG or BT_LOG_DEF_TAG. BT_LOG_TAG has + * higer priority and when defined overrides value provided by BT_LOG_DEF_TAG. + * When defined, value must evaluate to (const char *), so for strings double + * quotes must be used. + * + * Default tag could be defined with BT_LOG_DEF_TAG in build script (e.g. + * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target: + * + * CC_ARGS := -DBT_LOG_DEF_TAG=\"MISC\" + * + * And when necessary could be overriden with BT_LOG_TAG in .c/.cpp/.m files + * before including bt_log.h: + * + * #define BT_LOG_TAG "MAIN" + * #include "logging.h" + * + * If both BT_LOG_DEF_TAG and BT_LOG_TAG are undefined no tag will be added to + * the log message (tag prefix still could be added though). + * + * Output example: + * + * 04-29 22:43:20.244 40059 1299 I hello.MAIN Number of arguments: 1 + * | | + * | +- tag (e.g. module) + * +- tag prefix (e.g. process name) + */ +#if defined(BT_LOG_TAG) + #define _BT_LOG_TAG BT_LOG_TAG +#elif defined(BT_LOG_DEF_TAG) + #define _BT_LOG_TAG BT_LOG_DEF_TAG +#else + #define _BT_LOG_TAG 0 +#endif + +/* Source location is part of a log line that describes location (function or + * method name, file name and line number, e.g. "runloop@main.cpp:68") of a + * log statement that produced it. + * Source location formats are: + * - BT_LOG_SRCLOC_NONE - don't add source location to log line. + * - BT_LOG_SRCLOC_SHORT - add source location in short form (file and line + * number, e.g. "@main.cpp:68"). + * - BT_LOG_SRCLOC_LONG - add source location in long form (function or method + * name, file and line number, e.g. "runloop@main.cpp:68"). + */ +#define BT_LOG_SRCLOC_NONE 0 +#define BT_LOG_SRCLOC_SHORT 1 +#define BT_LOG_SRCLOC_LONG 2 + +/* Source location format is configured per compilation module (.c/.cpp/.m + * file) by defining BT_LOG_DEF_SRCLOC or BT_LOG_SRCLOC. BT_LOG_SRCLOC has + * higer priority and when defined overrides value provided by + * BT_LOG_DEF_SRCLOC. + * + * Common practice is to define default format with BT_LOG_DEF_SRCLOC in + * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire + * project or target: + * + * CC_ARGS := -DBT_LOG_DEF_SRCLOC=BT_LOG_SRCLOC_LONG + * + * And when necessary to override it with BT_LOG_SRCLOC in .c/.cpp/.m files + * before including bt_log.h: + * + * #define BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE + * #include "logging.h" + * + * If both BT_LOG_DEF_SRCLOC and BT_LOG_SRCLOC are undefined, then + * BT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined) and + * BT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined). + */ +#if defined(BT_LOG_SRCLOC) + #define _BT_LOG_SRCLOC BT_LOG_SRCLOC +#elif defined(BT_LOG_DEF_SRCLOC) + #define _BT_LOG_SRCLOC BT_LOG_DEF_SRCLOC +#else + #ifdef NDEBUG + #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE + #else + #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_LONG + #endif +#endif +#if BT_LOG_SRCLOC_LONG == _BT_LOG_SRCLOC + #define _BT_LOG_SRCLOC_FUNCTION _BT_LOG_FUNCTION +#else + #define _BT_LOG_SRCLOC_FUNCTION 0 +#endif + +/* Censoring provides conditional logging of secret information, also known as + * Personally Identifiable Information (PII) or Sensitive Personal Information + * (SPI). Censoring can be either enabled (BT_LOG_CENSORED) or disabled + * (BT_LOG_UNCENSORED). When censoring is enabled, log statements marked as + * "secrets" will be ignored and will have zero overhead (arguments also will + * not be evaluated). + */ +#define BT_LOG_CENSORED 1 +#define BT_LOG_UNCENSORED 0 + +/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining + * BT_LOG_DEF_CENSORING or BT_LOG_CENSORING. BT_LOG_CENSORING has higer priority + * and when defined overrides value provided by BT_LOG_DEF_CENSORING. + * + * Common practice is to define default censoring with BT_LOG_DEF_CENSORING in + * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire + * project or target: + * + * CC_ARGS := -DBT_LOG_DEF_CENSORING=BT_LOG_CENSORED + * + * And when necessary to override it with BT_LOG_CENSORING in .c/.cpp/.m files + * before including bt_log.h (consider doing it only for debug purposes and be + * very careful not to push such temporary changes to source control): + * + * #define BT_LOG_CENSORING BT_LOG_UNCENSORED + * #include "logging.h" + * + * If both BT_LOG_DEF_CENSORING and BT_LOG_CENSORING are undefined, then + * BT_LOG_CENSORED will be used for release builds (NDEBUG is defined) and + * BT_LOG_UNCENSORED otherwise (NDEBUG is not defined). + */ +#if defined(BT_LOG_CENSORING) + #define _BT_LOG_CENSORING BT_LOG_CENSORING +#elif defined(BT_LOG_DEF_CENSORING) + #define _BT_LOG_CENSORING BT_LOG_DEF_CENSORING +#else + #ifdef NDEBUG + #define _BT_LOG_CENSORING BT_LOG_CENSORED + #else + #define _BT_LOG_CENSORING BT_LOG_UNCENSORED + #endif +#endif + +/* Check censoring at compile time. Evaluates to true when censoring is disabled + * (i.e. when secrets will be logged). For example: + * + * #if BT_LOG_SECRETS + * char ssn[16]; + * getSocialSecurityNumber(ssn); + * BT_LOGI("Customer ssn: %s", ssn); + * #endif + * + * See BT_LOG_SECRET() macro for a more convenient way of guarding single log + * statement. + */ +#define BT_LOG_SECRETS (BT_LOG_UNCENSORED == _BT_LOG_CENSORING) + +/* Static (compile-time) initialization support allows to configure logging + * before entering main() function. This mostly useful in C++ where functions + * and methods could be called during initialization of global objects. Those + * functions and methods could record log messages too and for that reason + * static initialization of logging configuration is customizable. + * + * Macros below allow to specify values to use for initial configuration: + * - BT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none) + * - BT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see + * BT_LOG_MEM_WIDTH in bt_log.c) + * - BT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: stderr or + * platform specific, see BT_LOG_USE_XXX macros in bt_log.c) + * - BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level (default: 0 - + * all levals are "turned on") + * + * For example, in log_config.c: + * + * #include "logging.h" + * BT_LOG_DEFINE_TAG_PREFIX = "MyApp"; + * BT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH}; + * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback, 0}; + * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_INFO; + * + * However, to use any of those macros bt_log library must be compiled with + * following macros defined: + * - to use BT_LOG_DEFINE_TAG_PREFIX define BT_LOG_EXTERN_TAG_PREFIX + * - to use BT_LOG_DEFINE_GLOBAL_FORMAT define BT_LOG_EXTERN_GLOBAL_FORMAT + * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT define BT_LOG_EXTERN_GLOBAL_OUTPUT + * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define + * BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL + * + * When bt_log library compiled with one of BT_LOG_EXTERN_XXX macros defined, + * corresponding BT_LOG_DEFINE_XXX macro MUST be used exactly once somewhere. + * Otherwise build will fail with link error (undefined symbol). + */ +#define BT_LOG_DEFINE_TAG_PREFIX BT_HIDDEN const char *_bt_log_tag_prefix +#define BT_LOG_DEFINE_GLOBAL_FORMAT BT_HIDDEN bt_log_format _bt_log_global_format +#define BT_LOG_DEFINE_GLOBAL_OUTPUT BT_HIDDEN bt_log_output _bt_log_global_output +#define BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL BT_HIDDEN int _bt_log_global_output_lvl + +/* Pointer to global format options. Direct modification is not allowed. Use + * bt_log_set_mem_width() instead. Could be used to initialize bt_log_spec + * structure: + * + * const bt_log_output g_output = {BT_LOG_PUT_STD, output_callback, 0}; + * const bt_log_spec g_spec = {BT_LOG_GLOBAL_FORMAT, &g_output}; + * BT_LOGI_AUX(&g_spec, "Hello"); + */ +#define BT_LOG_GLOBAL_FORMAT ((const bt_log_format *)&_bt_log_global_format) + +/* Pointer to global output variable. Direct modification is not allowed. Use + * bt_log_set_output_v() or bt_log_set_output_p() instead. Could be used to + * initialize bt_log_spec structure: + * + * const bt_log_format g_format = {40}; + * const bt_log_spec g_spec = {g_format, BT_LOG_GLOBAL_OUTPUT}; + * BT_LOGI_AUX(&g_spec, "Hello"); + */ +#define BT_LOG_GLOBAL_OUTPUT ((const bt_log_output *)&_bt_log_global_output) + +/* When defined, all library symbols produced by linker will be prefixed with + * provided value. That allows to use bt_log library privately in another + * libraries without exposing bt_log symbols in their original form (to avoid + * possible conflicts with other libraries / components that also could use + * bt_log for logging). Value must be without quotes, for example: + * + * CC_ARGS := -DBT_LOG_LIBRARY_PREFIX=my_lib_ + * + * Note, that in this mode BT_LOG_LIBRARY_PREFIX must be defined when building + * bt_log library AND it also must be defined to the same value when building + * a library that uses it. For example, consider fictional KittyHttp library + * that wants to use bt_log for logging. First approach that could be taken is + * to add bt_log.h and bt_log.c to the KittyHttp's source code tree directly. + * In that case it will be enough just to define BT_LOG_LIBRARY_PREFIX in + * KittyHttp's build script: + * + * // KittyHttp/CMakeLists.txt + * target_compile_definitions(KittyHttp PRIVATE + * "BT_LOG_LIBRARY_PREFIX=KittyHttp_") + * + * If KittyHttp doesn't want to include bt_log source code in its source tree + * and wants to build bt_log as a separate library than bt_log library must be + * built with BT_LOG_LIBRARY_PREFIX defined to KittyHttp_ AND KittyHttp library + * itself also needs to define BT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do + * so either in its build script, as in example above, or by providing a + * wrapper header that KittyHttp library will need to use instead of bt_log.h: + * + * // KittyHttpLogging.h + * #define BT_LOG_LIBRARY_PREFIX KittyHttp_ + * #include "logging.h" + * + * Regardless of the method chosen, the end result is that bt_log symbols will + * be prefixed with "KittyHttp_", so if a user of KittyHttp (say DogeBrowser) + * also uses bt_log for logging, they will not interferer with each other. Both + * will have their own log level, output facility, format options etc. + */ +#ifdef BT_LOG_LIBRARY_PREFIX + #define _BT_LOG_DECOR__(prefix, name) prefix ## name + #define _BT_LOG_DECOR_(prefix, name) _BT_LOG_DECOR__(prefix, name) + #define _BT_LOG_DECOR(name) _BT_LOG_DECOR_(BT_LOG_LIBRARY_PREFIX, name) + + #define bt_log_set_tag_prefix _BT_LOG_DECOR(bt_log_set_tag_prefix) + #define bt_log_set_mem_width _BT_LOG_DECOR(bt_log_set_mem_width) + #define bt_log_set_output_level _BT_LOG_DECOR(bt_log_set_output_level) + #define bt_log_set_output_v _BT_LOG_DECOR(bt_log_set_output_v) + #define bt_log_set_output_p _BT_LOG_DECOR(bt_log_set_output_p) + #define bt_log_out_stderr_callback _BT_LOG_DECOR(bt_log_out_stderr_callback) + #define _bt_log_tag_prefix _BT_LOG_DECOR(_bt_log_tag_prefix) + #define _bt_log_global_format _BT_LOG_DECOR(_bt_log_global_format) + #define _bt_log_global_output _BT_LOG_DECOR(_bt_log_global_output) + #define _bt_log_global_output_lvl _BT_LOG_DECOR(_bt_log_global_output_lvl) + #define _bt_log_write_d _BT_LOG_DECOR(_bt_log_write_d) + #define _bt_log_write_aux_d _BT_LOG_DECOR(_bt_log_write_aux_d) + #define _bt_log_write _BT_LOG_DECOR(_bt_log_write) + #define _bt_log_write_aux _BT_LOG_DECOR(_bt_log_write_aux) + #define _bt_log_write_mem_d _BT_LOG_DECOR(_bt_log_write_mem_d) + #define _bt_log_write_mem_aux_d _BT_LOG_DECOR(_bt_log_write_mem_aux_d) + #define _bt_log_write_mem _BT_LOG_DECOR(_bt_log_write_mem) + #define _bt_log_write_mem_aux _BT_LOG_DECOR(_bt_log_write_mem_aux) + #define _bt_log_stderr_spec _BT_LOG_DECOR(_bt_log_stderr_spec) +#endif + +#if defined(__printflike) + #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __printflike(str_index, first_to_check) +#elif defined(__MINGW_PRINTF_FORMAT) + #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __attribute__((format(__MINGW_PRINTF_FORMAT, str_index, first_to_check))) +#elif defined(__GNUC__) + #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \ + __attribute__((format(__printf__, str_index, first_to_check))) +#else + #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) +#endif + +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__) + #define _BT_LOG_FUNCTION __FUNCTION__ +#else + #define _BT_LOG_FUNCTION __func__ +#endif + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) + #define _BT_LOG_INLINE __inline + #define _BT_LOG_IF(cond) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + if(cond) \ + __pragma(warning(pop)) + #define _BT_LOG_WHILE(cond) \ + __pragma(warning(push)) \ + __pragma(warning(disable:4127)) \ + while(cond) \ + __pragma(warning(pop)) +#else + #define _BT_LOG_INLINE inline + #define _BT_LOG_IF(cond) if(cond) + #define _BT_LOG_WHILE(cond) while(cond) +#endif +#define _BT_LOG_NEVER _BT_LOG_IF(0) +#define _BT_LOG_ONCE _BT_LOG_WHILE(0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set tag prefix. Prefix will be separated from the tag with dot ('.'). + * Use 0 or empty string to disable (default). Common use is to set it to + * the process (or build target) name (e.g. to separate client and server + * processes). Function will NOT copy provided prefix string, but will store the + * pointer. Hence specified prefix string must remain valid. See + * BT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() function. + * See BT_LOG_TAG for more information about tag and tag prefix. + */ +void bt_log_set_tag_prefix(const char *const prefix); + +/* Set number of bytes per log line in memory (ASCII-HEX) output. Example: + * + * I hello.MAIN 4c6f72656d20697073756d20646f6c6f Lorem ipsum dolo + * |<- w bytes ->| |<- w chars ->| + * + * See BT_LOGF_MEM and BT_LOGF_MEM_AUX for more details. + */ +void bt_log_set_mem_width(const unsigned w); + +/* Set "output" log level. See BT_LOG_LEVEL and BT_LOG_OUTPUT_LEVEL for more + * info about log levels. + */ +void bt_log_set_output_level(const int lvl); + +/* Put mask is a set of flags that define what fields will be added to each + * log message. Default value is BT_LOG_PUT_STD and other flags could be used to + * alter its behavior. See bt_log_set_output_v() for more details. + * + * Note about BT_LOG_PUT_SRC: it will be added only in debug builds (NDEBUG is + * not defined). + */ +enum +{ + BT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */ + BT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */ + BT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */ + BT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */ + BT_LOG_PUT_STD = 0xffff, /* everything (default) */ +}; + +typedef struct bt_log_message +{ + int lvl; /* Log level of the message */ + const char *tag; /* Associated tag (without tag prefix) */ + char *buf; /* Buffer start */ + char *e; /* Buffer end (last position where EOL with 0 could be written) */ + char *p; /* Buffer content end (append position) */ + char *tag_b; /* Prefixed tag start */ + char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */ + char *msg_b; /* Message start (expanded format string) */ +} +bt_log_message; + +/* Type of output callback function. It will be called for each log line allowed + * by both "current" and "output" log levels ("enabled" and "turned on"). + * Callback function is allowed to modify content of the buffers pointed by the + * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg + * is UTF-8 encoded (no BOM mark). + */ +typedef void (*bt_log_output_cb)(const bt_log_message *msg, void *arg); + +/* Format options. For more details see bt_log_set_mem_width(). + */ +typedef struct bt_log_format +{ + unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */ +} +bt_log_format; + +/* Output facility. + */ +typedef struct bt_log_output +{ + unsigned mask; /* What to put into log line buffer (see BT_LOG_PUT_XXX) */ + void *arg; /* User provided output callback argument */ + bt_log_output_cb callback; /* Output callback function */ +} +bt_log_output; + +/* Set output callback function. + * + * Mask allows to control what information will be added to the log line buffer + * before callback function is invoked. Default mask value is BT_LOG_PUT_STD. + */ +void bt_log_set_output_v(const unsigned mask, void *const arg, + const bt_log_output_cb callback); +static _BT_LOG_INLINE void bt_log_set_output_p(const bt_log_output *const output) +{ + bt_log_set_output_v(output->mask, output->arg, output->callback); +} + +/* Used with _AUX macros and allows to override global format and output + * facility. Use BT_LOG_GLOBAL_FORMAT and BT_LOG_GLOBAL_OUTPUT for values from + * global configuration. Example: + * + * static const bt_log_output module_output = { + * BT_LOG_PUT_STD, 0, custom_output_callback + * }; + * static const bt_log_spec module_spec = { + * BT_LOG_GLOBAL_FORMAT, &module_output + * }; + * BT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y); + * + * See BT_LOGF_AUX and BT_LOGF_MEM_AUX for details. + */ +typedef struct bt_log_spec +{ + const bt_log_format *format; + const bt_log_output *output; +} +bt_log_spec; + +#ifdef __cplusplus +} +#endif + +/* Execute log statement if condition is true. Example: + * + * BT_LOG_IF(1 < 2, BT_LOGI("Log this")); + * BT_LOG_IF(1 > 2, BT_LOGI("Don't log this")); + * + * Keep in mind though, that if condition can't be evaluated at compile time, + * then it will be evaluated at run time. This will increase exectuable size + * and can have noticeable performance overhead. Try to limit conditions to + * expressions that can be evaluated at compile time. + */ +#define BT_LOG_IF(cond, f) do { _BT_LOG_IF((cond)) { f; } } _BT_LOG_ONCE + +/* Mark log statement as "secret". Log statements that are marked as secrets + * will NOT be executed when censoring is enabled (see BT_LOG_CENSORED). + * Example: + * + * BT_LOG_SECRET(BT_LOGI("Credit card: %s", credit_card)); + * BT_LOG_SECRET(BT_LOGD_MEM(cipher, cipher_sz, "Cipher bytes:")); + */ +#define BT_LOG_SECRET(f) BT_LOG_IF(BT_LOG_SECRETS, f) + +/* Check "current" log level at compile time (ignoring "output" log level). + * Evaluates to true when specified log level is enabled. For example: + * + * #if BT_LOG_ENABLED_DEBUG + * const char *const g_enum_strings[] = { + * "enum_value_0", "enum_value_1", "enum_value_2" + * }; + * #endif + * // ... + * #if BT_LOG_ENABLED_DEBUG + * BT_LOGD("enum value: %s", g_enum_strings[v]); + * #endif + * + * See BT_LOG_LEVEL for details. + */ +#define BT_LOG_ENABLED(lvl) ((lvl) >= _BT_LOG_LEVEL) +#define BT_LOG_ENABLED_VERBOSE BT_LOG_ENABLED(BT_LOG_VERBOSE) +#define BT_LOG_ENABLED_DEBUG BT_LOG_ENABLED(BT_LOG_DEBUG) +#define BT_LOG_ENABLED_INFO BT_LOG_ENABLED(BT_LOG_INFO) +#define BT_LOG_ENABLED_WARN BT_LOG_ENABLED(BT_LOG_WARN) +#define BT_LOG_ENABLED_ERROR BT_LOG_ENABLED(BT_LOG_ERROR) +#define BT_LOG_ENABLED_FATAL BT_LOG_ENABLED(BT_LOG_FATAL) + +/* Check "output" log level at run time (taking into account "current" log + * level as well). Evaluates to true when specified log level is turned on AND + * enabled. For example: + * + * if (BT_LOG_ON_DEBUG) + * { + * char hash[65]; + * sha256(data_ptr, data_sz, hash); + * BT_LOGD("data: len=%u, sha256=%s", data_sz, hash); + * } + * + * See BT_LOG_OUTPUT_LEVEL for details. + */ +#define BT_LOG_ON(lvl) \ + (BT_LOG_ENABLED((lvl)) && (lvl) >= _BT_LOG_OUTPUT_LEVEL) +#define BT_LOG_ON_VERBOSE BT_LOG_ON(BT_LOG_VERBOSE) +#define BT_LOG_ON_DEBUG BT_LOG_ON(BT_LOG_DEBUG) +#define BT_LOG_ON_INFO BT_LOG_ON(BT_LOG_INFO) +#define BT_LOG_ON_WARN BT_LOG_ON(BT_LOG_WARN) +#define BT_LOG_ON_ERROR BT_LOG_ON(BT_LOG_ERROR) +#define BT_LOG_ON_FATAL BT_LOG_ON(BT_LOG_FATAL) + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char *_bt_log_tag_prefix; +extern bt_log_format _bt_log_global_format; +extern bt_log_output _bt_log_global_output; +extern int _bt_log_global_output_lvl; +extern const bt_log_spec _bt_log_stderr_spec; + +BT_HIDDEN +void _bt_log_write_d( + const char *const func, const char *const file, const unsigned line, + const int lvl, const char *const tag, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7); + +BT_HIDDEN +void _bt_log_write_aux_d( + const char *const func, const char *const file, const unsigned line, + const bt_log_spec *const log, const int lvl, const char *const tag, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(7, 8); + +BT_HIDDEN +void _bt_log_write( + const int lvl, const char *const tag, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(3, 4); + +BT_HIDDEN +void _bt_log_write_aux( + const bt_log_spec *const log, const int lvl, const char *const tag, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(4, 5); + +BT_HIDDEN +void _bt_log_write_mem_d( + const char *const func, const char *const file, const unsigned line, + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(8, 9); + +BT_HIDDEN +void _bt_log_write_mem_aux_d( + const char *const func, const char *const file, const unsigned line, + const bt_log_spec *const log, const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(9, 10); + +BT_HIDDEN +void _bt_log_write_mem( + const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(5, 6); + +BT_HIDDEN +void _bt_log_write_mem_aux( + const bt_log_spec *const log, const int lvl, const char *const tag, + const void *const d, const unsigned d_sz, + const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7); + +#ifdef __cplusplus +} +#endif + +/* Message logging macros: + * - BT_LOGV("format string", args, ...) + * - BT_LOGD("format string", args, ...) + * - BT_LOGI("format string", args, ...) + * - BT_LOGW("format string", args, ...) + * - BT_LOGE("format string", args, ...) + * - BT_LOGF("format string", args, ...) + * + * Message and error string (errno) logging macros: + * - BT_LOGV_ERRNO("initial message", "format string", args, ...) + * - BT_LOGD_ERRNO("initial message", "format string", args, ...) + * - BT_LOGI_ERRNO("initial message", "format string", args, ...) + * - BT_LOGW_ERRNO("initial message", "format string", args, ...) + * - BT_LOGE_ERRNO("initial message", "format string", args, ...) + * - BT_LOGF_ERRNO("initial message", "format string", args, ...) + * + * Memory logging macros: + * - BT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...) + * - BT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...) + * - BT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...) + * - BT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...) + * - BT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...) + * - BT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...) + * + * Auxiliary logging macros: + * - BT_LOGV_AUX(&log_instance, "format string", args, ...) + * - BT_LOGD_AUX(&log_instance, "format string", args, ...) + * - BT_LOGI_AUX(&log_instance, "format string", args, ...) + * - BT_LOGW_AUX(&log_instance, "format string", args, ...) + * - BT_LOGE_AUX(&log_instance, "format string", args, ...) + * - BT_LOGF_AUX(&log_instance, "format string", args, ...) + * + * Auxiliary memory logging macros: + * - BT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * - BT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * - BT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * - BT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * - BT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * - BT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...) + * + * Preformatted string logging macros: + * - BT_LOGV_STR("preformatted string"); + * - BT_LOGD_STR("preformatted string"); + * - BT_LOGI_STR("preformatted string"); + * - BT_LOGW_STR("preformatted string"); + * - BT_LOGE_STR("preformatted string"); + * - BT_LOGF_STR("preformatted string"); + * + * Explicit log level and tag macros: + * - BT_LOG_WRITE(level, tag, "format string", args, ...) + * - BT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", args, ...) + * - BT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, ...) + * - BT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz, + * "format string", args, ...) + * + * Format string follows printf() conventions. Both data_ptr and data_sz could + * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments + * match format specifiers in format string. + * + * Library assuming UTF-8 encoding for all strings (char *), including format + * string itself. + */ +#if BT_LOG_SRCLOC_NONE == _BT_LOG_SRCLOC + #define BT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write(lvl, tag, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_aux(log, lvl, tag, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \ + } _BT_LOG_ONCE +#else + #define BT_LOG_WRITE(lvl, tag, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ + lvl, tag, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_mem_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ + lvl, tag, d, d_sz, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ + log, lvl, tag, __VA_ARGS__); \ + } _BT_LOG_ONCE + #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \ + do { \ + if (BT_LOG_ON(lvl)) \ + _bt_log_write_mem_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \ + log, lvl, tag, d, d_sz, __VA_ARGS__); \ + } _BT_LOG_ONCE +#endif + +#define BT_LOG_WRITE_ERRNO(lvl, tag, _msg, _fmt, args...) \ + do { \ + const char *error_str; \ + error_str = g_strerror(errno); \ + BT_LOG_WRITE(lvl, tag, _msg ": %s" _fmt, error_str, ## args); \ + } _BT_LOG_ONCE + +static _BT_LOG_INLINE void _bt_log_unused(const int dummy, ...) {(void)dummy;} + +#define _BT_LOG_UNUSED(...) \ + do { _BT_LOG_NEVER _bt_log_unused(0, __VA_ARGS__); } _BT_LOG_ONCE + +#if BT_LOG_ENABLED_VERBOSE + #define BT_LOGV(...) \ + BT_LOG_WRITE(BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGV_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGV_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGV_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGV_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM(log, BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGV(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGV_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGV_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGV_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if BT_LOG_ENABLED_DEBUG + #define BT_LOGD(...) \ + BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGD_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGD_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGD_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGD_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGD(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGD_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGD_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGD_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if BT_LOG_ENABLED_INFO + #define BT_LOGI(...) \ + BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGI_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGI_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGI_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGI_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGI(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGI_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGI_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGI_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if BT_LOG_ENABLED_WARN + #define BT_LOGW(...) \ + BT_LOG_WRITE(BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGW_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGW_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGW_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGW_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGW(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGW_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGW_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGW_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if BT_LOG_ENABLED_ERROR + #define BT_LOGE(...) \ + BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGE_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGE_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGE_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGE_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGE(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGE_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGE_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGE_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#if BT_LOG_ENABLED_FATAL + #define BT_LOGF(...) \ + BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGF_ERRNO(...) \ + BT_LOG_WRITE_ERRNO(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGF_AUX(log, ...) \ + BT_LOG_WRITE_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__) + #define BT_LOGF_MEM(d, d_sz, ...) \ + BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) + #define BT_LOGF_MEM_AUX(log, d, d_sz, ...) \ + BT_LOG_WRITE_MEM_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__) +#else + #define BT_LOGF(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGF_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGF_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__) + #define BT_LOGF_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__) +#endif + +#define BT_LOGV_STR(s) BT_LOGV("%s", (s)) +#define BT_LOGD_STR(s) BT_LOGD("%s", (s)) +#define BT_LOGI_STR(s) BT_LOGI("%s", (s)) +#define BT_LOGW_STR(s) BT_LOGW("%s", (s)) +#define BT_LOGE_STR(s) BT_LOGE("%s", (s)) +#define BT_LOGF_STR(s) BT_LOGF("%s", (s)) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Output to standard error stream. Library uses it by default, though in few + * cases it could be necessary to specify it explicitly. For example, when + * bt_log library is compiled with BT_LOG_EXTERN_GLOBAL_OUTPUT, application must + * define and initialize global output variable: + * + * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR}; + * + * Another example is when using custom output, stderr could be used as a + * fallback when custom output facility failed to initialize: + * + * bt_log_set_output_v(BT_LOG_OUT_STDERR); + */ +enum { BT_LOG_OUT_STDERR_MASK = BT_LOG_PUT_STD }; + +BT_HIDDEN +void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg); +#define BT_LOG_OUT_STDERR BT_LOG_OUT_STDERR_MASK, 0, bt_log_out_stderr_callback + +/* Predefined spec for stderr. Uses global format options (BT_LOG_GLOBAL_FORMAT) + * and BT_LOG_OUT_STDERR. Could be used to force output to stderr for a + * particular message. Example: + * + * f = fopen("foo.log", "w"); + * if (!f) + * BT_LOGE_AUX(BT_LOG_STDERR, "Failed to open log file"); + */ +#define BT_LOG_STDERR (&_bt_log_stderr_spec) + +static inline +int bt_log_get_level_from_env(const char *var) +{ + const char *varval = getenv(var); + int level = BT_LOG_NONE; + + if (!varval) { + goto end; + } + + if (strcmp(varval, "VERBOSE") == 0 || + strcmp(varval, "V") == 0) { + level = BT_LOG_VERBOSE; + } else if (strcmp(varval, "DEBUG") == 0 || + strcmp(varval, "D") == 0) { + level = BT_LOG_DEBUG; + } else if (strcmp(varval, "INFO") == 0 || + strcmp(varval, "I") == 0) { + level = BT_LOG_INFO; + } else if (strcmp(varval, "WARN") == 0 || + strcmp(varval, "WARNING") == 0 || + strcmp(varval, "W") == 0) { + level = BT_LOG_WARN; + } else if (strcmp(varval, "ERROR") == 0 || + strcmp(varval, "E") == 0) { + level = BT_LOG_ERROR; + } else if (strcmp(varval, "FATAL") == 0 || + strcmp(varval, "F") == 0) { + level = BT_LOG_FATAL; + } else if (strcmp(varval, "NONE") == 0 || + strcmp(varval, "N") == 0) { + level = BT_LOG_NONE; + } else { + /* Should we warn here? How? */ + } + +end: + return level; +} + +#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym) \ + extern int _level_sym + +#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var) \ + BT_HIDDEN int _level_sym = BT_LOG_NONE; \ + static \ + void __attribute__((constructor)) _bt_log_level_ctor(void) \ + { \ + _level_sym = bt_log_get_level_from_env(_env_var); \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* BABELTRACE_LOGGING_INTERNAL_H */ diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am new file mode 100644 index 00000000..9b23e3c4 --- /dev/null +++ b/src/plugins/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = utils text ctf + +if ENABLE_DEBUG_INFO +SUBDIRS += lttng-utils +endif + +noinst_HEADERS = plugins-common.h diff --git a/src/plugins/ctf/Makefile.am b/src/plugins/ctf/Makefile.am new file mode 100644 index 00000000..bf962693 --- /dev/null +++ b/src/plugins/ctf/Makefile.am @@ -0,0 +1,30 @@ +SUBDIRS = common \ + fs-src \ + fs-sink \ + lttng-live + +noinst_HEADERS = print.h + +plugindir = "$(PLUGINSDIR)" +plugin_LTLIBRARIES = babeltrace-plugin-ctf.la + +# ctf plugin +babeltrace_plugin_ctf_la_SOURCES = plugin.c + +babeltrace_plugin_ctf_la_LDFLAGS = \ + $(LT_NO_UNDEFINED) \ + -avoid-version -module + +babeltrace_plugin_ctf_la_LIBADD = \ + common/libbabeltrace2-plugin-ctf-common.la \ + fs-sink/libbabeltrace2-plugin-ctf-fs-sink.la \ + fs-src/libbabeltrace2-plugin-ctf-fs-src.la \ + lttng-live/libbabeltrace2-plugin-ctf-lttng-live.la + +if !ENABLE_BUILT_IN_PLUGINS +babeltrace_plugin_ctf_la_LIBADD += \ + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/ctfser/libbabeltrace2-ctfser.la +endif diff --git a/src/plugins/ctf/common/Makefile.am b/src/plugins/ctf/common/Makefile.am new file mode 100644 index 00000000..055355b1 --- /dev/null +++ b/src/plugins/ctf/common/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = metadata bfcr msg-iter utils + +noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-common.la +libbabeltrace2_plugin_ctf_common_la_SOURCES = print.h +libbabeltrace2_plugin_ctf_common_la_LIBADD = \ + $(builddir)/metadata/libctf-parser.la \ + $(builddir)/metadata/libctf-ast.la \ + $(builddir)/bfcr/libctf-bfcr.la \ + $(builddir)/msg-iter/libctf-msg-iter.la \ + $(builddir)/utils/libctf-utils.la diff --git a/src/plugins/ctf/common/bfcr/Makefile.am b/src/plugins/ctf/common/bfcr/Makefile.am new file mode 100644 index 00000000..d0dd8c6f --- /dev/null +++ b/src/plugins/ctf/common/bfcr/Makefile.am @@ -0,0 +1,6 @@ +noinst_LTLIBRARIES = libctf-bfcr.la +libctf_bfcr_la_SOURCES = \ + bfcr.c \ + bfcr.h \ + logging.c \ + logging.h diff --git a/src/plugins/ctf/common/bfcr/bfcr.c b/src/plugins/ctf/common/bfcr/bfcr.c new file mode 100644 index 00000000..2bdd961d --- /dev/null +++ b/src/plugins/ctf/common/bfcr/bfcr.c @@ -0,0 +1,1344 @@ +/* + * Babeltrace - CTF binary field class reader (BFCR) + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-BFCR" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include +#include "compat/bitfield.h" +#include "common/common.h" +#include +#include "common/align.h" +#include + +#include "bfcr.h" +#include "../metadata/ctf-meta.h" + +#define DIV8(_x) ((_x) >> 3) +#define BYTES_TO_BITS(_x) ((_x) * 8) +#define BITS_TO_BYTES_FLOOR(_x) DIV8(_x) +#define BITS_TO_BYTES_CEIL(_x) DIV8((_x) + 7) +#define IN_BYTE_OFFSET(_at) ((_at) & 7) + +/* A visit stack entry */ +struct stack_entry { + /* + * Current class of base field, one of: + * + * * Structure + * * Array + * * Sequence + * * Variant + */ + struct ctf_field_class *base_class; + + /* Length of base field (always 1 for a variant class) */ + int64_t base_len; + + /* Index of next field to read */ + int64_t index; +}; + +/* Visit stack */ +struct stack { + /* Entries (struct stack_entry) */ + GArray *entries; + + /* Number of active entries */ + size_t size; +}; + +/* Reading states */ +enum bfcr_state { + BFCR_STATE_NEXT_FIELD, + BFCR_STATE_ALIGN_BASIC, + BFCR_STATE_ALIGN_COMPOUND, + BFCR_STATE_READ_BASIC_BEGIN, + BFCR_STATE_READ_BASIC_CONTINUE, + BFCR_STATE_DONE, +}; + +/* Binary class reader */ +struct bt_bfcr { + /* Bisit stack */ + struct stack *stack; + + /* Current basic field class */ + struct ctf_field_class *cur_basic_field_class; + + /* Current state */ + enum bfcr_state state; + + /* + * Last basic field class's byte order. + * + * This is used to detect errors since two contiguous basic + * classes for which the common boundary is not the boundary of + * a byte cannot have different byte orders. + * + * This is set to -1 on reset and when the last basic field class + * was a string class. + */ + enum ctf_byte_order last_bo; + + /* Current byte order (copied to last_bo after a successful read) */ + enum ctf_byte_order cur_bo; + + /* Stitch buffer infos */ + struct { + /* Stitch buffer */ + uint8_t buf[16]; + + /* Offset, within stitch buffer, of first bit */ + size_t offset; + + /* Length (bits) of data in stitch buffer from offset */ + size_t at; + } stitch; + + /* User buffer infos */ + struct { + /* Address */ + const uint8_t *addr; + + /* Offset of data from address (bits) */ + size_t offset; + + /* Current position from offset (bits) */ + size_t at; + + /* Offset of offset within whole packet (bits) */ + size_t packet_offset; + + /* Data size in buffer (bits) */ + size_t sz; + + /* Buffer size (bytes) */ + size_t buf_sz; + } buf; + + /* User stuff */ + struct { + /* Callback functions */ + struct bt_bfcr_cbs cbs; + + /* Private data */ + void *data; + } user; +}; + +static inline +const char *bfcr_state_string(enum bfcr_state state) +{ + switch (state) { + case BFCR_STATE_NEXT_FIELD: + return "BFCR_STATE_NEXT_FIELD"; + case BFCR_STATE_ALIGN_BASIC: + return "BFCR_STATE_ALIGN_BASIC"; + case BFCR_STATE_ALIGN_COMPOUND: + return "BFCR_STATE_ALIGN_COMPOUND"; + case BFCR_STATE_READ_BASIC_BEGIN: + return "BFCR_STATE_READ_BASIC_BEGIN"; + case BFCR_STATE_READ_BASIC_CONTINUE: + return "BFCR_STATE_READ_BASIC_CONTINUE"; + case BFCR_STATE_DONE: + return "BFCR_STATE_DONE"; + default: + return "(unknown)"; + } +} + +static +struct stack *stack_new(void) +{ + struct stack *stack = NULL; + + stack = g_new0(struct stack, 1); + if (!stack) { + BT_LOGE_STR("Failed to allocate one stack."); + goto error; + } + + stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry)); + if (!stack->entries) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + BT_LOGD("Created stack: addr=%p", stack); + return stack; + +error: + g_free(stack); + return NULL; +} + +static +void stack_destroy(struct stack *stack) +{ + if (!stack) { + return; + } + + BT_LOGD("Destroying stack: addr=%p", stack); + + if (stack->entries) { + g_array_free(stack->entries, TRUE); + } + + g_free(stack); +} + +static +int stack_push(struct stack *stack, struct ctf_field_class *base_class, + size_t base_len) +{ + struct stack_entry *entry; + + BT_ASSERT(stack); + BT_ASSERT(base_class); + BT_LOGV("Pushing field class on stack: stack-addr=%p, " + "fc-addr=%p, fc-type=%d, base-length=%zu, " + "stack-size-before=%zu, stack-size-after=%zu", + stack, base_class, base_class->type, + base_len, stack->size, stack->size + 1); + + if (stack->entries->len == stack->size) { + g_array_set_size(stack->entries, stack->size + 1); + } + + entry = &g_array_index(stack->entries, struct stack_entry, stack->size); + entry->base_class = base_class; + entry->base_len = base_len; + entry->index = 0; + stack->size++; + return 0; +} + +static inline +int64_t get_compound_field_class_length(struct bt_bfcr *bfcr, + struct ctf_field_class *fc) +{ + int64_t length; + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + length = (int64_t) struct_fc->members->len; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + /* Variant field classes always "contain" a single class */ + length = 1; + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + { + struct ctf_field_class_array *array_fc = (void *) fc; + + length = (int64_t) array_fc->length; + break; + } + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + length = bfcr->user.cbs.query.get_sequence_length(fc, + bfcr->user.data); + break; + default: + abort(); + } + + return length; +} + +static +int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class) +{ + int ret; + int64_t length = get_compound_field_class_length(bfcr, base_class); + + if (length < 0) { + BT_LOGW("Cannot get compound field class's field count: " + "bfcr-addr=%p, fc-addr=%p, fc-type=%d", + bfcr, base_class, base_class->type); + ret = BT_BFCR_STATUS_ERROR; + goto end; + } + + ret = stack_push(bfcr->stack, base_class, (size_t) length); + +end: + return ret; +} + +static inline +unsigned int stack_size(struct stack *stack) +{ + BT_ASSERT(stack); + return stack->size; +} + +static +void stack_pop(struct stack *stack) +{ + BT_ASSERT(stack); + BT_ASSERT(stack_size(stack)); + BT_LOGV("Popping from stack: " + "stack-addr=%p, stack-size-before=%u, stack-size-after=%u", + stack, stack->entries->len, stack->entries->len - 1); + stack->size--; +} + +static inline +bool stack_empty(struct stack *stack) +{ + return stack_size(stack) == 0; +} + +static +void stack_clear(struct stack *stack) +{ + BT_ASSERT(stack); + stack->size = 0; +} + +static inline +struct stack_entry *stack_top(struct stack *stack) +{ + BT_ASSERT(stack); + BT_ASSERT(stack_size(stack)); + return &g_array_index(stack->entries, struct stack_entry, + stack->size - 1); +} + +static inline +size_t available_bits(struct bt_bfcr *bfcr) +{ + return bfcr->buf.sz - bfcr->buf.at; +} + +static inline +void consume_bits(struct bt_bfcr *bfcr, size_t incr) +{ + BT_LOGV("Advancing cursor: bfcr-addr=%p, cur-before=%zu, cur-after=%zu", + bfcr, bfcr->buf.at, bfcr->buf.at + incr); + bfcr->buf.at += incr; +} + +static inline +bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz) +{ + return available_bits(bfcr) >= sz; +} + +static inline +bool at_least_one_bit_left(struct bt_bfcr *bfcr) +{ + return has_enough_bits(bfcr, 1); +} + +static inline +size_t packet_at(struct bt_bfcr *bfcr) +{ + return bfcr->buf.packet_offset + bfcr->buf.at; +} + +static inline +size_t buf_at_from_addr(struct bt_bfcr *bfcr) +{ + /* + * Considering this: + * + * ====== offset ===== (17) + * + * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + * ^ + * addr (0) ==== at ==== (12) + * + * We want this: + * + * =============================== (29) + */ + return bfcr->buf.offset + bfcr->buf.at; +} + +static +void stitch_reset(struct bt_bfcr *bfcr) +{ + bfcr->stitch.offset = 0; + bfcr->stitch.at = 0; +} + +static inline +size_t stitch_at_from_addr(struct bt_bfcr *bfcr) +{ + return bfcr->stitch.offset + bfcr->stitch.at; +} + +static +void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz) +{ + size_t stitch_byte_at; + size_t buf_byte_at; + size_t nb_bytes; + + if (sz == 0) { + return; + } + + stitch_byte_at = + BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr)); + buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr)); + nb_bytes = BITS_TO_BYTES_CEIL(sz); + BT_ASSERT(nb_bytes > 0); + BT_ASSERT(bfcr->buf.addr); + memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at], + nb_bytes); + bfcr->stitch.at += sz; + consume_bits(bfcr, sz); +} + +static +void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr) +{ + stitch_append_from_buf(bfcr, available_bits(bfcr)); +} + +static +void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr) +{ + stitch_reset(bfcr); + bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr)); + stitch_append_from_remaining_buf(bfcr); +} + +static inline +void read_unsigned_bitfield(const uint8_t *buf, size_t at, + unsigned int field_size, enum ctf_byte_order bo, + uint64_t *v) +{ + switch (bo) { + case CTF_BYTE_ORDER_BIG: + bt_bitfield_read_be(buf, uint8_t, at, field_size, v); + break; + case CTF_BYTE_ORDER_LITTLE: + bt_bitfield_read_le(buf, uint8_t, at, field_size, v); + break; + default: + abort(); + } + + BT_LOGV("Read unsigned bit array: cur=%zu, size=%u, " + "bo=%d, val=%" PRIu64, at, field_size, bo, *v); +} + +static inline +void read_signed_bitfield(const uint8_t *buf, size_t at, + unsigned int field_size, enum ctf_byte_order bo, int64_t *v) +{ + switch (bo) { + case CTF_BYTE_ORDER_BIG: + bt_bitfield_read_be(buf, uint8_t, at, field_size, v); + break; + case CTF_BYTE_ORDER_LITTLE: + bt_bitfield_read_le(buf, uint8_t, at, field_size, v); + break; + default: + abort(); + } + + BT_LOGV("Read signed bit array: cur=%zu, size=%u, " + "bo=%d, val=%" PRId64, at, field_size, bo, *v); +} + +typedef enum bt_bfcr_status (* read_basic_and_call_cb_t)(struct bt_bfcr *, + const uint8_t *, size_t); + +static inline +enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr, + enum ctf_byte_order next_bo) +{ + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + + /* Always valid when at a byte boundary */ + if (packet_at(bfcr) % 8 == 0) { + goto end; + } + + /* Always valid if last byte order is unknown */ + if (bfcr->last_bo == -1) { + goto end; + } + + /* Always valid if next byte order is unknown */ + if (next_bo == -1) { + goto end; + } + + /* Make sure last byte order is compatible with the next byte order */ + switch (bfcr->last_bo) { + case CTF_BYTE_ORDER_BIG: + if (next_bo != CTF_BYTE_ORDER_BIG) { + status = BT_BFCR_STATUS_ERROR; + } + break; + case CTF_BYTE_ORDER_LITTLE: + if (next_bo != CTF_BYTE_ORDER_LITTLE) { + status = BT_BFCR_STATUS_ERROR; + } + break; + default: + status = BT_BFCR_STATUS_ERROR; + } + +end: + if (status < 0) { + BT_LOGW("Cannot read bit array: two different byte orders not at a byte boundary: " + "bfcr-addr=%p, last-bo=%d, next-bo=%d", + bfcr, bfcr->last_bo, next_bo); + } + + return status; +} + +static +enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr, + const uint8_t *buf, size_t at) +{ + double dblval; + unsigned int field_size; + enum ctf_byte_order bo; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + struct ctf_field_class_float *fc = (void *) bfcr->cur_basic_field_class; + + BT_ASSERT(fc); + field_size = fc->base.size; + bo = fc->base.byte_order; + bfcr->cur_bo = bo; + + switch (field_size) { + case 32: + { + uint64_t v; + union { + uint32_t u; + float f; + } f32; + + read_unsigned_bitfield(buf, at, field_size, bo, &v); + f32.u = (uint32_t) v; + dblval = (double) f32.f; + break; + } + case 64: + { + union { + uint64_t u; + double d; + } f64; + + read_unsigned_bitfield(buf, at, field_size, bo, &f64.u); + dblval = f64.d; + break; + } + default: + /* Only 32-bit and 64-bit fields are supported currently */ + abort(); + } + + BT_LOGV("Read floating point number value: bfcr=%p, cur=%zu, val=%f", + bfcr, at, dblval); + + if (bfcr->user.cbs.classes.floating_point) { + BT_LOGV("Calling user function (floating point number)."); + status = bfcr->user.cbs.classes.floating_point(dblval, + bfcr->cur_basic_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + } + } + + return status; +} + +static inline +enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr, + const uint8_t *buf, size_t at) +{ + unsigned int field_size; + enum ctf_byte_order bo; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + struct ctf_field_class_int *fc = (void *) bfcr->cur_basic_field_class; + + field_size = fc->base.size; + bo = fc->base.byte_order; + + /* + * Update current byte order now because we could be reading + * the integer value of an enumeration class, and thus we know + * here the actual supporting integer class's byte order. + */ + bfcr->cur_bo = bo; + + if (fc->is_signed) { + int64_t v; + + read_signed_bitfield(buf, at, field_size, bo, &v); + + if (bfcr->user.cbs.classes.signed_int) { + BT_LOGV("Calling user function (signed integer)."); + status = bfcr->user.cbs.classes.signed_int(v, + bfcr->cur_basic_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: " + "bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + } + } + } else { + uint64_t v; + + read_unsigned_bitfield(buf, at, field_size, bo, &v); + + if (bfcr->user.cbs.classes.unsigned_int) { + BT_LOGV("Calling user function (unsigned integer)."); + status = bfcr->user.cbs.classes.unsigned_int(v, + bfcr->cur_basic_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: " + "bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + } + } + } + + return status; +} + +static inline +enum bt_bfcr_status read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr, + read_basic_and_call_cb_t read_basic_and_call_cb) +{ + size_t available; + size_t needed_bits; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + struct ctf_field_class_bit_array *fc = + (void *) bfcr->cur_basic_field_class; + + if (!at_least_one_bit_left(bfcr)) { + BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); + status = BT_BFCR_STATUS_EOF; + goto end; + } + + available = available_bits(bfcr); + needed_bits = fc->size - bfcr->stitch.at; + BT_LOGV("Continuing basic field decoding: " + "bfcr-addr=%p, field-size=%u, needed-size=%zu, " + "available-size=%zu", + bfcr, fc->size, needed_bits, available); + if (needed_bits <= available) { + /* We have all the bits; append to stitch, then decode */ + stitch_append_from_buf(bfcr, needed_bits); + status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf, + bfcr->stitch.offset); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("Cannot read basic field: " + "bfcr-addr=%p, fc-addr=%p, status=%s", + bfcr, bfcr->cur_basic_field_class, + bt_bfcr_status_string(status)); + goto end; + } + + if (stack_empty(bfcr->stack)) { + /* Root is a basic class */ + bfcr->state = BFCR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(bfcr->stack)->index++; + bfcr->state = BFCR_STATE_NEXT_FIELD; + bfcr->last_bo = bfcr->cur_bo; + } + goto end; + } + + /* We are here; it means we don't have enough data to decode this */ + BT_LOGV_STR("Not enough data to read the next basic field: appending to stitch buffer."); + stitch_append_from_remaining_buf(bfcr); + status = BT_BFCR_STATUS_EOF; + +end: + return status; +} + +static inline +enum bt_bfcr_status read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr, + read_basic_and_call_cb_t read_basic_and_call_cb) +{ + size_t available; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + struct ctf_field_class_bit_array *fc = + (void *) bfcr->cur_basic_field_class; + + if (!at_least_one_bit_left(bfcr)) { + BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); + status = BT_BFCR_STATUS_EOF; + goto end; + } + + status = validate_contiguous_bo(bfcr, fc->byte_order); + if (status != BT_BFCR_STATUS_OK) { + /* validate_contiguous_bo() logs errors */ + goto end; + } + + available = available_bits(bfcr); + + if (fc->size <= available) { + /* We have all the bits; decode and set now */ + BT_ASSERT(bfcr->buf.addr); + status = read_basic_and_call_cb(bfcr, bfcr->buf.addr, + buf_at_from_addr(bfcr)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("Cannot read basic field: " + "bfcr-addr=%p, fc-addr=%p, status=%s", + bfcr, bfcr->cur_basic_field_class, + bt_bfcr_status_string(status)); + goto end; + } + + consume_bits(bfcr, fc->size); + + if (stack_empty(bfcr->stack)) { + /* Root is a basic class */ + bfcr->state = BFCR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(bfcr->stack)->index++; + bfcr->state = BFCR_STATE_NEXT_FIELD; + bfcr->last_bo = bfcr->cur_bo; + } + + goto end; + } + + /* We are here; it means we don't have enough data to decode this */ + BT_LOGV_STR("Not enough data to read the next basic field: setting stitch buffer."); + stitch_set_from_remaining_buf(bfcr); + bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE; + status = BT_BFCR_STATUS_EOF; + +end: + return status; +} + +static inline +enum bt_bfcr_status read_basic_int_class_and_call_begin( + struct bt_bfcr *bfcr) +{ + return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb); +} + +static inline +enum bt_bfcr_status read_basic_int_class_and_call_continue( + struct bt_bfcr *bfcr) +{ + return read_bit_array_class_and_call_continue(bfcr, + read_basic_int_and_call_cb); +} + +static inline +enum bt_bfcr_status read_basic_float_class_and_call_begin( + struct bt_bfcr *bfcr) +{ + return read_bit_array_class_and_call_begin(bfcr, + read_basic_float_and_call_cb); +} + +static inline +enum bt_bfcr_status read_basic_float_class_and_call_continue( + struct bt_bfcr *bfcr) +{ + return read_bit_array_class_and_call_continue(bfcr, + read_basic_float_and_call_cb); +} + +static inline +enum bt_bfcr_status read_basic_string_class_and_call( + struct bt_bfcr *bfcr, bool begin) +{ + size_t buf_at_bytes; + const uint8_t *result; + size_t available_bytes; + const uint8_t *first_chr; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + + if (!at_least_one_bit_left(bfcr)) { + BT_LOGV("Reached end of data: bfcr-addr=%p", bfcr); + status = BT_BFCR_STATUS_EOF; + goto end; + } + + BT_ASSERT(buf_at_from_addr(bfcr) % 8 == 0); + available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr)); + buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr)); + BT_ASSERT(bfcr->buf.addr); + first_chr = &bfcr->buf.addr[buf_at_bytes]; + result = memchr(first_chr, '\0', available_bytes); + + if (begin && bfcr->user.cbs.classes.string_begin) { + BT_LOGV("Calling user function (string, beginning)."); + status = bfcr->user.cbs.classes.string_begin( + bfcr->cur_basic_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + if (!result) { + /* No null character yet */ + if (bfcr->user.cbs.classes.string) { + BT_LOGV("Calling user function (substring)."); + status = bfcr->user.cbs.classes.string( + (const char *) first_chr, + available_bytes, bfcr->cur_basic_field_class, + bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: " + "bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + consume_bits(bfcr, BYTES_TO_BITS(available_bytes)); + bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE; + status = BT_BFCR_STATUS_EOF; + } else { + /* Found the null character */ + size_t result_len = (size_t) (result - first_chr); + + if (bfcr->user.cbs.classes.string && result_len) { + BT_LOGV("Calling user function (substring)."); + status = bfcr->user.cbs.classes.string( + (const char *) first_chr, + result_len, bfcr->cur_basic_field_class, + bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: " + "bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + if (bfcr->user.cbs.classes.string_end) { + BT_LOGV("Calling user function (string, end)."); + status = bfcr->user.cbs.classes.string_end( + bfcr->cur_basic_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: " + "bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + consume_bits(bfcr, BYTES_TO_BITS(result_len + 1)); + + if (stack_empty(bfcr->stack)) { + /* Root is a basic class */ + bfcr->state = BFCR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(bfcr->stack)->index++; + bfcr->state = BFCR_STATE_NEXT_FIELD; + bfcr->last_bo = bfcr->cur_bo; + } + } + +end: + return status; +} + +static inline +enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr) +{ + enum bt_bfcr_status status; + + BT_ASSERT(bfcr->cur_basic_field_class); + + switch (bfcr->cur_basic_field_class->type) { + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + status = read_basic_int_class_and_call_begin(bfcr); + break; + case CTF_FIELD_CLASS_TYPE_FLOAT: + status = read_basic_float_class_and_call_begin(bfcr); + break; + case CTF_FIELD_CLASS_TYPE_STRING: + status = read_basic_string_class_and_call(bfcr, true); + break; + default: + abort(); + } + + return status; +} + +static inline +enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr) +{ + enum bt_bfcr_status status; + + BT_ASSERT(bfcr->cur_basic_field_class); + + switch (bfcr->cur_basic_field_class->type) { + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + status = read_basic_int_class_and_call_continue(bfcr); + break; + case CTF_FIELD_CLASS_TYPE_FLOAT: + status = read_basic_float_class_and_call_continue(bfcr); + break; + case CTF_FIELD_CLASS_TYPE_STRING: + status = read_basic_string_class_and_call(bfcr, false); + break; + default: + abort(); + } + + return status; +} + +static inline +size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align) +{ + size_t aligned_packet_at; + + aligned_packet_at = ALIGN(packet_at(bfcr), align); + return aligned_packet_at - packet_at(bfcr); +} + +static inline +enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr, + struct ctf_field_class *field_class, enum bfcr_state next_state) +{ + unsigned int field_alignment; + size_t skip_bits; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + + /* Get field's alignment */ + field_alignment = field_class->alignment; + + /* + * 0 means "undefined" for variants; what we really want is 1 + * (always aligned) + */ + BT_ASSERT(field_alignment >= 1); + + /* Compute how many bits we need to skip */ + skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment); + + /* Nothing to skip? aligned */ + if (skip_bits == 0) { + bfcr->state = next_state; + goto end; + } + + /* Make sure there's at least one bit left */ + if (!at_least_one_bit_left(bfcr)) { + status = BT_BFCR_STATUS_EOF; + goto end; + } + + /* Consume as many bits as possible in what's left */ + consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits)); + + /* Are we done now? */ + skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment); + if (skip_bits == 0) { + /* Yes: go to next state */ + bfcr->state = next_state; + goto end; + } else { + /* No: need more data */ + BT_LOGV("Reached end of data when aligning: bfcr-addr=%p", bfcr); + status = BT_BFCR_STATUS_EOF; + } + +end: + return status; +} + +static inline +enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr) +{ + int ret; + struct stack_entry *top; + struct ctf_field_class *next_field_class = NULL; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + + if (stack_empty(bfcr->stack)) { + goto end; + } + + top = stack_top(bfcr->stack); + + /* Are we done with this base class? */ + while (top->index == top->base_len) { + if (bfcr->user.cbs.classes.compound_end) { + BT_LOGV("Calling user function (compound, end)."); + status = bfcr->user.cbs.classes.compound_end( + top->base_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + stack_pop(bfcr->stack); + + /* Are we done with the root class? */ + if (stack_empty(bfcr->stack)) { + bfcr->state = BFCR_STATE_DONE; + goto end; + } + + top = stack_top(bfcr->stack); + top->index++; + } + + /* Get next field's class */ + switch (top->base_class->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + next_field_class = ctf_field_class_struct_borrow_member_by_index( + (void *) top->base_class, (uint64_t) top->index)->fc; + break; + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = + (void *) top->base_class; + + next_field_class = array_fc->elem_fc; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + /* Variant classes are dynamic: the user should know! */ + next_field_class = + bfcr->user.cbs.query.borrow_variant_selected_field_class( + top->base_class, bfcr->user.data); + break; + default: + break; + } + + if (!next_field_class) { + BT_LOGW("Cannot get the field class of the next field: " + "bfcr-addr=%p, base-fc-addr=%p, base-fc-type=%d, " + "index=%" PRId64, + bfcr, top->base_class, top->base_class->type, + top->index); + status = BT_BFCR_STATUS_ERROR; + goto end; + } + + if (next_field_class->is_compound) { + if (bfcr->user.cbs.classes.compound_begin) { + BT_LOGV("Calling user function (compound, begin)."); + status = bfcr->user.cbs.classes.compound_begin( + next_field_class, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(status)); + if (status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + goto end; + } + } + + ret = stack_push_with_len(bfcr, next_field_class); + if (ret) { + /* stack_push_with_len() logs errors */ + status = BT_BFCR_STATUS_ERROR; + goto end; + } + + /* Next state: align a compound class */ + bfcr->state = BFCR_STATE_ALIGN_COMPOUND; + } else { + /* Replace current basic field class */ + BT_LOGV("Replacing current basic field class: " + "bfcr-addr=%p, cur-basic-fc-addr=%p, " + "next-basic-fc-addr=%p", + bfcr, bfcr->cur_basic_field_class, next_field_class); + bfcr->cur_basic_field_class = next_field_class; + + /* Next state: align a basic class */ + bfcr->state = BFCR_STATE_ALIGN_BASIC; + } + +end: + return status; +} + +static inline +enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr) +{ + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + + BT_LOGV("Handling state: bfcr-addr=%p, state=%s", + bfcr, bfcr_state_string(bfcr->state)); + + switch (bfcr->state) { + case BFCR_STATE_NEXT_FIELD: + status = next_field_state(bfcr); + break; + case BFCR_STATE_ALIGN_BASIC: + status = align_class_state(bfcr, bfcr->cur_basic_field_class, + BFCR_STATE_READ_BASIC_BEGIN); + break; + case BFCR_STATE_ALIGN_COMPOUND: + status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class, + BFCR_STATE_NEXT_FIELD); + break; + case BFCR_STATE_READ_BASIC_BEGIN: + status = read_basic_begin_state(bfcr); + break; + case BFCR_STATE_READ_BASIC_CONTINUE: + status = read_basic_continue_state(bfcr); + break; + case BFCR_STATE_DONE: + break; + } + + BT_LOGV("Handled state: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(status)); + return status; +} + +BT_HIDDEN +struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data) +{ + struct bt_bfcr *bfcr; + + BT_LOGD_STR("Creating binary class reader (BFCR)."); + bfcr = g_new0(struct bt_bfcr, 1); + if (!bfcr) { + BT_LOGE_STR("Failed to allocate one binary class reader."); + goto end; + } + + bfcr->stack = stack_new(); + if (!bfcr->stack) { + BT_LOGE_STR("Cannot create BFCR's stack."); + bt_bfcr_destroy(bfcr); + bfcr = NULL; + goto end; + } + + bfcr->state = BFCR_STATE_NEXT_FIELD; + bfcr->user.cbs = cbs; + bfcr->user.data = data; + BT_LOGD("Created BFCR: addr=%p", bfcr); + +end: + return bfcr; +} + +BT_HIDDEN +void bt_bfcr_destroy(struct bt_bfcr *bfcr) +{ + if (bfcr->stack) { + stack_destroy(bfcr->stack); + } + + BT_LOGD("Destroying BFCR: addr=%p", bfcr); + g_free(bfcr); +} + +static +void reset(struct bt_bfcr *bfcr) +{ + BT_LOGD("Resetting BFCR: addr=%p", bfcr); + stack_clear(bfcr->stack); + stitch_reset(bfcr); + bfcr->buf.addr = NULL; + bfcr->last_bo = -1; +} + +static +void update_packet_offset(struct bt_bfcr *bfcr) +{ + BT_LOGV("Updating packet offset for next call: " + "bfcr-addr=%p, cur-packet-offset=%zu, next-packet-offset=%zu", + bfcr, bfcr->buf.packet_offset, + bfcr->buf.packet_offset + bfcr->buf.at); + bfcr->buf.packet_offset += bfcr->buf.at; +} + +BT_HIDDEN +size_t bt_bfcr_start(struct bt_bfcr *bfcr, + struct ctf_field_class *cls, const uint8_t *buf, + size_t offset, size_t packet_offset, size_t sz, + enum bt_bfcr_status *status) +{ + BT_ASSERT(bfcr); + BT_ASSERT(BYTES_TO_BITS(sz) >= offset); + reset(bfcr); + bfcr->buf.addr = buf; + bfcr->buf.offset = offset; + bfcr->buf.at = 0; + bfcr->buf.packet_offset = packet_offset; + bfcr->buf.buf_sz = sz; + bfcr->buf.sz = BYTES_TO_BITS(sz) - offset; + *status = BT_BFCR_STATUS_OK; + + BT_LOGV("Starting decoding: bfcr-addr=%p, fc-addr=%p, " + "buf-addr=%p, buf-size=%zu, offset=%zu, " + "packet-offset=%zu", + bfcr, cls, buf, sz, offset, packet_offset); + + /* Set root class */ + if (cls->is_compound) { + /* Compound class: push on visit stack */ + int stack_ret; + + if (bfcr->user.cbs.classes.compound_begin) { + BT_LOGV("Calling user function (compound, begin)."); + *status = bfcr->user.cbs.classes.compound_begin( + cls, bfcr->user.data); + BT_LOGV("User function returned: status=%s", + bt_bfcr_status_string(*status)); + if (*status != BT_BFCR_STATUS_OK) { + BT_LOGW("User function failed: bfcr-addr=%p, status=%s", + bfcr, bt_bfcr_status_string(*status)); + goto end; + } + } + + stack_ret = stack_push_with_len(bfcr, cls); + if (stack_ret) { + /* stack_push_with_len() logs errors */ + *status = BT_BFCR_STATUS_ERROR; + goto end; + } + + bfcr->state = BFCR_STATE_ALIGN_COMPOUND; + } else { + /* Basic class: set as current basic class */ + bfcr->cur_basic_field_class = cls; + bfcr->state = BFCR_STATE_ALIGN_BASIC; + } + + /* Run the machine! */ + BT_LOGV_STR("Running the state machine."); + + while (true) { + *status = handle_state(bfcr); + if (*status != BT_BFCR_STATUS_OK || + bfcr->state == BFCR_STATE_DONE) { + break; + } + } + + /* Update packet offset for next time */ + update_packet_offset(bfcr); + +end: + return bfcr->buf.at; +} + +BT_HIDDEN +size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz, + enum bt_bfcr_status *status) +{ + BT_ASSERT(bfcr); + BT_ASSERT(buf); + BT_ASSERT(sz > 0); + bfcr->buf.addr = buf; + bfcr->buf.offset = 0; + bfcr->buf.at = 0; + bfcr->buf.buf_sz = sz; + bfcr->buf.sz = BYTES_TO_BITS(sz); + *status = BT_BFCR_STATUS_OK; + + BT_LOGV("Continuing decoding: bfcr-addr=%p, buf-addr=%p, buf-size=%zu", + bfcr, buf, sz); + + /* Continue running the machine */ + BT_LOGV_STR("Running the state machine."); + + while (true) { + *status = handle_state(bfcr); + if (*status != BT_BFCR_STATUS_OK || + bfcr->state == BFCR_STATE_DONE) { + break; + } + } + + /* Update packet offset for next time */ + update_packet_offset(bfcr); + return bfcr->buf.at; +} + +BT_HIDDEN +void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, + bt_bfcr_unsigned_int_cb_func cb) +{ + BT_ASSERT(bfcr); + BT_ASSERT(cb); + bfcr->user.cbs.classes.unsigned_int = cb; +} diff --git a/src/plugins/ctf/common/bfcr/bfcr.h b/src/plugins/ctf/common/bfcr/bfcr.h new file mode 100644 index 00000000..9cc5aadc --- /dev/null +++ b/src/plugins/ctf/common/bfcr/bfcr.h @@ -0,0 +1,378 @@ +#ifndef CTF_BFCR_H +#define CTF_BFCR_H + +/* + * Babeltrace - CTF binary field class reader (BFCR) + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/babeltrace.h" + +#include "../metadata/ctf-meta.h" + +/** + * @file bfcr.h + * + * Event-driven CTF binary field class reader (BFCR). + * + * This is a common, internal API used by CTF source plugins. It allows + * a binary CTF IR field class to be decoded from user-provided buffers. + * As the class is decoded (and, possibly, its nested classes), + * registered user callback functions are called. + * + * This API is only concerned with reading one CTF class at a time from + * one or more buffer of bytes. It does not know CTF dynamic scopes, + * events, or streams. Sequence lengths and selected variant classes are + * requested to the user when needed. + */ + +/** + * Binary class reader API status codes. + */ +enum bt_bfcr_status { + /** Out of memory. */ + BT_BFCR_STATUS_ENOMEM = -5, + /** + * The binary stream reader reached the end of the user-provided + * buffer, but data is still needed to finish decoding the + * requested class. + * + * The user needs to call bt_bfcr_continue() as long as + * #BT_BFCR_STATUS_EOF is returned to complete the decoding + * process of a given class. + */ + BT_BFCR_STATUS_EOF = 1, + + /** Invalid argument. */ + BT_BFCR_STATUS_INVAL = -3, + + /** General error. */ + BT_BFCR_STATUS_ERROR = -1, + + /** Everything okay. */ + BT_BFCR_STATUS_OK = 0, +}; + +/** Field class reader. */ +struct bt_bfcr; + +typedef enum bt_bfcr_status (* bt_bfcr_unsigned_int_cb_func)(uint64_t, + struct ctf_field_class *, void *); + +/* + * Field class reader user callback functions. + */ +struct bt_bfcr_cbs { + /** + * Field class callback functions. + * + * This CTF binary class reader is event-driven. The following + * functions are called during the decoding process, either when + * a compound class begins/ends, or when a basic class is + * completely decoded (along with its value). + * + * Each function also receives the CTF field class associated + * with the call, and user data (registered to the class reader + * calling them). + * + * Actual trace IR fields are \em not created here; this would + * be the responsibility of a class reader's user (the provider + * of those callback functions). + * + * All the class callback functions return one of the following + * values: + * + * - #BT_BFCR_STATUS_OK: Everything is okay; + * continue the decoding process. + * - #BT_BFCR_STATUS_ERROR: General error (reported + * to class reader's user). + * + * Any member of this structure may be set to \c NULL, should + * a specific message be not needed. + */ + struct { + /** + * Called when a signed integer class is completely + * decoded. This could also be the supporting signed + * integer class of an enumeration class (\p class will + * indicate this). + * + * @param value Signed integer value + * @param class Integer or enumeration class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* signed_int)(int64_t value, + struct ctf_field_class *cls, void *data); + + /** + * Called when an unsigned integer class is completely + * decoded. This could also be the supporting signed + * integer class of an enumeration class (\p class will + * indicate this). + * + * @param value Unsigned integer value + * @param class Integer or enumeration class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + bt_bfcr_unsigned_int_cb_func unsigned_int; + + /** + * Called when a floating point number class is + * completely decoded. + * + * @param value Floating point number value + * @param class Floating point number class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* floating_point)(double value, + struct ctf_field_class *cls, void *data); + + /** + * Called when a string class begins. + * + * All the following user callback function calls will + * be made to bt_bfcr_cbs::classes::string(), each of + * them providing one substring of the complete string + * class's value. + * + * @param class Beginning string class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* string_begin)( + struct ctf_field_class *cls, void *data); + + /** + * Called when a string class's substring is decoded + * (between a call to bt_bfcr_cbs::classes::string_begin() + * and a call to bt_bfcr_cbs::classes::string_end()). + * + * @param value String value (\em not null-terminated) + * @param len String value length + * @param class String class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* string)(const char *value, + size_t len, struct ctf_field_class *cls, + void *data); + + /** + * Called when a string class ends. + * + * @param class Ending string class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* string_end)( + struct ctf_field_class *cls, void *data); + + /** + * Called when a compound class begins. + * + * All the following class callback function calls will + * signal sequential elements of this compound class, + * until the next corresponding + * bt_bfcr_cbs::classes::compound_end() is called. + * + * If \p class is a variant class, then only one class + * callback function call will follow before the call to + * bt_bfcr_cbs::classes::compound_end(). This single + * call indicates the selected class of this variant + * class. + * + * @param class Beginning compound class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* compound_begin)( + struct ctf_field_class *cls, void *data); + + /** + * Called when a compound class ends. + * + * @param class Ending compound class + * @param data User data + * @returns #BT_BFCR_STATUS_OK or + * #BT_BFCR_STATUS_ERROR + */ + enum bt_bfcr_status (* compound_end)( + struct ctf_field_class *cls, void *data); + } classes; + + /** + * Query callback functions are used when the class reader needs + * dynamic information, i.e. a sequence class's current length + * or a variant class's current selected class. + * + * Both functions need to be set unless it is known that no + * sequences or variants will have to be decoded. + */ + struct { + /** + * Called to query the current length of a given sequence + * class. + * + * @param class Sequence class + * @param data User data + * @returns Sequence length or + * #BT_BFCR_STATUS_ERROR on error + */ + int64_t (* get_sequence_length)(struct ctf_field_class *cls, + void *data); + + /** + * Called to query the current selected class of a given + * variant class. + * + * @param class Variant class + * @param data User data + * @returns Current selected class (owned by + * this) or \c NULL on error + */ + struct ctf_field_class * (* borrow_variant_selected_field_class)( + struct ctf_field_class *cls, void *data); + } query; +}; + +/** + * Creates a CTF binary class reader. + * + * @param cbs User callback functions + * @param data User data (passed to user callback functions) + * @returns New binary class reader on success, or \c NULL on error + */ +BT_HIDDEN +struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data); + +/** + * Destroys a CTF binary class reader, freeing all internal resources. + * + * @param bfcr Binary class reader + */ +BT_HIDDEN +void bt_bfcr_destroy(struct bt_bfcr *bfcr); + +/** + * Decodes a given CTF class from a buffer of bytes. + * + * The number of \em bits consumed by this function is returned. + * + * The \p status output parameter is where a status is written, amongst + * the following: + * + * - #BT_BFCR_STATUS_OK: Decoding is done. + * - #BT_BFCR_STATUS_EOF: The end of the buffer was reached, + * but more data is needed to finish the decoding process of the + * requested class. The user needs to call bt_bfcr_continue() + * as long as #BT_BFCR_STATUS_EOF is returned to complete the + * decoding process of the original class. + * - #BT_BFCR_STATUS_INVAL: Invalid argument. + * - #BT_BFCR_STATUS_ERROR: General error. + * + * Calling this function resets the class reader's internal state. If + * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to + * be called next, \em not bt_bfcr_decode(). + * + * @param bfcr Binary class reader + * @param class Field class to decode + * @param buf Buffer + * @param offset Offset of first bit from \p buf (bits) + * @param packet_offset Offset of \p offset within the CTF + * binary packet containing \p class (bits) + * @param sz Size of buffer in bytes (from \p buf) + * @param status Returned status (see description above) + * @returns Number of consumed bits + */ +BT_HIDDEN +size_t bt_bfcr_start(struct bt_bfcr *bfcr, + struct ctf_field_class *cls, const uint8_t *buf, + size_t offset, size_t packet_offset, size_t sz, + enum bt_bfcr_status *status); + +/** + * Continues the decoding process a given CTF class. + * + * The number of bits consumed by this function is returned. + * + * The \p status output parameter is where a status is placed, amongst + * the following: + * + * - #BT_BFCR_STATUS_OK: decoding is done. + * - #BT_BFCR_STATUS_EOF: the end of the buffer was reached, + * but more data is needed to finish the decoding process of the + * requested class. The user needs to call bt_bfcr_continue() + * as long as #BT_BFCR_STATUS_EOF is returned to complete the + * decoding process of the original class. + * - #BT_BFCR_STATUS_INVAL: invalid argument. + * - #BT_BFCR_STATUS_ERROR: general error. + * + * @param bfcr Binary class reader + * @param buf Buffer + * @param sz Size of buffer in bytes (from \p offset) + * @param status Returned status (see description above) + * @returns Number of consumed bits + */ +BT_HIDDEN +size_t bt_bfcr_continue(struct bt_bfcr *bfcr, + const uint8_t *buf, size_t sz, + enum bt_bfcr_status *status); + +BT_HIDDEN +void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr, + bt_bfcr_unsigned_int_cb_func cb); + +static inline +const char *bt_bfcr_status_string(enum bt_bfcr_status status) +{ + switch (status) { + case BT_BFCR_STATUS_ENOMEM: + return "BT_BFCR_STATUS_ENOMEM"; + case BT_BFCR_STATUS_EOF: + return "BT_BFCR_STATUS_EOF"; + case BT_BFCR_STATUS_INVAL: + return "BT_BFCR_STATUS_INVAL"; + case BT_BFCR_STATUS_ERROR: + return "BT_BFCR_STATUS_ERROR"; + case BT_BFCR_STATUS_OK: + return "BT_BFCR_STATUS_OK"; + default: + return "(unknown)"; + } +} + +#endif /* CTF_BFCR_H */ diff --git a/src/plugins/ctf/common/bfcr/btr.gdb b/src/plugins/ctf/common/bfcr/btr.gdb new file mode 100644 index 00000000..71cc0f6e --- /dev/null +++ b/src/plugins/ctf/common/bfcr/btr.gdb @@ -0,0 +1,24 @@ +define ctf-btr-show-stack + if (stack_empty($arg0)) + printf "stack is empty!\n" + else + set $stack_size = stack_size($arg0) + set $stack_at = (int) ($stack_size - 1) + printf "%3s %10s %4s %3s\n", "pos", "base addr", "blen", "idx" + + while ($stack_at >= 0) + set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at) + + if ($stack_at == $stack_size - 1) + printf "%3d %10p %3d %3d <-- top\n", $stack_at, \ + $stack_entry->base_class, $stack_entry->base_len, \ + $stack_entry->index + else + printf "%3d %10p %3d %3d\n", $stack_at, \ + $stack_entry->base_class, $stack_entry->base_len, \ + $stack_entry->index + end + set $stack_at = $stack_at - 1 + end + end +end diff --git a/src/plugins/ctf/common/bfcr/logging.c b/src/plugins/ctf/common/bfcr/logging.c new file mode 100644 index 00000000..77ac65f2 --- /dev/null +++ b/src/plugins/ctf/common/bfcr/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bfcr_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bfcr_log_level, "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL"); diff --git a/src/plugins/ctf/common/bfcr/logging.h b/src/plugins/ctf/common/bfcr/logging.h new file mode 100644 index 00000000..cb284685 --- /dev/null +++ b/src/plugins/ctf/common/bfcr/logging.h @@ -0,0 +1,31 @@ +#ifndef CTF_BFCR_LOGGING_H +#define CTF_BFCR_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bfcr_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bfcr_log_level); + +#endif /* CTF_BFCR_LOGGING_H */ diff --git a/src/plugins/ctf/common/metadata/Makefile.am b/src/plugins/ctf/common/metadata/Makefile.am new file mode 100644 index 00000000..fa5b6f8e --- /dev/null +++ b/src/plugins/ctf/common/metadata/Makefile.am @@ -0,0 +1,80 @@ +AM_CPPFLAGS += -I$(builddir) -I$(srcdir) +AM_YFLAGS = -t -d -v + +noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la + +BUILT_SOURCES = parser.h + +libctf_parser_la_SOURCES = lexer.l parser.y objstack.c +# scanner-symbols.h is included to prefix generated yy_* symbols +# with bt_. +libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -include $(srcdir)/scanner-symbols.h +libctf_parser_la_CFLAGS = $(AM_CFLAGS) -Wno-unused-function + +libctf_ast_la_SOURCES = \ + visitor-generate-ir.c \ + visitor-semantic-validator.c \ + visitor-parent-links.c \ + ast.h \ + objstack.h \ + parser.h \ + scanner.h \ + scanner-symbols.h \ + decoder.c \ + decoder.h \ + logging.c \ + logging.h \ + ctf-meta.h \ + ctf-meta-visitors.h \ + ctf-meta-validate.c \ + ctf-meta-update-meanings.c \ + ctf-meta-update-in-ir.c \ + ctf-meta-update-default-clock-classes.c \ + ctf-meta-update-text-array-sequence.c \ + ctf-meta-update-value-storing-indexes.c \ + ctf-meta-update-stream-class-config.c \ + ctf-meta-warn-meaningless-header-fields.c \ + ctf-meta-translate.c \ + ctf-meta-resolve.c + +libctf_ast_la_LIBADD = $(UUID_LIBS) + +if BABELTRACE_BUILD_WITH_MINGW +libctf_ast_la_LIBADD += -lrpcrt4 -lintl -liconv -lole32 $(POPT_LIBS) +endif + +# start with empty files to clean +CLEANFILES = + +if HAVE_BISON +# we have bison: we can clean the generated parser files +CLEANFILES += parser.c parser.h parser.output +else # HAVE_BISON +# create target used to stop the build if we want to build the parser, +# but we don't have the necessary tool to do so +ERR_MSG = "Error: Cannot build target because bison is missing." +ERR_MSG += "Make sure bison is installed and run the configure script again." + +parser.c parser.h: parser.y + @echo $(ERR_MSG) + @false + +all-local: parser.c parser.h +endif # HAVE_BISON + +if HAVE_FLEX +# we have flex: we can clean the generated lexer files +CLEANFILES += lexer.c +else # HAVE_FLEX +# create target used to stop the build if we want to build the lexer, +# but we don't have the necessary tool to do so +ERR_MSG = "Error: Cannot build target because flex is missing." +ERR_MSG += "Make sure flex is installed and run the configure script again." + +filter-lexer.c: lexer.l + @echo $(ERR_MSG) + @false + +all-local: lexer.c +endif # HAVE_FLEX diff --git a/src/plugins/ctf/common/metadata/ast.h b/src/plugins/ctf/common/metadata/ast.h new file mode 100644 index 00000000..6f6fbe7b --- /dev/null +++ b/src/plugins/ctf/common/metadata/ast.h @@ -0,0 +1,344 @@ +#ifndef _CTF_AST_H +#define _CTF_AST_H + +/* + * ctf-ast.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include +#include +#include "common/list.h" +#include +#include "common/babeltrace.h" + +#include "decoder.h" +#include "ctf-meta.h" + +// the parameter name (of the reentrant 'yyparse' function) +// data is a pointer to a 'SParserParam' structure +//#define YYPARSE_PARAM scanner + +struct ctf_node; +struct ctf_parser; +struct ctf_visitor_generate_ir; + +#define EINCOMPLETE 1000 + +#define FOREACH_CTF_NODES(F) \ + F(NODE_UNKNOWN) \ + F(NODE_ROOT) \ + F(NODE_ERROR) \ + F(NODE_EVENT) \ + F(NODE_STREAM) \ + F(NODE_ENV) \ + F(NODE_TRACE) \ + F(NODE_CLOCK) \ + F(NODE_CALLSITE) \ + F(NODE_CTF_EXPRESSION) \ + F(NODE_UNARY_EXPRESSION) \ + F(NODE_TYPEDEF) \ + F(NODE_TYPEALIAS_TARGET) \ + F(NODE_TYPEALIAS_ALIAS) \ + F(NODE_TYPEALIAS) \ + F(NODE_TYPE_SPECIFIER) \ + F(NODE_TYPE_SPECIFIER_LIST) \ + F(NODE_POINTER) \ + F(NODE_TYPE_DECLARATOR) \ + F(NODE_FLOATING_POINT) \ + F(NODE_INTEGER) \ + F(NODE_STRING) \ + F(NODE_ENUMERATOR) \ + F(NODE_ENUM) \ + F(NODE_STRUCT_OR_VARIANT_DECLARATION) \ + F(NODE_VARIANT) \ + F(NODE_STRUCT) + +enum node_type { +#define ENTRY(S) S, + FOREACH_CTF_NODES(ENTRY) +#undef ENTRY + NR_NODE_TYPES, +}; + +struct ctf_node { + /* + * Parent node is only set on demand by specific visitor. + */ + struct ctf_node *parent; + struct bt_list_head siblings; + struct bt_list_head tmp_head; + unsigned int lineno; + /* + * We mark nodes visited in the generate-ir phase (last + * phase). We only mark the 1-depth level nodes as visited + * (never the root node, and not their sub-nodes). This allows + * skipping already visited nodes when doing incremental + * metadata append. + */ + int visited; + + enum node_type type; + union { + struct { + } unknown; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + struct bt_list_head trace; + struct bt_list_head env; + struct bt_list_head stream; + struct bt_list_head event; + struct bt_list_head clock; + struct bt_list_head callsite; + } root; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } event; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } stream; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } env; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } trace; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } clock; + struct { + /* + * Children nodes are ctf_expression, field_class_def, + * field_class_alias and field_class_specifier_list. + */ + struct bt_list_head declaration_list; + } callsite; + struct { + struct bt_list_head left; /* Should be string */ + struct bt_list_head right; /* Unary exp. or type */ + } ctf_expression; + struct { + enum { + UNARY_UNKNOWN = 0, + UNARY_STRING, + UNARY_SIGNED_CONSTANT, + UNARY_UNSIGNED_CONSTANT, + UNARY_SBRAC, + } type; + union { + /* + * string for identifier, id_type, keywords, + * string literals and character constants. + */ + char *string; + int64_t signed_constant; + uint64_t unsigned_constant; + struct ctf_node *sbrac_exp; + } u; + enum { + UNARY_LINK_UNKNOWN = 0, + UNARY_DOTLINK, + UNARY_ARROWLINK, + UNARY_DOTDOTDOT, + } link; + } unary_expression; + struct { + struct ctf_node *field_class_specifier_list; + struct bt_list_head field_class_declarators; + } field_class_def; + /* new type is "alias", existing type "target" */ + struct { + struct ctf_node *field_class_specifier_list; + struct bt_list_head field_class_declarators; + } field_class_alias_target; + struct { + struct ctf_node *field_class_specifier_list; + struct bt_list_head field_class_declarators; + } field_class_alias_name; + struct { + struct ctf_node *target; + struct ctf_node *alias; + } field_class_alias; + struct { + enum { + TYPESPEC_UNKNOWN = 0, + TYPESPEC_VOID, + TYPESPEC_CHAR, + TYPESPEC_SHORT, + TYPESPEC_INT, + TYPESPEC_LONG, + TYPESPEC_FLOAT, + TYPESPEC_DOUBLE, + TYPESPEC_SIGNED, + TYPESPEC_UNSIGNED, + TYPESPEC_BOOL, + TYPESPEC_COMPLEX, + TYPESPEC_IMAGINARY, + TYPESPEC_CONST, + TYPESPEC_ID_TYPE, + TYPESPEC_FLOATING_POINT, + TYPESPEC_INTEGER, + TYPESPEC_STRING, + TYPESPEC_STRUCT, + TYPESPEC_VARIANT, + TYPESPEC_ENUM, + } type; + /* For struct, variant and enum */ + struct ctf_node *node; + const char *id_type; + } field_class_specifier; + struct { + /* list of field_class_specifier */ + struct bt_list_head head; + } field_class_specifier_list; + struct { + unsigned int const_qualifier; + } pointer; + struct { + struct bt_list_head pointers; + enum { + TYPEDEC_UNKNOWN = 0, + TYPEDEC_ID, /* identifier */ + TYPEDEC_NESTED, /* (), array or sequence */ + } type; + union { + char *id; + struct { + /* typedec has no pointer list */ + struct ctf_node *field_class_declarator; + /* + * unary expression (value) or + * field_class_specifier_list. + */ + struct bt_list_head length; + /* for abstract type declarator */ + unsigned int abstract_array; + } nested; + } u; + struct ctf_node *bitfield_len; + } field_class_declarator; + struct { + /* Children nodes are ctf_expression. */ + struct bt_list_head expressions; + } floating_point; + struct { + /* Children nodes are ctf_expression. */ + struct bt_list_head expressions; + } integer; + struct { + /* Children nodes are ctf_expression. */ + struct bt_list_head expressions; + } string; + struct { + char *id; + /* + * Range list or single value node. Contains unary + * expressions. + */ + struct bt_list_head values; + } enumerator; + struct { + char *enum_id; + /* + * Either NULL, or points to unary expression or + * field_class_specifier_list. + */ + struct ctf_node *container_field_class; + struct bt_list_head enumerator_list; + int has_body; + } _enum; + struct { + struct ctf_node *field_class_specifier_list; + struct bt_list_head field_class_declarators; + } struct_or_variant_declaration; + struct { + char *name; + char *choice; + /* + * list of field_class_def, field_class_alias and + * declarations + */ + struct bt_list_head declaration_list; + int has_body; + } variant; + struct { + char *name; + /* + * list of field_class_def, field_class_alias and + * declarations + */ + struct bt_list_head declaration_list; + int has_body; + struct bt_list_head min_align; /* align() attribute */ + } _struct; + } u; +}; + +struct ctf_ast { + struct ctf_node root; +}; + +const char *node_type(struct ctf_node *node); + +BT_HIDDEN +struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create( + bt_self_component_source *self_comp, + const struct ctf_metadata_decoder_config *config); + +void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor); + +BT_HIDDEN +bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class( + struct ctf_visitor_generate_ir *visitor); + +BT_HIDDEN +struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class( + struct ctf_visitor_generate_ir *visitor); + +BT_HIDDEN +int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor, + struct ctf_node *node); + +BT_HIDDEN +int ctf_visitor_semantic_check(int depth, struct ctf_node *node); + +BT_HIDDEN +int ctf_visitor_parent_links(int depth, struct ctf_node *node); + +#endif /* _CTF_AST_H */ diff --git a/src/plugins/ctf/common/metadata/ctf-meta-resolve.c b/src/plugins/ctf/common/metadata/ctf-meta-resolve.c new file mode 100644 index 00000000..130ae61b --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-resolve.c @@ -0,0 +1,1316 @@ +/* + * Copyright 2016-2018 - Philippe Proulx + * Copyright 2015 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-RESOLVE" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include "common/common.h" +#include +#include +#include +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +typedef GPtrArray field_class_stack; + +/* + * A stack frame. + * + * `fc` contains a compound field class (structure, variant, array, + * or sequence) and `index` indicates the index of the field class in + * the upper frame (-1 for array and sequence field classes). `name` + * indicates the name of the field class in the upper frame (empty + * string for array and sequence field classes). + */ +struct field_class_stack_frame { + struct ctf_field_class *fc; + int64_t index; +}; + +/* + * The current context of the resolving engine. + */ +struct resolve_context { + struct ctf_trace_class *tc; + struct ctf_stream_class *sc; + struct ctf_event_class *ec; + + struct { + struct ctf_field_class *packet_header; + struct ctf_field_class *packet_context; + struct ctf_field_class *event_header; + struct ctf_field_class *event_common_context; + struct ctf_field_class *event_spec_context; + struct ctf_field_class *event_payload; + } scopes; + + /* Root scope being visited */ + enum ctf_scope root_scope; + field_class_stack *field_class_stack; + struct ctf_field_class *cur_fc; +}; + +/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */ +static const char * const absolute_path_prefixes[] = { + [CTF_SCOPE_PACKET_HEADER] = "trace.packet.header.", + [CTF_SCOPE_PACKET_CONTEXT] = "stream.packet.context.", + [CTF_SCOPE_EVENT_HEADER] = "stream.event.header.", + [CTF_SCOPE_EVENT_COMMON_CONTEXT] = "stream.event.context.", + [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT] = "event.context.", + [CTF_SCOPE_EVENT_PAYLOAD] = "event.fields.", +}; + +/* Number of path tokens used for the absolute prefixes */ +static const uint64_t absolute_path_prefix_ptoken_counts[] = { + [CTF_SCOPE_PACKET_HEADER] = 3, + [CTF_SCOPE_PACKET_CONTEXT] = 3, + [CTF_SCOPE_EVENT_HEADER] = 3, + [CTF_SCOPE_EVENT_COMMON_CONTEXT] = 3, + [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT] = 2, + [CTF_SCOPE_EVENT_PAYLOAD] = 2, +}; + +static +void destroy_field_class_stack_frame(struct field_class_stack_frame *frame) +{ + if (!frame) { + return; + } + + g_free(frame); +} + +/* + * Creates a class stack. + */ +static +field_class_stack *field_class_stack_create(void) +{ + return g_ptr_array_new_with_free_func( + (GDestroyNotify) destroy_field_class_stack_frame); +} + +/* + * Destroys a class stack. + */ +static +void field_class_stack_destroy(field_class_stack *stack) +{ + if (stack) { + g_ptr_array_free(stack, TRUE); + } +} + +/* + * Pushes a field class onto a class stack. + */ +static +int field_class_stack_push(field_class_stack *stack, struct ctf_field_class *fc) +{ + int ret = 0; + struct field_class_stack_frame *frame = NULL; + + if (!stack || !fc) { + BT_LOGE("Invalid parameter: stack or field class is NULL."); + ret = -1; + goto end; + } + + frame = g_new0(struct field_class_stack_frame, 1); + if (!frame) { + BT_LOGE_STR("Failed to allocate one field class stack frame."); + ret = -1; + goto end; + } + + BT_LOGV("Pushing field class on context's stack: " + "fc-addr=%p, stack-size-before=%u", fc, stack->len); + frame->fc = fc; + g_ptr_array_add(stack, frame); + +end: + return ret; +} + +/* + * Checks whether or not `stack` is empty. + */ +static +bool field_class_stack_empty(field_class_stack *stack) +{ + return stack->len == 0; +} + +/* + * Returns the number of frames in `stack`. + */ +static +size_t field_class_stack_size(field_class_stack *stack) +{ + return stack->len; +} + +/* + * Returns the top frame of `stack`. + */ +static +struct field_class_stack_frame *field_class_stack_peek(field_class_stack *stack) +{ + struct field_class_stack_frame *entry = NULL; + + if (!stack || field_class_stack_empty(stack)) { + goto end; + } + + entry = g_ptr_array_index(stack, stack->len - 1); +end: + return entry; +} + +/* + * Returns the frame at index `index` in `stack`. + */ +static +struct field_class_stack_frame *field_class_stack_at(field_class_stack *stack, + size_t index) +{ + struct field_class_stack_frame *entry = NULL; + + if (!stack || index >= stack->len) { + goto end; + } + + entry = g_ptr_array_index(stack, index); + +end: + return entry; +} + +/* + * Removes the top frame of `stack`. + */ +static +void field_class_stack_pop(field_class_stack *stack) +{ + if (!field_class_stack_empty(stack)) { + /* + * This will call the frame's destructor and free it, as + * well as put its contained field class. + */ + BT_LOGV("Popping context's stack: stack-size-before=%u", + stack->len); + g_ptr_array_set_size(stack, stack->len - 1); + } +} + +/* + * Returns the scope field class of `scope` in the context `ctx`. + */ +static +struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx, + enum ctf_scope scope) +{ + switch (scope) { + case CTF_SCOPE_PACKET_HEADER: + return ctx->scopes.packet_header; + case CTF_SCOPE_PACKET_CONTEXT: + return ctx->scopes.packet_context; + case CTF_SCOPE_EVENT_HEADER: + return ctx->scopes.event_header; + case CTF_SCOPE_EVENT_COMMON_CONTEXT: + return ctx->scopes.event_common_context; + case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: + return ctx->scopes.event_spec_context; + case CTF_SCOPE_EVENT_PAYLOAD: + return ctx->scopes.event_payload; + default: + abort(); + } + + return NULL; +} + +/* + * Returns the CTF scope from a path string. May return -1 if the path + * is found to be relative. + */ +static +enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr) +{ + enum ctf_scope scope; + enum ctf_scope ret = -1; + const size_t prefixes_count = sizeof(absolute_path_prefixes) / + sizeof(*absolute_path_prefixes); + + for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER + + prefixes_count; scope++) { + /* + * Chech if path string starts with a known absolute + * path prefix. + * + * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES. + */ + if (strncmp(pathstr, absolute_path_prefixes[scope], + strlen(absolute_path_prefixes[scope]))) { + /* Prefix does not match: try the next one */ + BT_LOGV("Prefix does not match: trying the next one: " + "path=\"%s\", path-prefix=\"%s\", scope=%s", + pathstr, absolute_path_prefixes[scope], + ctf_scope_string(scope)); + continue; + } + + /* Found it! */ + ret = scope; + BT_LOGV("Found root scope from absolute path: " + "path=\"%s\", scope=%s", pathstr, + ctf_scope_string(scope)); + goto end; + } + +end: + return ret; +} + +/* + * Destroys a path token. + */ +static +void ptokens_destroy_func(gpointer ptoken, gpointer data) +{ + g_string_free(ptoken, TRUE); +} + +/* + * Destroys a path token list. + */ +static +void ptokens_destroy(GList *ptokens) +{ + if (!ptokens) { + return; + } + + g_list_foreach(ptokens, ptokens_destroy_func, NULL); + g_list_free(ptokens); +} + +/* + * Returns the string contained in a path token. + */ +static +const char *ptoken_get_string(GList *ptoken) +{ + GString *tokenstr = (GString *) ptoken->data; + + return tokenstr->str; +} + +/* + * Converts a path string to a path token list, that is, splits the + * individual words of a path string into a list of individual + * strings. + */ +static +GList *pathstr_to_ptokens(const char *pathstr) +{ + const char *at = pathstr; + const char *last = at; + GList *ptokens = NULL; + + for (;;) { + if (*at == '.' || *at == '\0') { + GString *tokenstr; + + if (at == last) { + /* Error: empty token */ + BT_LOGE("Empty path token: path=\"%s\", pos=%u", + pathstr, (unsigned int) (at - pathstr)); + goto error; + } + + tokenstr = g_string_new(NULL); + g_string_append_len(tokenstr, last, at - last); + ptokens = g_list_append(ptokens, tokenstr); + last = at + 1; + } + + if (*at == '\0') { + break; + } + + at++; + } + + return ptokens; + +error: + ptokens_destroy(ptokens); + return NULL; +} + +/* + * Converts a path token list to a field path object. The path token + * list is relative from `fc`. The index of the source looking for its + * target within `fc` is indicated by `src_index`. This can be `INT64_MAX` + * if the source is contained in `fc`. + * + * `field_path` is an output parameter owned by the caller that must be + * filled here. + */ +static +int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path, + struct ctf_field_class *fc, int64_t src_index) +{ + int ret = 0; + GList *cur_ptoken = ptokens; + bool first_level_done = false; + + /* Locate target */ + while (cur_ptoken) { + int64_t child_index; + struct ctf_field_class *child_fc; + const char *ft_name = ptoken_get_string(cur_ptoken); + + BT_LOGV("Current path token: token=\"%s\"", ft_name); + + /* Find to which index corresponds the current path token */ + if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || + fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { + child_index = -1; + } else { + child_index = + ctf_field_class_compound_get_field_class_index_from_name( + fc, ft_name); + if (child_index < 0) { + /* + * Error: field name does not exist or + * wrong current class. + */ + BT_LOGV("Cannot get index of field class: " + "field-name=\"%s\", " + "src-index=%" PRId64 ", " + "child-index=%" PRId64 ", " + "first-level-done=%d", + ft_name, src_index, child_index, + first_level_done); + ret = -1; + goto end; + } else if (child_index > src_index && + !first_level_done) { + BT_LOGV("Child field class is located after source field class: " + "field-name=\"%s\", " + "src-index=%" PRId64 ", " + "child-index=%" PRId64 ", " + "first-level-done=%d", + ft_name, src_index, child_index, + first_level_done); + ret = -1; + goto end; + } + + /* Next path token */ + cur_ptoken = g_list_next(cur_ptoken); + first_level_done = true; + } + + /* Create new field path entry */ + ctf_field_path_append_index(field_path, child_index); + + /* Get child field class */ + child_fc = ctf_field_class_compound_borrow_field_class_by_index( + fc, child_index); + BT_ASSERT(child_fc); + + /* Move child class to current class */ + fc = child_fc; + } + +end: + return ret; +} + +/* + * Converts a known absolute path token list to a field path object + * within the resolving context `ctx`. + * + * `field_path` is an output parameter owned by the caller that must be + * filled here. + */ +static +int absolute_ptokens_to_field_path(GList *ptokens, + struct ctf_field_path *field_path, + struct resolve_context *ctx) +{ + int ret = 0; + GList *cur_ptoken; + struct ctf_field_class *fc; + + /* + * Make sure we're not referring to a scope within a translated + * object. + */ + switch (field_path->root) { + case CTF_SCOPE_PACKET_HEADER: + if (ctx->tc->is_translated) { + BT_LOGE("Trace class is already translated: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + break; + case CTF_SCOPE_PACKET_CONTEXT: + case CTF_SCOPE_EVENT_HEADER: + case CTF_SCOPE_EVENT_COMMON_CONTEXT: + if (!ctx->sc) { + BT_LOGE("No current stream class: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + if (ctx->sc->is_translated) { + BT_LOGE("Stream class is already translated: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + break; + case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: + case CTF_SCOPE_EVENT_PAYLOAD: + if (!ctx->ec) { + BT_LOGE("No current event class: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + if (ctx->ec->is_translated) { + BT_LOGE("Event class is already translated: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + break; + + default: + abort(); + } + + /* Skip absolute path tokens */ + cur_ptoken = g_list_nth(ptokens, + absolute_path_prefix_ptoken_counts[field_path->root]); + + /* Start with root class */ + fc = borrow_class_from_ctx(ctx, field_path->root); + if (!fc) { + /* Error: root class is not available */ + BT_LOGE("Root field class is not available: " + "root-scope=%s", + ctf_scope_string(field_path->root)); + ret = -1; + goto end; + } + + /* Locate target */ + ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX); + +end: + return ret; +} + +/* + * Converts a known relative path token list to a field path object + * within the resolving context `ctx`. + * + * `field_path` is an output parameter owned by the caller that must be + * filled here. + */ +static +int relative_ptokens_to_field_path(GList *ptokens, + struct ctf_field_path *field_path, struct resolve_context *ctx) +{ + int ret = 0; + int64_t parent_pos_in_stack; + struct ctf_field_path tail_field_path; + + ctf_field_path_init(&tail_field_path); + parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1; + + while (parent_pos_in_stack >= 0) { + struct ctf_field_class *parent_class = + field_class_stack_at(ctx->field_class_stack, + parent_pos_in_stack)->fc; + int64_t cur_index = field_class_stack_at(ctx->field_class_stack, + parent_pos_in_stack)->index; + + BT_LOGV("Locating target field class from current parent field class: " + "parent-pos=%" PRId64 ", parent-fc-addr=%p, " + "cur-index=%" PRId64, + parent_pos_in_stack, parent_class, cur_index); + + /* Locate target from current parent class */ + ret = ptokens_to_field_path(ptokens, &tail_field_path, + parent_class, cur_index); + if (ret) { + /* Not found... yet */ + BT_LOGV_STR("Not found at this point."); + ctf_field_path_clear(&tail_field_path); + } else { + /* Found: stitch tail field path to head field path */ + uint64_t i = 0; + size_t tail_field_path_len = + tail_field_path.path->len; + + while (BT_TRUE) { + struct ctf_field_class *cur_class = + field_class_stack_at( + ctx->field_class_stack, i)->fc; + int64_t index = field_class_stack_at( + ctx->field_class_stack, i)->index; + + if (cur_class == parent_class) { + break; + } + + ctf_field_path_append_index(field_path, + index); + i++; + } + + for (i = 0; i < tail_field_path_len; i++) { + int64_t index = + ctf_field_path_borrow_index_by_index( + &tail_field_path, i); + + ctf_field_path_append_index(field_path, + (int64_t) index); + } + break; + } + + parent_pos_in_stack--; + } + + if (parent_pos_in_stack < 0) { + /* Not found */ + ret = -1; + } + + ctf_field_path_fini(&tail_field_path); + return ret; +} + +/* + * Converts a path string to a field path object within the resolving + * context `ctx`. + */ +static +int pathstr_to_field_path(const char *pathstr, + struct ctf_field_path *field_path, struct resolve_context *ctx) +{ + int ret = 0; + enum ctf_scope root_scope; + GList *ptokens = NULL; + + /* Convert path string to path tokens */ + ptokens = pathstr_to_ptokens(pathstr); + if (!ptokens) { + BT_LOGE("Cannot convert path string to path tokens: " + "path=\"%s\"", pathstr); + ret = -1; + goto end; + } + + /* Absolute or relative path? */ + root_scope = get_root_scope_from_absolute_pathstr(pathstr); + + if (root_scope == -1) { + /* Relative path: start with current root scope */ + field_path->root = ctx->root_scope; + BT_LOGV("Detected relative path: starting with current root scope: " + "scope=%s", ctf_scope_string(field_path->root)); + ret = relative_ptokens_to_field_path(ptokens, field_path, ctx); + if (ret) { + BT_LOGE("Cannot get relative field path of path string: " + "path=\"%s\", start-scope=%s, end-scope=%s", + pathstr, ctf_scope_string(ctx->root_scope), + ctf_scope_string(field_path->root)); + goto end; + } + } else { + /* Absolute path: use found root scope */ + field_path->root = root_scope; + BT_LOGV("Detected absolute path: using root scope: " + "scope=%s", ctf_scope_string(field_path->root)); + ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx); + if (ret) { + BT_LOGE("Cannot get absolute field path of path string: " + "path=\"%s\", root-scope=%s", + pathstr, ctf_scope_string(root_scope)); + goto end; + } + } + + if (BT_LOG_ON_VERBOSE && ret == 0) { + GString *field_path_pretty = ctf_field_path_string(field_path); + const char *field_path_pretty_str = + field_path_pretty ? field_path_pretty->str : NULL; + + BT_LOGV("Found field path: path=\"%s\", field-path=\"%s\"", + pathstr, field_path_pretty_str); + + if (field_path_pretty) { + g_string_free(field_path_pretty, TRUE); + } + } + +end: + ptokens_destroy(ptokens); + return ret; +} + +/* + * Retrieves a field class by following the field path `field_path` in + * the resolving context `ctx`. + */ +static +struct ctf_field_class *field_path_to_field_class( + struct ctf_field_path *field_path, struct resolve_context *ctx) +{ + uint64_t i; + struct ctf_field_class *fc; + + /* Start with root class */ + fc = borrow_class_from_ctx(ctx, field_path->root); + if (!fc) { + /* Error: root class is not available */ + BT_LOGE("Root field class is not available: root-scope=%s", + ctf_scope_string(field_path->root)); + goto end; + } + + /* Locate target */ + for (i = 0; i < field_path->path->len; i++) { + struct ctf_field_class *child_fc; + int64_t child_index = + ctf_field_path_borrow_index_by_index(field_path, i); + + /* Get child field class */ + child_fc = ctf_field_class_compound_borrow_field_class_by_index( + fc, child_index); + BT_ASSERT(child_fc); + + /* Move child class to current class */ + fc = child_fc; + } + +end: + return fc; +} + +/* + * Fills the equivalent field path object of the context class stack. + */ +static +void get_ctx_stack_field_path(struct resolve_context *ctx, + struct ctf_field_path *field_path) +{ + uint64_t i; + + BT_ASSERT(field_path); + field_path->root = ctx->root_scope; + ctf_field_path_clear(field_path); + + for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) { + struct field_class_stack_frame *frame = + field_class_stack_at(ctx->field_class_stack, i); + + ctf_field_path_append_index(field_path, frame->index); + } +} + +/* + * Returns the index of the lowest common ancestor of two field path + * objects having the same root scope. + */ +int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1, + struct ctf_field_path *field_path2) +{ + int64_t lca_index = 0; + uint64_t field_path1_len, field_path2_len; + + if (BT_LOG_ON_VERBOSE) { + GString *field_path1_pretty = + ctf_field_path_string(field_path1); + GString *field_path2_pretty = + ctf_field_path_string(field_path2); + const char *field_path1_pretty_str = + field_path1_pretty ? field_path1_pretty->str : NULL; + const char *field_path2_pretty_str = + field_path2_pretty ? field_path2_pretty->str : NULL; + + BT_LOGV("Finding lowest common ancestor (LCA) between two field paths: " + "field-path-1=\"%s\", field-path-2=\"%s\"", + field_path1_pretty_str, field_path2_pretty_str); + + if (field_path1_pretty) { + g_string_free(field_path1_pretty, TRUE); + } + + if (field_path2_pretty) { + g_string_free(field_path2_pretty, TRUE); + } + } + + /* + * Start from both roots and find the first mismatch. + */ + BT_ASSERT(field_path1->root == field_path2->root); + field_path1_len = field_path1->path->len; + field_path2_len = field_path2->path->len; + + while (true) { + int64_t target_index, ctx_index; + + if (lca_index == (int64_t) field_path2_len || + lca_index == (int64_t) field_path1_len) { + /* + * This means that both field paths never split. + * This is invalid because the target cannot be + * an ancestor of the source. + */ + BT_LOGE("Source field class is an ancestor of target field class or vice versa: " + "lca-index=%" PRId64 ", " + "field-path-1-len=%" PRIu64 ", " + "field-path-2-len=%" PRIu64, + lca_index, field_path1_len, field_path2_len); + lca_index = -1; + break; + } + + target_index = ctf_field_path_borrow_index_by_index(field_path1, + lca_index); + ctx_index = ctf_field_path_borrow_index_by_index(field_path2, + lca_index); + + if (target_index != ctx_index) { + /* LCA index is the previous */ + break; + } + + lca_index++; + } + + BT_LOGV("Found LCA: lca-index=%" PRId64, lca_index); + return lca_index; +} + +/* + * Validates a target field path. + */ +static +int validate_target_field_path(struct ctf_field_path *target_field_path, + struct ctf_field_class *target_fc, + struct resolve_context *ctx) +{ + int ret = 0; + struct ctf_field_path ctx_field_path; + uint64_t target_field_path_len = target_field_path->path->len; + int64_t lca_index; + + /* Get context field path */ + ctf_field_path_init(&ctx_field_path); + get_ctx_stack_field_path(ctx, &ctx_field_path); + + /* + * Make sure the target is not a root. + */ + if (target_field_path_len == 0) { + BT_LOGE_STR("Target field path's length is 0 (targeting the root)."); + ret = -1; + goto end; + } + + /* + * Make sure the root of the target field path is not located + * after the context field path's root. + */ + if (target_field_path->root > ctx_field_path.root) { + BT_LOGE("Target field class is located after source field class: " + "target-root=%s, source-root=%s", + ctf_scope_string(target_field_path->root), + ctf_scope_string(ctx_field_path.root)); + ret = -1; + goto end; + } + + if (target_field_path->root == ctx_field_path.root) { + int64_t target_index, ctx_index; + + /* + * Find the index of the lowest common ancestor of both field + * paths. + */ + lca_index = get_field_paths_lca_index(target_field_path, + &ctx_field_path); + if (lca_index < 0) { + BT_LOGE_STR("Cannot get least common ancestor."); + ret = -1; + goto end; + } + + /* + * Make sure the target field path is located before the + * context field path. + */ + target_index = ctf_field_path_borrow_index_by_index( + target_field_path, (uint64_t) lca_index); + ctx_index = ctf_field_path_borrow_index_by_index( + &ctx_field_path, (uint64_t) lca_index); + + if (target_index >= ctx_index) { + BT_LOGE("Target field class's index is greater than or equal to source field class's index in LCA: " + "lca-index=%" PRId64 ", " + "target-index=%" PRId64 ", " + "source-index=%" PRId64, + lca_index, target_index, ctx_index); + ret = -1; + goto end; + } + } + + /* + * Make sure the target class has the right class and properties. + */ + switch (ctx->cur_fc->type) { + case CTF_FIELD_CLASS_TYPE_VARIANT: + if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE("Variant field class's tag field class is not an enumeration field class: " + "tag-fc-addr=%p, tag-fc-id=%d", + target_fc, target_fc->type); + ret = -1; + goto end; + } + break; + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_int *int_fc = (void *) target_fc; + + if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT && + target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE("Sequence field class's length field class is not an unsigned integer field class: " + "length-fc-addr=%p, length-fc-id=%d", + target_fc, target_fc->type); + ret = -1; + goto end; + } + + if (int_fc->is_signed) { + BT_LOGE("Sequence field class's length field class is not an unsigned integer field class: " + "length-fc-addr=%p, length-fc-id=%d", + target_fc, target_fc->type); + ret = -1; + goto end; + } + break; + } + default: + abort(); + } + +end: + ctf_field_path_fini(&ctx_field_path); + return ret; +} + +/* + * Resolves a variant or sequence field class `fc`. + */ +static +int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc, + struct resolve_context *ctx) +{ + int ret = 0; + const char *pathstr; + struct ctf_field_path target_field_path; + struct ctf_field_class *target_fc = NULL; + GString *target_field_path_pretty = NULL; + const char *target_field_path_pretty_str; + + ctf_field_path_init(&target_field_path); + + /* Get path string */ + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_sequence *seq_fc = (void *) fc; + pathstr = seq_fc->length_ref->str; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + pathstr = var_fc->tag_ref->str; + break; + } + default: + abort(); + } + + if (!pathstr) { + BT_LOGE_STR("Cannot get path string."); + ret = -1; + goto end; + } + + /* Get target field path out of path string */ + ret = pathstr_to_field_path(pathstr, &target_field_path, ctx); + if (ret) { + BT_LOGE("Cannot get target field path for path string: " + "path=\"%s\"", pathstr); + goto end; + } + + target_field_path_pretty = ctf_field_path_string( + &target_field_path); + target_field_path_pretty_str = + target_field_path_pretty ? target_field_path_pretty->str : NULL; + + /* Get target field class */ + target_fc = field_path_to_field_class(&target_field_path, ctx); + if (!target_fc) { + BT_LOGE("Cannot get target field class for path string: " + "path=\"%s\", target-field-path=\"%s\"", + pathstr, target_field_path_pretty_str); + ret = -1; + goto end; + } + + ret = validate_target_field_path(&target_field_path, + target_fc, ctx); + if (ret) { + BT_LOGE("Invalid target field path for path string: " + "path=\"%s\", target-field-path=\"%s\"", + pathstr, target_field_path_pretty_str); + goto end; + } + + /* Set target field path and target field class */ + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_sequence *seq_fc = (void *) fc; + + ctf_field_path_copy_content(&seq_fc->length_path, + &target_field_path); + seq_fc->length_fc = (void *) target_fc; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + ctf_field_path_copy_content(&var_fc->tag_path, + &target_field_path); + ctf_field_class_variant_set_tag_field_class(var_fc, + (void *) target_fc); + break; + } + default: + abort(); + } + +end: + if (target_field_path_pretty) { + g_string_free(target_field_path_pretty, TRUE); + } + + ctf_field_path_fini(&target_field_path); + return ret; +} + +/* + * Resolves a field class `fc`. + */ +static +int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx) +{ + int ret = 0; + + if (!fc) { + /* Field class is not available; still valid */ + goto end; + } + + ctx->cur_fc = fc; + + /* Resolve sequence/variant field class */ + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + case CTF_FIELD_CLASS_TYPE_VARIANT: + ret = resolve_sequence_or_variant_field_class(fc, ctx); + if (ret) { + BT_LOGE("Cannot resolve sequence field class's length or variant field class's tag: " + "ret=%d, fc-addr=%p", ret, fc); + goto end; + } + + break; + default: + break; + } + + /* Recurse into compound classes */ + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + case CTF_FIELD_CLASS_TYPE_VARIANT: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + case CTF_FIELD_CLASS_TYPE_ARRAY: + { + uint64_t i; + uint64_t field_count = + ctf_field_class_compound_get_field_class_count(fc); + + ret = field_class_stack_push(ctx->field_class_stack, fc); + if (ret) { + BT_LOGE("Cannot push field class on context's stack: " + "fc-addr=%p", fc); + goto end; + } + + for (i = 0; i < field_count; i++) { + struct ctf_field_class *child_fc = + ctf_field_class_compound_borrow_field_class_by_index( + fc, i); + + BT_ASSERT(child_fc); + + if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY|| + fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { + field_class_stack_peek( + ctx->field_class_stack)->index = -1; + } else { + field_class_stack_peek( + ctx->field_class_stack)->index = + (int64_t) i; + } + + BT_LOGV("Resolving field class's child field class: " + "parent-fc-addr=%p, child-fc-addr=%p, " + "index=%" PRIu64 ", count=%" PRIu64, + fc, child_fc, i, field_count); + ret = resolve_field_class(child_fc, ctx); + if (ret) { + goto end; + } + } + + field_class_stack_pop(ctx->field_class_stack); + break; + } + default: + break; + } + +end: + return ret; +} + +/* + * Resolves the root field class corresponding to the scope `root_scope`. + */ +static +int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx) +{ + int ret; + + BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0); + ctx->root_scope = root_scope; + ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx); + ctx->root_scope = -1; + return ret; +} + +static +int resolve_event_class_field_classes(struct resolve_context *ctx, + struct ctf_event_class *ec) +{ + int ret = 0; + + BT_ASSERT(!ctx->scopes.event_spec_context); + BT_ASSERT(!ctx->scopes.event_payload); + + if (ec->is_translated) { + goto end; + } + + ctx->ec = ec; + ctx->scopes.event_spec_context = ec->spec_context_fc; + ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx); + if (ret) { + BT_LOGE("Cannot resolve event specific context field class: " + "ret=%d", ret); + goto end; + } + + ctx->scopes.event_payload = ec->payload_fc; + ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx); + if (ret) { + BT_LOGE("Cannot resolve event payload field class: " + "ret=%d", ret); + goto end; + } + +end: + ctx->scopes.event_spec_context = NULL; + ctx->scopes.event_payload = NULL; + ctx->ec = NULL; + return ret; +} + +static +int resolve_stream_class_field_classes(struct resolve_context *ctx, + struct ctf_stream_class *sc) +{ + int ret = 0; + uint64_t i; + + BT_ASSERT(!ctx->scopes.packet_context); + BT_ASSERT(!ctx->scopes.event_header); + BT_ASSERT(!ctx->scopes.event_common_context); + ctx->sc = sc; + + if (!sc->is_translated) { + ctx->scopes.packet_context = sc->packet_context_fc; + ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx); + if (ret) { + BT_LOGE("Cannot resolve packet context field class: " + "ret=%d", ret); + goto end; + } + + ctx->scopes.event_header = sc->event_header_fc; + ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx); + if (ret) { + BT_LOGE("Cannot resolve event header field class: " + "ret=%d", ret); + goto end; + } + + ctx->scopes.event_common_context = sc->event_common_context_fc; + ret = resolve_root_class(CTF_SCOPE_EVENT_SPECIFIC_CONTEXT, ctx); + if (ret) { + BT_LOGE("Cannot resolve event common context field class: " + "ret=%d", ret); + goto end; + } + } + + ctx->scopes.packet_context = sc->packet_context_fc; + ctx->scopes.event_header = sc->event_header_fc; + ctx->scopes.event_common_context = sc->event_common_context_fc; + + for (i = 0; i < sc->event_classes->len; i++) { + struct ctf_event_class *ec = sc->event_classes->pdata[i]; + + ret = resolve_event_class_field_classes(ctx, ec); + if (ret) { + BT_LOGE("Cannot resolve event class's field classes: " + "ec-id=%" PRIu64 ", ec-name=\"%s\"", + ec->id, ec->name->str); + goto end; + } + } + +end: + ctx->scopes.packet_context = NULL; + ctx->scopes.event_header = NULL; + ctx->scopes.event_common_context = NULL; + ctx->sc = NULL; + return ret; +} + +BT_HIDDEN +int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc) +{ + int ret = 0; + uint64_t i; + struct resolve_context ctx = { + .tc = tc, + .sc = NULL, + .ec = NULL, + .scopes = { + .packet_header = tc->packet_header_fc, + .packet_context = NULL, + .event_header = NULL, + .event_common_context = NULL, + .event_spec_context = NULL, + .event_payload = NULL, + }, + .root_scope = CTF_SCOPE_PACKET_HEADER, + .cur_fc = NULL, + }; + + /* Initialize class stack */ + ctx.field_class_stack = field_class_stack_create(); + if (!ctx.field_class_stack) { + BT_LOGE_STR("Cannot create field class stack."); + ret = -1; + goto end; + } + + if (!tc->is_translated) { + ctx.scopes.packet_header = tc->packet_header_fc; + ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, &ctx); + if (ret) { + BT_LOGE("Cannot resolve packet header field class: " + "ret=%d", ret); + goto end; + } + } + + ctx.scopes.packet_header = tc->packet_header_fc; + + for (i = 0; i < tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = tc->stream_classes->pdata[i]; + + ret = resolve_stream_class_field_classes(&ctx, sc); + if (ret) { + BT_LOGE("Cannot resolve stream class's field classes: " + "sc-id=%" PRIu64, sc->id); + goto end; + } + } + +end: + field_class_stack_destroy(ctx.field_class_stack); + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-translate.c b/src/plugins/ctf/common/metadata/ctf-meta-translate.c new file mode 100644 index 00000000..287cc259 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-translate.c @@ -0,0 +1,626 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-TRANSLATE" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +struct ctx { + bt_self_component_source *self_comp; + bt_trace_class *ir_tc; + bt_stream_class *ir_sc; + struct ctf_trace_class *tc; + struct ctf_stream_class *sc; + struct ctf_event_class *ec; + enum ctf_scope scope; +}; + +static inline +bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, + struct ctf_field_class *fc); + +static inline +void ctf_field_class_int_set_props(struct ctf_field_class_int *fc, + bt_field_class *ir_fc) +{ + bt_field_class_integer_set_field_value_range(ir_fc, + fc->base.size); + bt_field_class_integer_set_preferred_display_base(ir_fc, + fc->disp_base); +} + +static inline +bt_field_class *ctf_field_class_int_to_ir(struct ctx *ctx, + struct ctf_field_class_int *fc) +{ + bt_field_class *ir_fc; + + if (fc->is_signed) { + ir_fc = bt_field_class_signed_integer_create(ctx->ir_tc); + } else { + ir_fc = bt_field_class_unsigned_integer_create(ctx->ir_tc); + } + + BT_ASSERT(ir_fc); + ctf_field_class_int_set_props(fc, ir_fc); + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_enum_to_ir(struct ctx *ctx, + struct ctf_field_class_enum *fc) +{ + int ret; + bt_field_class *ir_fc; + uint64_t i; + + if (fc->base.is_signed) { + ir_fc = bt_field_class_signed_enumeration_create(ctx->ir_tc); + } else { + ir_fc = bt_field_class_unsigned_enumeration_create(ctx->ir_tc); + } + + BT_ASSERT(ir_fc); + ctf_field_class_int_set_props((void *) fc, ir_fc); + + for (i = 0; i < fc->mappings->len; i++) { + struct ctf_field_class_enum_mapping *mapping = + ctf_field_class_enum_borrow_mapping_by_index(fc, i); + + if (fc->base.is_signed) { + ret = bt_field_class_signed_enumeration_map_range( + ir_fc, mapping->label->str, + mapping->range.lower.i, mapping->range.upper.i); + } else { + ret = bt_field_class_unsigned_enumeration_map_range( + ir_fc, mapping->label->str, + mapping->range.lower.u, mapping->range.upper.u); + } + + BT_ASSERT(ret == 0); + } + + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_float_to_ir(struct ctx *ctx, + struct ctf_field_class_float *fc) +{ + bt_field_class *ir_fc; + + ir_fc = bt_field_class_real_create(ctx->ir_tc); + BT_ASSERT(ir_fc); + + if (fc->base.size == 32) { + bt_field_class_real_set_is_single_precision(ir_fc, + BT_TRUE); + } + + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_string_to_ir(struct ctx *ctx, + struct ctf_field_class_string *fc) +{ + bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc); + + BT_ASSERT(ir_fc); + return ir_fc; +} + +static inline +void translate_struct_field_class_members(struct ctx *ctx, + struct ctf_field_class_struct *fc, bt_field_class *ir_fc, + bool with_header_prefix, + struct ctf_field_class_struct *context_fc) +{ + uint64_t i; + int ret; + + for (i = 0; i < fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index(fc, i); + bt_field_class *member_ir_fc; + const char *name = named_fc->name->str; + + if (!named_fc->fc->in_ir) { + continue; + } + + member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc); + BT_ASSERT(member_ir_fc); + ret = bt_field_class_structure_append_member(ir_fc, name, + member_ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(member_ir_fc); + } +} + +static inline +bt_field_class *ctf_field_class_struct_to_ir(struct ctx *ctx, + struct ctf_field_class_struct *fc) +{ + bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc); + + BT_ASSERT(ir_fc); + translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL); + return ir_fc; +} + +static inline +bt_field_class *borrow_ir_fc_from_field_path(struct ctx *ctx, + struct ctf_field_path *field_path) +{ + bt_field_class *ir_fc = NULL; + struct ctf_field_class *fc = ctf_field_path_borrow_field_class( + field_path, ctx->tc, ctx->sc, ctx->ec); + + BT_ASSERT(fc); + + if (fc->in_ir) { + ir_fc = fc->ir_fc; + } + + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_variant_to_ir(struct ctx *ctx, + struct ctf_field_class_variant *fc) +{ + int ret; + bt_field_class *ir_fc = bt_field_class_variant_create(ctx->ir_tc); + uint64_t i; + + BT_ASSERT(ir_fc); + + if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER && + fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) { + ret = bt_field_class_variant_set_selector_field_class( + ir_fc, borrow_ir_fc_from_field_path(ctx, + &fc->tag_path)); + BT_ASSERT(ret == 0); + } + + for (i = 0; i < fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index(fc, i); + bt_field_class *option_ir_fc; + + BT_ASSERT(named_fc->fc->in_ir); + option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc); + BT_ASSERT(option_ir_fc); + ret = bt_field_class_variant_append_option( + ir_fc, named_fc->name->str, option_ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(option_ir_fc); + } + + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_array_to_ir(struct ctx *ctx, + struct ctf_field_class_array *fc) +{ + bt_field_class *ir_fc; + bt_field_class *elem_ir_fc; + + if (fc->base.is_text) { + ir_fc = bt_field_class_string_create(ctx->ir_tc); + BT_ASSERT(ir_fc); + goto end; + } + + elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc); + BT_ASSERT(elem_ir_fc); + ir_fc = bt_field_class_static_array_create(ctx->ir_tc, elem_ir_fc, + fc->length); + BT_ASSERT(ir_fc); + bt_field_class_put_ref(elem_ir_fc); + +end: + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_sequence_to_ir(struct ctx *ctx, + struct ctf_field_class_sequence *fc) +{ + int ret; + bt_field_class *ir_fc; + bt_field_class *elem_ir_fc; + + if (fc->base.is_text) { + ir_fc = bt_field_class_string_create(ctx->ir_tc); + BT_ASSERT(ir_fc); + goto end; + } + + elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc); + BT_ASSERT(elem_ir_fc); + ir_fc = bt_field_class_dynamic_array_create(ctx->ir_tc, elem_ir_fc); + BT_ASSERT(ir_fc); + bt_field_class_put_ref(elem_ir_fc); + BT_ASSERT(ir_fc); + + if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER && + fc->length_path.root != CTF_SCOPE_EVENT_HEADER) { + ret = bt_field_class_dynamic_array_set_length_field_class( + ir_fc, borrow_ir_fc_from_field_path(ctx, &fc->length_path)); + BT_ASSERT(ret == 0); + } + +end: + return ir_fc; +} + +static inline +bt_field_class *ctf_field_class_to_ir(struct ctx *ctx, + struct ctf_field_class *fc) +{ + bt_field_class *ir_fc = NULL; + + BT_ASSERT(fc); + BT_ASSERT(fc->in_ir); + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + ir_fc = ctf_field_class_int_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ENUM: + ir_fc = ctf_field_class_enum_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_FLOAT: + ir_fc = ctf_field_class_float_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRING: + ir_fc = ctf_field_class_string_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRUCT: + ir_fc = ctf_field_class_struct_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ARRAY: + ir_fc = ctf_field_class_array_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + ir_fc = ctf_field_class_sequence_to_ir(ctx, (void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_VARIANT: + ir_fc = ctf_field_class_variant_to_ir(ctx, (void *) fc); + break; + default: + abort(); + } + + fc->ir_fc = ir_fc; + return ir_fc; +} + +static inline +bool ctf_field_class_struct_has_immediate_member_in_ir( + struct ctf_field_class_struct *fc) +{ + uint64_t i; + bool has_immediate_member_in_ir = false; + + /* + * If the structure field class has no members at all, then it + * was an empty structure in the beginning, so leave it existing + * and empty. + */ + if (fc->members->len == 0) { + has_immediate_member_in_ir = true; + goto end; + } + + for (i = 0; i < fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index(fc, i); + + if (named_fc->fc->in_ir) { + has_immediate_member_in_ir = true; + goto end; + } + } + +end: + return has_immediate_member_in_ir; +} + +static inline +bt_field_class *scope_ctf_field_class_to_ir(struct ctx *ctx) +{ + bt_field_class *ir_fc = NULL; + struct ctf_field_class *fc = NULL; + + switch (ctx->scope) { + case CTF_SCOPE_PACKET_CONTEXT: + fc = ctx->sc->packet_context_fc; + break; + case CTF_SCOPE_EVENT_COMMON_CONTEXT: + fc = ctx->sc->event_common_context_fc; + break; + case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: + fc = ctx->ec->spec_context_fc; + break; + case CTF_SCOPE_EVENT_PAYLOAD: + fc = ctx->ec->payload_fc; + break; + default: + abort(); + } + + if (fc && ctf_field_class_struct_has_immediate_member_in_ir( + (void *) fc)) { + ir_fc = ctf_field_class_to_ir(ctx, fc); + } + + return ir_fc; +} + +static inline +void ctf_event_class_to_ir(struct ctx *ctx) +{ + int ret; + bt_event_class *ir_ec = NULL; + bt_field_class *ir_fc; + + BT_ASSERT(ctx->ec); + + if (ctx->ec->is_translated) { + ir_ec = bt_stream_class_borrow_event_class_by_id( + ctx->ir_sc, ctx->ec->id); + BT_ASSERT(ir_ec); + goto end; + } + + ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id); + BT_ASSERT(ir_ec); + bt_event_class_put_ref(ir_ec); + ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT; + ir_fc = scope_ctf_field_class_to_ir(ctx); + if (ir_fc) { + ret = bt_event_class_set_specific_context_field_class( + ir_ec, ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(ir_fc); + } + + ctx->scope = CTF_SCOPE_EVENT_PAYLOAD; + ir_fc = scope_ctf_field_class_to_ir(ctx); + if (ir_fc) { + ret = bt_event_class_set_payload_field_class(ir_ec, + ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(ir_fc); + } + + if (ctx->ec->name->len > 0) { + ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str); + BT_ASSERT(ret == 0); + } + + if (ctx->ec->emf_uri->len > 0) { + ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str); + BT_ASSERT(ret == 0); + } + + if (ctx->ec->log_level != -1) { + bt_event_class_set_log_level(ir_ec, ctx->ec->log_level); + } + + ctx->ec->is_translated = true; + ctx->ec->ir_ec = ir_ec; + +end: + return; +} + + +static inline +void ctf_stream_class_to_ir(struct ctx *ctx) +{ + int ret; + bt_field_class *ir_fc; + + BT_ASSERT(ctx->sc); + + if (ctx->sc->is_translated) { + ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id( + ctx->ir_tc, ctx->sc->id); + BT_ASSERT(ctx->ir_sc); + goto end; + } + + ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id); + BT_ASSERT(ctx->ir_sc); + bt_stream_class_put_ref(ctx->ir_sc); + ctx->scope = CTF_SCOPE_PACKET_CONTEXT; + ir_fc = scope_ctf_field_class_to_ir(ctx); + if (ir_fc) { + ret = bt_stream_class_set_packet_context_field_class( + ctx->ir_sc, ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(ir_fc); + } + + ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT; + ir_fc = scope_ctf_field_class_to_ir(ctx); + if (ir_fc) { + ret = bt_stream_class_set_event_common_context_field_class( + ctx->ir_sc, ir_fc); + BT_ASSERT(ret == 0); + bt_field_class_put_ref(ir_fc); + } + + bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc, + BT_FALSE); + bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE); + + if (ctx->sc->default_clock_class) { + BT_ASSERT(ctx->sc->default_clock_class->ir_cc); + ret = bt_stream_class_set_default_clock_class(ctx->ir_sc, + ctx->sc->default_clock_class->ir_cc); + BT_ASSERT(ret == 0); + } + + bt_stream_class_set_packets_have_beginning_default_clock_snapshot( + ctx->ir_sc, ctx->sc->packets_have_ts_begin); + bt_stream_class_set_packets_have_end_default_clock_snapshot( + ctx->ir_sc, ctx->sc->packets_have_ts_end); + bt_stream_class_set_supports_discarded_events(ctx->ir_sc, + ctx->sc->has_discarded_events, + ctx->sc->discarded_events_have_default_cs); + bt_stream_class_set_supports_discarded_packets(ctx->ir_sc, + ctx->sc->has_discarded_packets, + ctx->sc->discarded_packets_have_default_cs); + ctx->sc->is_translated = true; + ctx->sc->ir_sc = ctx->ir_sc; + +end: + return; +} + +static inline +void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc) +{ + int ret; + + if (strlen(cc->name->str) > 0) { + ret = bt_clock_class_set_name(ir_cc, cc->name->str); + BT_ASSERT(ret == 0); + } + + if (strlen(cc->description->str) > 0) { + ret = bt_clock_class_set_description(ir_cc, cc->description->str); + BT_ASSERT(ret == 0); + } + + bt_clock_class_set_frequency(ir_cc, cc->frequency); + bt_clock_class_set_precision(ir_cc, cc->precision); + bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles); + + if (cc->has_uuid) { + bt_clock_class_set_uuid(ir_cc, cc->uuid); + } + + bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute); +} + +static inline +int ctf_trace_class_to_ir(struct ctx *ctx) +{ + int ret = 0; + uint64_t i; + + BT_ASSERT(ctx->tc); + BT_ASSERT(ctx->ir_tc); + + if (ctx->tc->is_translated) { + goto end; + } + + if (ctx->tc->is_uuid_set) { + bt_trace_class_set_uuid(ctx->ir_tc, ctx->tc->uuid); + } + + for (i = 0; i < ctx->tc->env_entries->len; i++) { + struct ctf_trace_class_env_entry *env_entry = + ctf_trace_class_borrow_env_entry_by_index(ctx->tc, i); + + switch (env_entry->type) { + case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT: + ret = bt_trace_class_set_environment_entry_integer( + ctx->ir_tc, env_entry->name->str, + env_entry->value.i); + break; + case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR: + ret = bt_trace_class_set_environment_entry_string( + ctx->ir_tc, env_entry->name->str, + env_entry->value.str->str); + break; + default: + abort(); + } + + if (ret) { + goto end; + } + } + + for (i = 0; i < ctx->tc->clock_classes->len; i++) { + struct ctf_clock_class *cc = ctx->tc->clock_classes->pdata[i]; + + cc->ir_cc = bt_clock_class_create( + bt_self_component_source_as_self_component( + ctx->self_comp)); + ctf_clock_class_to_ir(cc->ir_cc, cc); + } + + bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc, + BT_FALSE); + ctx->tc->is_translated = true; + ctx->tc->ir_tc = ctx->ir_tc; + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_translate(bt_self_component_source *self_comp, + bt_trace_class *ir_tc, struct ctf_trace_class *tc) +{ + int ret = 0; + uint64_t i; + struct ctx ctx = { 0 }; + + ctx.self_comp = self_comp; + ctx.tc = tc; + ctx.ir_tc = ir_tc; + ret = ctf_trace_class_to_ir(&ctx); + if (ret) { + goto end; + } + + for (i = 0; i < tc->stream_classes->len; i++) { + uint64_t j; + ctx.sc = tc->stream_classes->pdata[i]; + + ctf_stream_class_to_ir(&ctx); + + for (j = 0; j < ctx.sc->event_classes->len; j++) { + ctx.ec = ctx.sc->event_classes->pdata[j]; + + ctf_event_class_to_ir(&ctx); + ctx.ec = NULL; + } + + ctx.sc = NULL; + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c b/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c new file mode 100644 index 00000000..f114d80b --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c @@ -0,0 +1,203 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-DEF-CC" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static inline +int find_mapped_clock_class(struct ctf_field_class *fc, + struct ctf_clock_class **clock_class) +{ + int ret = 0; + uint64_t i; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + { + struct ctf_field_class_int *int_fc = (void *) fc; + + if (int_fc->mapped_clock_class) { + if (*clock_class && *clock_class != + int_fc->mapped_clock_class) { + BT_LOGE("Stream class contains more than one " + "clock class: expected-cc-name=\"%s\", " + "other-cc-name=\"%s\"", + (*clock_class)->name->str, + int_fc->mapped_clock_class->name->str); + ret = -1; + goto end; + } + + *clock_class = int_fc->mapped_clock_class; + } + + break; + } + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + ret = find_mapped_clock_class(named_fc->fc, + clock_class); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + ret = find_mapped_clock_class(named_fc->fc, + clock_class); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + ret = find_mapped_clock_class(array_fc->elem_fc, clock_class); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +static inline +int update_stream_class_default_clock_class( + struct ctf_stream_class *stream_class) +{ + int ret = 0; + struct ctf_clock_class *clock_class = + stream_class->default_clock_class; + uint64_t i; + + ret = find_mapped_clock_class(stream_class->packet_context_fc, + &clock_class); + if (ret) { + goto end; + } + + ret = find_mapped_clock_class(stream_class->event_header_fc, + &clock_class); + if (ret) { + goto end; + } + + ret = find_mapped_clock_class(stream_class->event_common_context_fc, + &clock_class); + if (ret) { + goto end; + } + + for (i = 0; i < stream_class->event_classes->len; i++) { + struct ctf_event_class *event_class = + stream_class->event_classes->pdata[i]; + + ret = find_mapped_clock_class(event_class->spec_context_fc, + &clock_class); + if (ret) { + goto end; + } + + ret = find_mapped_clock_class(event_class->payload_fc, + &clock_class); + if (ret) { + goto end; + } + } + + if (!stream_class->default_clock_class) { + stream_class->default_clock_class = clock_class; + } + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc) +{ + uint64_t i; + int ret = 0; + struct ctf_clock_class *clock_class = NULL; + + ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class); + if (ret) { + goto end; + } + + if (clock_class) { + ret = -1; + goto end; + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = + ctf_tc->stream_classes->pdata[i]; + + ret = update_stream_class_default_clock_class( + ctf_tc->stream_classes->pdata[i]); + if (ret) { + BT_LOGE("Stream class contains more than one " + "clock class: stream-class-id=%" PRIu64, + sc->id); + goto end; + } + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c b/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c new file mode 100644 index 00000000..e6342942 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c @@ -0,0 +1,293 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-IN-IR" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include "compat/glib.h" +#include +#include +#include +#include +#include "common/assert.h" + +#include "ctf-meta-visitors.h" + +static +void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir) +{ + uint64_t i; + + if (!fc) { + goto end; + } + + fc->in_ir = in_ir; + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + force_update_field_class_in_ir(named_fc->fc, in_ir); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_named_field_class *named_fc; + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + force_update_field_class_in_ir(named_fc->fc, in_ir); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + force_update_field_class_in_ir(array_fc->elem_fc, in_ir); + break; + } + default: + break; + } + +end: + return; +} + +static +void update_field_class_in_ir(struct ctf_field_class *fc, + GHashTable *ft_dependents) +{ + int64_t i; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + { + struct ctf_field_class_int *int_fc = (void *) fc; + + /* + * Conditions to be in trace IR; one of: + * + * 1. Does NOT have a mapped clock class AND does not + * have a special meaning. + * 2. Another field class depends on it. + */ + if ((!int_fc->mapped_clock_class && + int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) || + bt_g_hash_table_contains(ft_dependents, fc)) { + fc->in_ir = true; + } + + break; + } + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + /* + * Make it part of IR if it's empty because it was + * originally empty. + */ + if (struct_fc->members->len == 0) { + fc->in_ir = true; + } + + /* Reverse order */ + for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + update_field_class_in_ir(named_fc->fc, ft_dependents); + + if (named_fc->fc->in_ir) { + /* At least one member is part of IR */ + fc->in_ir = true; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_named_field_class *named_fc; + struct ctf_field_class_variant *var_fc = (void *) fc; + + /* + * Reverse order, although it is not important for this + * loop because a field class within a variant field + * type's option cannot depend on a field class in + * another option of the same variant field class. + */ + for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) { + named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + update_field_class_in_ir(named_fc->fc, ft_dependents); + + if (named_fc->fc->in_ir) { + /* At least one option is part of IR */ + fc->in_ir = true; + } + } + + if (fc->in_ir) { + /* + * At least one option will make it to IR. In + * this case, make all options part of IR + * because the variant's tag could still select + * (dynamically) a removed option. This can mean + * having an empty structure as an option, for + * example, but at least all the options are + * selectable. + */ + for (i = 0; i < var_fc->options->len; i++) { + ctf_field_class_variant_borrow_option_by_index( + var_fc, i)->fc->in_ir = true; + } + + /* + * This variant field class is part of IR and + * depends on a tag field class (which must also + * be part of IR). + */ + g_hash_table_insert(ft_dependents, var_fc->tag_fc, + var_fc->tag_fc); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + update_field_class_in_ir(array_fc->elem_fc, ft_dependents); + fc->in_ir = array_fc->elem_fc->in_ir; + + if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) { + struct ctf_field_class_array *arr_fc = (void *) fc; + + assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE || + arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID); + + /* + * UUID field class: nothing depends on this, so + * it's not part of IR. + */ + if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) { + fc->in_ir = false; + array_fc->elem_fc->in_ir = false; + } + } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { + if (fc->in_ir) { + struct ctf_field_class_sequence *seq_fc = (void *) fc; + + /* + * This sequence field class is part of + * IR and depends on a length field class + * (which must also be part of IR). + */ + g_hash_table_insert(ft_dependents, + seq_fc->length_fc, seq_fc->length_fc); + } + } + + break; + } + default: + fc->in_ir = true; + break; + } + +end: + return; +} + +/* + * Scopes and field classes are processed in reverse order because we need + * to know if a given integer field class has dependents (sequence or + * variant field classes) when we reach it. Dependents can only be located + * after the length/tag field class in the metadata tree. + */ +BT_HIDDEN +int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc) +{ + int ret = 0; + uint64_t i; + + GHashTable *ft_dependents = g_hash_table_new(g_direct_hash, + g_direct_equal); + + BT_ASSERT(ft_dependents); + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; + uint64_t j; + + for (j = 0; j < sc->event_classes->len; j++) { + struct ctf_event_class *ec = sc->event_classes->pdata[j]; + + if (ec->is_translated) { + continue; + } + + update_field_class_in_ir(ec->payload_fc, ft_dependents); + update_field_class_in_ir(ec->spec_context_fc, + ft_dependents); + } + + if (!sc->is_translated) { + update_field_class_in_ir(sc->event_common_context_fc, + ft_dependents); + force_update_field_class_in_ir(sc->event_header_fc, + false); + update_field_class_in_ir(sc->packet_context_fc, + ft_dependents); + } + } + + if (!ctf_tc->is_translated) { + force_update_field_class_in_ir(ctf_tc->packet_header_fc, + false); + } + + g_hash_table_destroy(ft_dependents); + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c b/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c new file mode 100644 index 00000000..bd801979 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c @@ -0,0 +1,232 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-MEANINGS" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static +int set_int_field_class_meaning_by_name(struct ctf_field_class *fc, + const char *field_name, const char *id_name, + enum ctf_field_class_meaning meaning) +{ + int ret = 0; + uint64_t i; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + { + struct ctf_field_class_int *int_fc = (void *) fc; + + if (field_name && strcmp(field_name, id_name) == 0) { + int_fc->meaning = meaning; + } + + break; + } + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + ret = set_int_field_class_meaning_by_name(named_fc->fc, + named_fc->name->str, id_name, meaning); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + ret = set_int_field_class_meaning_by_name(named_fc->fc, + NULL, id_name, meaning); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + ret = set_int_field_class_meaning_by_name(array_fc->elem_fc, + NULL, id_name, meaning); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +static +int update_stream_class_meanings(struct ctf_stream_class *sc) +{ + int ret = 0; + struct ctf_field_class_int *int_fc; + uint64_t i; + + if (!sc->is_translated) { + if (sc->packet_context_fc) { + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_begin"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_end"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME; + + /* + * Remove mapped clock class to avoid updating + * the clock immediately when decoding. + */ + int_fc->mapped_clock_class = NULL; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "events_discarded"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "packet_seq_num"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT; + + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "packet_size"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "content_size"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE; + } + } + + if (sc->event_header_fc) { + ret = set_int_field_class_meaning_by_name( + sc->event_header_fc, NULL, "id", + CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID); + if (ret) { + goto end; + } + } + } + + for (i = 0; i < sc->event_classes->len; i++) { + struct ctf_event_class *ec = sc->event_classes->pdata[i]; + + if (ec->is_translated) { + continue; + } + } + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc) +{ + int ret = 0; + struct ctf_field_class_int *int_fc; + struct ctf_named_field_class *named_fc; + uint64_t i; + + if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) { + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) ctf_tc->packet_header_fc, "magic"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) ctf_tc->packet_header_fc, "stream_id"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) ctf_tc->packet_header_fc, + "stream_instance_id"); + if (int_fc) { + int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID; + } + + named_fc = ctf_field_class_struct_borrow_member_by_name( + (void *) ctf_tc->packet_header_fc, "uuid"); + if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) { + struct ctf_field_class_array *array_fc = + (void *) named_fc->fc; + + array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID; + } + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + ret = update_stream_class_meanings( + ctf_tc->stream_classes->pdata[i]); + if (ret) { + goto end; + } + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c b/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c new file mode 100644 index 00000000..d5dd9d93 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c @@ -0,0 +1,83 @@ +/* + * Copyright 2019 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-SC-CONFIG" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +BT_HIDDEN +int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc) +{ + struct ctf_field_class_int *int_fc; + uint64_t i; + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = + ctf_tc->stream_classes->pdata[i]; + + if (sc->is_translated) { + continue; + } + + if (!sc->packet_context_fc) { + continue; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_begin"); + if (int_fc && int_fc->meaning == + CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) { + sc->packets_have_ts_begin = true; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_end"); + if (int_fc && int_fc->meaning == + CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) { + sc->packets_have_ts_end = true; + } + + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "events_discarded"); + if (int_fc && int_fc->meaning == + CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) { + sc->has_discarded_events = true; + } + + sc->discarded_events_have_default_cs = + sc->has_discarded_events && sc->packets_have_ts_begin && + sc->packets_have_ts_end; + int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name( + (void *) sc->packet_context_fc, "packet_seq_num"); + if (int_fc && int_fc->meaning == + CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) { + sc->has_discarded_packets = true; + } + + sc->discarded_packets_have_default_cs = + sc->has_discarded_packets && + sc->packets_have_ts_begin && sc->packets_have_ts_end; + } + + return 0; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c b/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c new file mode 100644 index 00000000..8201954d --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c @@ -0,0 +1,176 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-TEXT-ARRAY-SEQ" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static inline +int set_text_array_sequence_field_class(struct ctf_field_class *fc) +{ + int ret = 0; + uint64_t i; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + ret = set_text_array_sequence_field_class(named_fc->fc); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + ret = set_text_array_sequence_field_class(named_fc->fc); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT || + array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) { + struct ctf_field_class_int *int_fc = + (void *) array_fc->elem_fc; + + if (int_fc->base.base.alignment == 8 && + int_fc->base.size == 8 && + int_fc->encoding == CTF_ENCODING_UTF8) { + array_fc->is_text = true; + + /* + * Force integer element to be unsigned; + * this makes the decoder enter a single + * path when reading a text + * array/sequence and we can safely + * decode bytes as characters anyway. + */ + int_fc->is_signed = false; + } + } + + ret = set_text_array_sequence_field_class(array_fc->elem_fc); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc) +{ + int ret = 0; + uint64_t i; + + if (!ctf_tc->is_translated) { + ret = set_text_array_sequence_field_class( + ctf_tc->packet_header_fc); + if (ret) { + goto end; + } + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; + uint64_t j; + + if (!sc->is_translated) { + ret = set_text_array_sequence_field_class( + sc->packet_context_fc); + if (ret) { + goto end; + } + + ret = set_text_array_sequence_field_class( + sc->event_header_fc); + if (ret) { + goto end; + } + + ret = set_text_array_sequence_field_class( + sc->event_common_context_fc); + if (ret) { + goto end; + } + } + + for (j = 0; j < sc->event_classes->len; j++) { + struct ctf_event_class *ec = + sc->event_classes->pdata[j]; + + if (ec->is_translated) { + continue; + } + + ret = set_text_array_sequence_field_class( + ec->spec_context_fc); + if (ret) { + goto end; + } + + ret = set_text_array_sequence_field_class( + ec->payload_fc); + if (ret) { + goto end; + } + } + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c b/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c new file mode 100644 index 00000000..81f76382 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c @@ -0,0 +1,177 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-UPDATE-VALUE-STORING-INDEXES" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static +int update_field_class_stored_value_index(struct ctf_field_class *fc, + struct ctf_trace_class *tc, + struct ctf_stream_class *sc, + struct ctf_event_class *ec) +{ + int ret = 0; + uint64_t i; + struct ctf_field_path *field_path = NULL; + struct ctf_field_class_int *tgt_fc = NULL; + uint64_t *stored_value_index = NULL; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + field_path = &var_fc->tag_path; + stored_value_index = &var_fc->stored_tag_index; + tgt_fc = (void *) var_fc->tag_fc; + break; + } + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_sequence *seq_fc = (void *) fc; + + field_path = &seq_fc->length_path; + stored_value_index = &seq_fc->stored_length_index; + tgt_fc = seq_fc->length_fc; + break; + } + default: + break; + } + + if (field_path) { + BT_ASSERT(tgt_fc); + BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT || + tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM); + if (tgt_fc->storing_index >= 0) { + /* Already storing its value */ + *stored_value_index = (uint64_t) tgt_fc->storing_index; + } else { + /* Not storing its value: allocate new index */ + tgt_fc->storing_index = tc->stored_value_count; + *stored_value_index = (uint64_t) tgt_fc->storing_index; + tc->stored_value_count++; + } + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + ret = update_field_class_stored_value_index(named_fc->fc, + tc, sc, ec); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + ret = update_field_class_stored_value_index(named_fc->fc, + tc, sc, ec); + if (ret) { + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + ret = update_field_class_stored_value_index(array_fc->elem_fc, + tc, sc, ec); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc) +{ + uint64_t i; + + if (!ctf_tc->is_translated) { + update_field_class_stored_value_index( + ctf_tc->packet_header_fc, ctf_tc, NULL, NULL); + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + uint64_t j; + struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; + + if (!sc->is_translated) { + update_field_class_stored_value_index(sc->packet_context_fc, + ctf_tc, sc, NULL); + update_field_class_stored_value_index(sc->event_header_fc, + ctf_tc, sc, NULL); + update_field_class_stored_value_index( + sc->event_common_context_fc, ctf_tc, sc, NULL); + } + + for (j = 0; j < sc->event_classes->len; j++) { + struct ctf_event_class *ec = + sc->event_classes->pdata[j]; + + if (!ec->is_translated) { + update_field_class_stored_value_index( + ec->spec_context_fc, ctf_tc, sc, ec); + update_field_class_stored_value_index( + ec->payload_fc, ctf_tc, sc, ec); + } + } + } + + return 0; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-validate.c b/src/plugins/ctf/common/metadata/ctf-meta-validate.c new file mode 100644 index 00000000..bb1aac0c --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-validate.c @@ -0,0 +1,349 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-VALIDATE" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static +int validate_stream_class(struct ctf_stream_class *sc) +{ + int ret = 0; + struct ctf_field_class_int *int_fc; + struct ctf_field_class *fc; + + if (sc->is_translated) { + goto end; + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_begin"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`timestamp_begin` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`timestamp_begin` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "timestamp_end"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`timestamp_end` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`timestamp_end` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "events_discarded"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`events_discarded` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`events_discarded` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "packet_seq_num"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`packet_seq_num` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`packet_seq_num` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "packet_size"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`packet_size` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`packet_size` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->packet_context_fc, "content_size"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet context field class: " + "`content_size` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet context field class: " + "`content_size` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) sc->event_header_fc, "id"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid event header field class: " + "`id` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid event header field class: " + "`id` member is signed."); + goto invalid; + } + } else { + if (sc->event_classes->len > 1) { + BT_LOGE_STR("Invalid event header field class: " + "missing `id` member as there's " + "more than one event class."); + goto invalid; + } + } + + goto end; + +invalid: + ret = -1; + +end: + return ret; +} + +BT_HIDDEN +int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc) +{ + int ret = 0; + struct ctf_field_class_int *int_fc; + uint64_t i; + + if (!ctf_tc->is_translated) { + struct ctf_field_class *fc; + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) ctf_tc->packet_header_fc, "magic"); + if (fc) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + (void *) ctf_tc->packet_header_fc, + 0); + + if (named_fc->fc != fc) { + BT_LOGE_STR("Invalid packet header field class: " + "`magic` member is not the first member."); + goto invalid; + } + + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet header field class: " + "`magic` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet header field class: " + "`magic` member is signed."); + goto invalid; + } + + if (int_fc->base.size != 32) { + BT_LOGE_STR("Invalid packet header field class: " + "`magic` member is not 32-bit."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) ctf_tc->packet_header_fc, "stream_id"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet header field class: " + "`stream_id` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet header field class: " + "`stream_id` member is signed."); + goto invalid; + } + } else { + if (ctf_tc->stream_classes->len > 1) { + BT_LOGE_STR("Invalid packet header field class: " + "missing `stream_id` member as there's " + "more than one stream class."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) ctf_tc->packet_header_fc, + "stream_instance_id"); + if (fc) { + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + BT_LOGE_STR("Invalid packet header field class: " + "`stream_instance_id` member is not an integer field class."); + goto invalid; + } + + int_fc = (void *) fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet header field class: " + "`stream_instance_id` member is signed."); + goto invalid; + } + } + + fc = ctf_field_class_struct_borrow_member_field_class_by_name( + (void *) ctf_tc->packet_header_fc, "uuid"); + if (fc) { + struct ctf_field_class_array *array_fc = (void *) fc; + + if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member is not an array field class."); + goto invalid; + } + + array_fc = (void *) fc; + + if (array_fc->length != 16) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member is not a 16-element array field class."); + goto invalid; + } + + if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member's element field class is not " + "an integer field class."); + goto invalid; + } + + int_fc = (void *) array_fc->base.elem_fc; + + if (int_fc->is_signed) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member's element field class " + "is a signed integer field class."); + goto invalid; + } + + if (int_fc->base.size != 8) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member's element field class " + "is not an 8-bit integer field class."); + goto invalid; + } + + if (int_fc->base.base.alignment != 8) { + BT_LOGE_STR("Invalid packet header field class: " + "`uuid` member's element field class's " + "alignment is not 8."); + goto invalid; + } + } + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = + ctf_tc->stream_classes->pdata[i]; + + ret = validate_stream_class(sc); + if (ret) { + BT_LOGE("Invalid stream class: sc-id=%" PRIu64, sc->id); + goto invalid; + } + } + + goto end; + +invalid: + ret = -1; + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta-visitors.h b/src/plugins/ctf/common/metadata/ctf-meta-visitors.h new file mode 100644 index 00000000..9eb3cf38 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-visitors.h @@ -0,0 +1,56 @@ +#ifndef _CTF_META_VISITORS_H +#define _CTF_META_VISITORS_H + +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include "common/babeltrace.h" + +#include "ctf-meta.h" + +BT_HIDDEN +int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc); + +BT_HIDDEN +int ctf_trace_class_translate(bt_self_component_source *self_comp, + bt_trace_class *ir_tc, struct ctf_trace_class *tc); + +BT_HIDDEN +int ctf_trace_class_update_default_clock_classes( + struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc); + +BT_HIDDEN +void ctf_trace_class_warn_meaningless_header_fields( + struct ctf_trace_class *ctf_tc); + +#endif /* _CTF_META_VISITORS_H */ diff --git a/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c b/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c new file mode 100644 index 00000000..73b6fe92 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c @@ -0,0 +1,136 @@ +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-META-WARN-MEANINGLESS-HEADER-FIELDS" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "ctf-meta-visitors.h" + +static inline +void warn_meaningless_field(const char *name, const char *scope_name) +{ + BT_LOGW("User field found in %s: ignoring: name=\"%s\"", + scope_name, name); +} + +static inline +void warn_meaningless_fields(struct ctf_field_class *fc, const char *name, + const char *scope_name) +{ + uint64_t i; + + if (!fc) { + goto end; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_FLOAT: + case CTF_FIELD_CLASS_TYPE_STRING: + warn_meaningless_field(name, scope_name); + break; + case CTF_FIELD_CLASS_TYPE_INT: + case CTF_FIELD_CLASS_TYPE_ENUM: + { + struct ctf_field_class_int *int_fc = (void *) fc; + + if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE && + !int_fc->mapped_clock_class) { + warn_meaningless_field(name, scope_name); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + warn_meaningless_fields(named_fc->fc, + named_fc->name->str, scope_name); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + warn_meaningless_fields(named_fc->fc, + named_fc->name->str, scope_name); + } + + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + { + struct ctf_field_class_array *array_fc = (void *) fc; + + if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) { + goto end; + } + + } + /* fall-through */ + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + warn_meaningless_fields(array_fc->elem_fc, name, scope_name); + break; + } + default: + abort(); + } + +end: + return; +} + +BT_HIDDEN +void ctf_trace_class_warn_meaningless_header_fields( + struct ctf_trace_class *ctf_tc) +{ + uint64_t i; + + if (!ctf_tc->is_translated) { + warn_meaningless_fields( + ctf_tc->packet_header_fc, NULL, "packet header"); + } + + for (i = 0; i < ctf_tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i]; + + if (!sc->is_translated) { + warn_meaningless_fields(sc->event_header_fc, NULL, + "event header"); + } + } +} diff --git a/src/plugins/ctf/common/metadata/ctf-meta.h b/src/plugins/ctf/common/metadata/ctf-meta.h new file mode 100644 index 00000000..29c5e178 --- /dev/null +++ b/src/plugins/ctf/common/metadata/ctf-meta.h @@ -0,0 +1,1680 @@ +#ifndef _CTF_META_H +#define _CTF_META_H + +/* + * Copyright 2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include "common/common.h" +#include "common/assert.h" +#include +#include +#include + +enum ctf_field_class_type { + CTF_FIELD_CLASS_TYPE_INT, + CTF_FIELD_CLASS_TYPE_ENUM, + CTF_FIELD_CLASS_TYPE_FLOAT, + CTF_FIELD_CLASS_TYPE_STRING, + CTF_FIELD_CLASS_TYPE_STRUCT, + CTF_FIELD_CLASS_TYPE_ARRAY, + CTF_FIELD_CLASS_TYPE_SEQUENCE, + CTF_FIELD_CLASS_TYPE_VARIANT, +}; + +enum ctf_field_class_meaning { + CTF_FIELD_CLASS_MEANING_NONE, + CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME, + CTF_FIELD_CLASS_MEANING_PACKET_END_TIME, + CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID, + CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID, + CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID, + CTF_FIELD_CLASS_MEANING_MAGIC, + CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT, + CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT, + CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE, + CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE, + CTF_FIELD_CLASS_MEANING_UUID, +}; + +enum ctf_byte_order { + CTF_BYTE_ORDER_DEFAULT, + CTF_BYTE_ORDER_LITTLE, + CTF_BYTE_ORDER_BIG, +}; + +enum ctf_encoding { + CTF_ENCODING_NONE, + CTF_ENCODING_UTF8, +}; + +enum ctf_scope { + CTF_SCOPE_PACKET_HEADER, + CTF_SCOPE_PACKET_CONTEXT, + CTF_SCOPE_EVENT_HEADER, + CTF_SCOPE_EVENT_COMMON_CONTEXT, + CTF_SCOPE_EVENT_SPECIFIC_CONTEXT, + CTF_SCOPE_EVENT_PAYLOAD, +}; + +struct ctf_clock_class { + GString *name; + GString *description; + uint64_t frequency; + uint64_t precision; + int64_t offset_seconds; + uint64_t offset_cycles; + uint8_t uuid[16]; + bool has_uuid; + bool is_absolute; + + /* Weak, set during translation */ + bt_clock_class *ir_cc; +}; + +struct ctf_field_class { + enum ctf_field_class_type type; + unsigned int alignment; + bool is_compound; + bool in_ir; + + /* Weak, set during translation. NULL if `in_ir` is false below. */ + bt_field_class *ir_fc; +}; + +struct ctf_field_class_bit_array { + struct ctf_field_class base; + enum ctf_byte_order byte_order; + unsigned int size; +}; + +struct ctf_field_class_int { + struct ctf_field_class_bit_array base; + enum ctf_field_class_meaning meaning; + bool is_signed; + bt_field_class_integer_preferred_display_base disp_base; + enum ctf_encoding encoding; + int64_t storing_index; + + /* Weak */ + struct ctf_clock_class *mapped_clock_class; +}; + +struct ctf_range { + union { + uint64_t u; + int64_t i; + } lower; + + union { + uint64_t u; + int64_t i; + } upper; +}; + +struct ctf_field_class_enum_mapping { + GString *label; + struct ctf_range range; +}; + +struct ctf_field_class_enum { + struct ctf_field_class_int base; + + /* Array of `struct ctf_field_class_enum_mapping` */ + GArray *mappings; +}; + +struct ctf_field_class_float { + struct ctf_field_class_bit_array base; +}; + +struct ctf_field_class_string { + struct ctf_field_class base; + enum ctf_encoding encoding; +}; + +struct ctf_named_field_class { + GString *name; + + /* Owned by this */ + struct ctf_field_class *fc; +}; + +struct ctf_field_class_struct { + struct ctf_field_class base; + + /* Array of `struct ctf_named_field_class` */ + GArray *members; +}; + +struct ctf_field_path { + enum ctf_scope root; + + /* Array of `int64_t` */ + GArray *path; +}; + +struct ctf_field_class_variant_range { + struct ctf_range range; + uint64_t option_index; +}; + +struct ctf_field_class_variant { + struct ctf_field_class base; + GString *tag_ref; + struct ctf_field_path tag_path; + uint64_t stored_tag_index; + + /* Array of `struct ctf_named_field_class` */ + GArray *options; + + /* Array of `struct ctf_field_class_variant_range` */ + GArray *ranges; + + /* Weak */ + struct ctf_field_class_enum *tag_fc; +}; + +struct ctf_field_class_array_base { + struct ctf_field_class base; + struct ctf_field_class *elem_fc; + bool is_text; +}; + +struct ctf_field_class_array { + struct ctf_field_class_array_base base; + enum ctf_field_class_meaning meaning; + uint64_t length; +}; + +struct ctf_field_class_sequence { + struct ctf_field_class_array_base base; + GString *length_ref; + struct ctf_field_path length_path; + uint64_t stored_length_index; + + /* Weak */ + struct ctf_field_class_int *length_fc; +}; + +struct ctf_event_class { + GString *name; + uint64_t id; + GString *emf_uri; + bt_event_class_log_level log_level; + bool is_translated; + + /* Owned by this */ + struct ctf_field_class *spec_context_fc; + + /* Owned by this */ + struct ctf_field_class *payload_fc; + + /* Weak, set during translation */ + bt_event_class *ir_ec; +}; + +struct ctf_stream_class { + uint64_t id; + bool is_translated; + bool packets_have_ts_begin; + bool packets_have_ts_end; + bool has_discarded_events; + bool has_discarded_packets; + bool discarded_events_have_default_cs; + bool discarded_packets_have_default_cs; + + /* Owned by this */ + struct ctf_field_class *packet_context_fc; + + /* Owned by this */ + struct ctf_field_class *event_header_fc; + + /* Owned by this */ + struct ctf_field_class *event_common_context_fc; + + /* Array of `struct ctf_event_class *`, owned by this */ + GPtrArray *event_classes; + + /* + * Hash table mapping event class IDs to `struct ctf_event_class *`, + * weak. + */ + GHashTable *event_classes_by_id; + + /* Weak */ + struct ctf_clock_class *default_clock_class; + + /* Weak, set during translation */ + bt_stream_class *ir_sc; +}; + +enum ctf_trace_class_env_entry_type { + CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT, + CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR, +}; + +struct ctf_trace_class_env_entry { + enum ctf_trace_class_env_entry_type type; + GString *name; + + struct { + int64_t i; + GString *str; + } value; +}; + +struct ctf_trace_class { + unsigned int major; + unsigned int minor; + uint8_t uuid[16]; + bool is_uuid_set; + enum ctf_byte_order default_byte_order; + + /* Owned by this */ + struct ctf_field_class *packet_header_fc; + + uint64_t stored_value_count; + + /* Array of `struct ctf_clock_class *` (owned by this) */ + GPtrArray *clock_classes; + + /* Array of `struct ctf_stream_class *` */ + GPtrArray *stream_classes; + + /* Array of `struct ctf_trace_class_env_entry` */ + GArray *env_entries; + + bool is_translated; + + /* Weak, set during translation */ + bt_trace_class *ir_tc; +}; + +static inline +void ctf_field_class_destroy(struct ctf_field_class *fc); + +static inline +void _ctf_field_class_init(struct ctf_field_class *fc, + enum ctf_field_class_type type, unsigned int alignment) +{ + BT_ASSERT(fc); + fc->type = type; + fc->alignment = alignment; + fc->in_ir = false; +} + +static inline +void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc, + enum ctf_field_class_type type) +{ + _ctf_field_class_init((void *) fc, type, 1); +} + +static inline +void _ctf_field_class_int_init(struct ctf_field_class_int *fc, + enum ctf_field_class_type type) +{ + _ctf_field_class_bit_array_init((void *) fc, type); + fc->meaning = CTF_FIELD_CLASS_MEANING_NONE; + fc->storing_index = -1; +} + +static inline +void ctf_field_path_init(struct ctf_field_path *field_path) +{ + BT_ASSERT(field_path); + field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t)); + BT_ASSERT(field_path->path); +} + +static inline +void ctf_field_path_fini(struct ctf_field_path *field_path) +{ + BT_ASSERT(field_path); + + if (field_path->path) { + g_array_free(field_path->path, TRUE); + } +} + +static inline +void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + named_fc->name = g_string_new(NULL); + BT_ASSERT(named_fc->name); +} + +static inline +void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + + if (named_fc->name) { + g_string_free(named_fc->name, TRUE); + } + + ctf_field_class_destroy(named_fc->fc); +} + +static inline +void _ctf_field_class_enum_mapping_init( + struct ctf_field_class_enum_mapping *mapping) +{ + BT_ASSERT(mapping); + mapping->label = g_string_new(NULL); + BT_ASSERT(mapping->label); +} + +static inline +void _ctf_field_class_enum_mapping_fini( + struct ctf_field_class_enum_mapping *mapping) +{ + BT_ASSERT(mapping); + + if (mapping->label) { + g_string_free(mapping->label, TRUE); + } +} + +static inline +struct ctf_field_class_int *ctf_field_class_int_create(void) +{ + struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1); + + BT_ASSERT(fc); + _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT); + return fc; +} + +static inline +struct ctf_field_class_float *ctf_field_class_float_create(void) +{ + struct ctf_field_class_float *fc = + g_new0(struct ctf_field_class_float, 1); + + BT_ASSERT(fc); + _ctf_field_class_bit_array_init((void *) fc, CTF_FIELD_CLASS_TYPE_FLOAT); + return fc; +} + +static inline +struct ctf_field_class_string *ctf_field_class_string_create(void) +{ + struct ctf_field_class_string *fc = + g_new0(struct ctf_field_class_string, 1); + + BT_ASSERT(fc); + _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRING, 8); + return fc; +} + +static inline +struct ctf_field_class_enum *ctf_field_class_enum_create(void) +{ + struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1); + + BT_ASSERT(fc); + _ctf_field_class_int_init((void *) fc, CTF_FIELD_CLASS_TYPE_ENUM); + fc->mappings = g_array_new(FALSE, TRUE, + sizeof(struct ctf_field_class_enum_mapping)); + BT_ASSERT(fc->mappings); + return fc; +} + +static inline +struct ctf_field_class_struct *ctf_field_class_struct_create(void) +{ + struct ctf_field_class_struct *fc = + g_new0(struct ctf_field_class_struct, 1); + + BT_ASSERT(fc); + _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRUCT, 1); + fc->members = g_array_new(FALSE, TRUE, + sizeof(struct ctf_named_field_class)); + BT_ASSERT(fc->members); + fc->base.is_compound = true; + return fc; +} + +static inline +struct ctf_field_class_variant *ctf_field_class_variant_create(void) +{ + struct ctf_field_class_variant *fc = + g_new0(struct ctf_field_class_variant, 1); + + BT_ASSERT(fc); + _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_VARIANT, 1); + fc->options = g_array_new(FALSE, TRUE, + sizeof(struct ctf_named_field_class)); + BT_ASSERT(fc->options); + fc->ranges = g_array_new(FALSE, TRUE, + sizeof(struct ctf_field_class_variant_range)); + BT_ASSERT(fc->ranges); + fc->tag_ref = g_string_new(NULL); + BT_ASSERT(fc->tag_ref); + ctf_field_path_init(&fc->tag_path); + fc->base.is_compound = true; + return fc; +} + +static inline +struct ctf_field_class_array *ctf_field_class_array_create(void) +{ + struct ctf_field_class_array *fc = + g_new0(struct ctf_field_class_array, 1); + + BT_ASSERT(fc); + _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_ARRAY, 1); + fc->base.base.is_compound = true; + return fc; +} + +static inline +struct ctf_field_class_sequence *ctf_field_class_sequence_create(void) +{ + struct ctf_field_class_sequence *fc = + g_new0(struct ctf_field_class_sequence, 1); + + BT_ASSERT(fc); + _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1); + fc->length_ref = g_string_new(NULL); + BT_ASSERT(fc->length_ref); + ctf_field_path_init(&fc->length_path); + fc->base.base.is_compound = true; + return fc; +} + +static inline +void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc) +{ + BT_ASSERT(fc); + g_free(fc); +} + +static inline +void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc) +{ + BT_ASSERT(fc); + + if (fc->mappings) { + uint64_t i; + + for (i = 0; i < fc->mappings->len; i++) { + struct ctf_field_class_enum_mapping *mapping = + &g_array_index(fc->mappings, + struct ctf_field_class_enum_mapping, i); + + _ctf_field_class_enum_mapping_fini(mapping); + } + + g_array_free(fc->mappings, TRUE); + } + + g_free(fc); +} + +static inline +void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc) +{ + BT_ASSERT(fc); + g_free(fc); +} + +static inline +void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc) +{ + BT_ASSERT(fc); + g_free(fc); +} + +static inline +void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc) +{ + BT_ASSERT(fc); + + if (fc->members) { + uint64_t i; + + for (i = 0; i < fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + &g_array_index(fc->members, + struct ctf_named_field_class, i); + + _ctf_named_field_class_fini(named_fc); + } + + g_array_free(fc->members, TRUE); + } + + g_free(fc); +} + +static inline +void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc) +{ + BT_ASSERT(fc); + ctf_field_class_destroy(fc->elem_fc); +} + +static inline +void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc) +{ + BT_ASSERT(fc); + _ctf_field_class_array_base_fini((void *) fc); + g_free(fc); +} + +static inline +void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc) +{ + BT_ASSERT(fc); + _ctf_field_class_array_base_fini((void *) fc); + + if (fc->length_ref) { + g_string_free(fc->length_ref, TRUE); + } + + ctf_field_path_fini(&fc->length_path); + g_free(fc); +} + +static inline +void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc) +{ + BT_ASSERT(fc); + + if (fc->options) { + uint64_t i; + + for (i = 0; i < fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + &g_array_index(fc->options, + struct ctf_named_field_class, i); + + _ctf_named_field_class_fini(named_fc); + } + + g_array_free(fc->options, TRUE); + } + + if (fc->ranges) { + g_array_free(fc->ranges, TRUE); + } + + if (fc->tag_ref) { + g_string_free(fc->tag_ref, TRUE); + } + + ctf_field_path_fini(&fc->tag_path); + g_free(fc); +} + +static inline +void ctf_field_class_destroy(struct ctf_field_class *fc) +{ + if (!fc) { + return; + } + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + _ctf_field_class_int_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ENUM: + _ctf_field_class_enum_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_FLOAT: + _ctf_field_class_float_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRING: + _ctf_field_class_string_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRUCT: + _ctf_field_class_struct_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ARRAY: + _ctf_field_class_array_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + _ctf_field_class_sequence_destroy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_VARIANT: + _ctf_field_class_variant_destroy((void *) fc); + break; + default: + abort(); + } +} + +static inline +void ctf_field_class_enum_append_mapping(struct ctf_field_class_enum *fc, + const char *label, uint64_t u_lower, uint64_t u_upper) +{ + struct ctf_field_class_enum_mapping *mapping; + + BT_ASSERT(fc); + BT_ASSERT(label); + g_array_set_size(fc->mappings, fc->mappings->len + 1); + + mapping = &g_array_index(fc->mappings, + struct ctf_field_class_enum_mapping, fc->mappings->len - 1); + _ctf_field_class_enum_mapping_init(mapping); + g_string_assign(mapping->label, label); + mapping->range.lower.u = u_lower; + mapping->range.upper.u = u_upper; +} + +static inline +struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_index( + struct ctf_field_class_enum *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->mappings->len); + return &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping, + index); +} + +static inline +struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_index( + struct ctf_field_class_struct *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->members->len); + return &g_array_index(fc->members, struct ctf_named_field_class, + index); +} + +static inline +struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_name( + struct ctf_field_class_struct *fc, const char *name) +{ + uint64_t i; + struct ctf_named_field_class *ret_named_fc = NULL; + + BT_ASSERT(fc); + BT_ASSERT(name); + + for (i = 0; i < fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index(fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_named_fc = named_fc; + goto end; + } + } + +end: + return ret_named_fc; +} + +static inline +struct ctf_field_class *ctf_field_class_struct_borrow_member_field_class_by_name( + struct ctf_field_class_struct *struct_fc, const char *name) +{ + struct ctf_named_field_class *named_fc = NULL; + struct ctf_field_class *fc = NULL; + + if (!struct_fc) { + goto end; + } + + named_fc = ctf_field_class_struct_borrow_member_by_name(struct_fc, name); + if (!named_fc) { + goto end; + } + + fc = named_fc->fc; + +end: + return fc; +} + +static inline +struct ctf_field_class_int * +ctf_field_class_struct_borrow_member_int_field_class_by_name( + struct ctf_field_class_struct *struct_fc, const char *name) +{ + struct ctf_field_class_int *int_fc = NULL; + + int_fc = (void *) + ctf_field_class_struct_borrow_member_field_class_by_name( + struct_fc, name); + if (!int_fc) { + goto end; + } + + if (int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_INT && + int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_ENUM) { + int_fc = NULL; + goto end; + } + +end: + return int_fc; +} + + +static inline +void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc, + const char *name, struct ctf_field_class *member_fc) +{ + struct ctf_named_field_class *named_fc; + + BT_ASSERT(fc); + BT_ASSERT(name); + g_array_set_size(fc->members, fc->members->len + 1); + + named_fc = &g_array_index(fc->members, struct ctf_named_field_class, + fc->members->len - 1); + _ctf_named_field_class_init(named_fc); + g_string_assign(named_fc->name, name); + named_fc->fc = member_fc; + + if (member_fc->alignment > fc->base.alignment) { + fc->base.alignment = member_fc->alignment; + } +} + +static inline +struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_index( + struct ctf_field_class_variant *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->options->len); + return &g_array_index(fc->options, struct ctf_named_field_class, + index); +} + +static inline +struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_name( + struct ctf_field_class_variant *fc, const char *name) +{ + uint64_t i; + struct ctf_named_field_class *ret_named_fc = NULL; + + BT_ASSERT(fc); + BT_ASSERT(name); + + for (i = 0; i < fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index(fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_named_fc = named_fc; + goto end; + } + } + +end: + return ret_named_fc; +} + +static inline +struct ctf_field_class_variant_range * +ctf_field_class_variant_borrow_range_by_index( + struct ctf_field_class_variant *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->ranges->len); + return &g_array_index(fc->ranges, struct ctf_field_class_variant_range, + index); +} + +static inline +void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc, + const char *name, struct ctf_field_class *option_fc) +{ + struct ctf_named_field_class *named_fc; + + BT_ASSERT(fc); + BT_ASSERT(name); + g_array_set_size(fc->options, fc->options->len + 1); + + named_fc = &g_array_index(fc->options, struct ctf_named_field_class, + fc->options->len - 1); + _ctf_named_field_class_init(named_fc); + g_string_assign(named_fc->name, name); + named_fc->fc = option_fc; +} + +static inline +void ctf_field_class_variant_set_tag_field_class( + struct ctf_field_class_variant *fc, + struct ctf_field_class_enum *tag_fc) +{ + uint64_t option_i; + + BT_ASSERT(fc); + BT_ASSERT(tag_fc); + fc->tag_fc = tag_fc; + + for (option_i = 0; option_i < fc->options->len; option_i++) { + uint64_t mapping_i; + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + fc, option_i); + + for (mapping_i = 0; mapping_i < tag_fc->mappings->len; + mapping_i++) { + struct ctf_field_class_enum_mapping *mapping = + ctf_field_class_enum_borrow_mapping_by_index( + tag_fc, mapping_i); + + if (strcmp(named_fc->name->str, + mapping->label->str) == 0) { + struct ctf_field_class_variant_range range; + + range.range = mapping->range; + range.option_index = option_i; + g_array_append_val(fc->ranges, range); + } + } + } +} + +static inline +struct ctf_field_class *ctf_field_class_compound_borrow_field_class_by_index( + struct ctf_field_class *comp_fc, uint64_t index) +{ + struct ctf_field_class *fc = NULL; + + switch (comp_fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + (void *) comp_fc, index); + + BT_ASSERT(named_fc); + fc = named_fc->fc; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + (void *) comp_fc, index); + + BT_ASSERT(named_fc); + fc = named_fc->fc; + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct ctf_field_class_array_base *array_fc = (void *) comp_fc; + + fc = array_fc->elem_fc; + break; + } + default: + break; + } + + return fc; +} + +static inline +uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc) +{ + uint64_t field_count; + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + field_count = struct_fc->members->len; + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + field_count = var_fc->options->len; + break; + } + case CTF_FIELD_CLASS_TYPE_ARRAY: + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + /* + * Array and sequence types always contain a single + * member (the element type). + */ + field_count = 1; + break; + default: + abort(); + } + + return field_count; +} + +static inline +int64_t ctf_field_class_compound_get_field_class_index_from_name( + struct ctf_field_class *fc, const char *name) +{ + int64_t ret_index = -1; + uint64_t i; + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_STRUCT: + { + struct ctf_field_class_struct *struct_fc = (void *) fc; + + for (i = 0; i < struct_fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_index = (int64_t) i; + goto end; + } + } + + break; + } + case CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct ctf_field_class_variant *var_fc = (void *) fc; + + for (i = 0; i < var_fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_index = (int64_t) i; + goto end; + } + } + + break; + } + default: + break; + } + +end: + return ret_index; +} + +static inline +void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index) +{ + BT_ASSERT(fp); + g_array_append_val(fp->path, index); +} + +static inline +int64_t ctf_field_path_borrow_index_by_index(struct ctf_field_path *fp, + uint64_t index) +{ + BT_ASSERT(fp); + BT_ASSERT(index < fp->path->len); + return g_array_index(fp->path, int64_t, index); +} + +static inline +void ctf_field_path_clear(struct ctf_field_path *fp) +{ + BT_ASSERT(fp); + g_array_set_size(fp->path, 0); +} + +static inline +const char *ctf_scope_string(enum ctf_scope scope) +{ + switch (scope) { + case CTF_SCOPE_PACKET_HEADER: + return "CTF_SCOPE_PACKET_HEADER"; + case CTF_SCOPE_PACKET_CONTEXT: + return "CTF_SCOPE_PACKET_CONTEXT"; + case CTF_SCOPE_EVENT_HEADER: + return "CTF_SCOPE_EVENT_HEADER"; + case CTF_SCOPE_EVENT_COMMON_CONTEXT: + return "CTF_SCOPE_EVENT_COMMON_CONTEXT"; + case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: + return "CTF_SCOPE_EVENT_SPECIFIC_CONTEXT"; + case CTF_SCOPE_EVENT_PAYLOAD: + return "CTF_SCOPE_EVENT_PAYLOAD"; + default: + abort(); + } +} + +static inline +GString *ctf_field_path_string(struct ctf_field_path *path) +{ + GString *str = g_string_new(NULL); + uint64_t i; + + BT_ASSERT(path); + + if (!str) { + goto end; + } + + g_string_append_printf(str, "[%s", ctf_scope_string(path->root)); + + for (i = 0; i < path->path->len; i++) { + g_string_append_printf(str, ", %" PRId64, + ctf_field_path_borrow_index_by_index(path, i)); + } + + g_string_append(str, "]"); + +end: + return str; +} + +static inline +struct ctf_field_class *ctf_field_path_borrow_field_class( + struct ctf_field_path *field_path, + struct ctf_trace_class *tc, + struct ctf_stream_class *sc, + struct ctf_event_class *ec) +{ + uint64_t i; + struct ctf_field_class *fc; + + switch (field_path->root) { + case CTF_SCOPE_PACKET_HEADER: + fc = tc->packet_header_fc; + break; + case CTF_SCOPE_PACKET_CONTEXT: + fc = sc->packet_context_fc; + break; + case CTF_SCOPE_EVENT_HEADER: + fc = sc->event_header_fc; + break; + case CTF_SCOPE_EVENT_COMMON_CONTEXT: + fc = sc->event_common_context_fc; + break; + case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT: + fc = ec->spec_context_fc; + break; + case CTF_SCOPE_EVENT_PAYLOAD: + fc = ec->payload_fc; + break; + default: + abort(); + } + + BT_ASSERT(fc); + + for (i = 0; i < field_path->path->len; i++) { + int64_t child_index = + ctf_field_path_borrow_index_by_index(field_path, i); + struct ctf_field_class *child_fc = + ctf_field_class_compound_borrow_field_class_by_index( + fc, child_index); + BT_ASSERT(child_fc); + fc = child_fc; + } + + BT_ASSERT(fc); + return fc; +} + +static inline +struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc); + +static inline +void ctf_field_class_bit_array_copy_content( + struct ctf_field_class_bit_array *dst_fc, + struct ctf_field_class_bit_array *src_fc) +{ + BT_ASSERT(dst_fc); + BT_ASSERT(src_fc); + dst_fc->byte_order = src_fc->byte_order; + dst_fc->size = src_fc->size; +} + +static inline +void ctf_field_class_int_copy_content( + struct ctf_field_class_int *dst_fc, + struct ctf_field_class_int *src_fc) +{ + ctf_field_class_bit_array_copy_content((void *) dst_fc, (void *) src_fc); + dst_fc->meaning = src_fc->meaning; + dst_fc->is_signed = src_fc->is_signed; + dst_fc->disp_base = src_fc->disp_base; + dst_fc->encoding = src_fc->encoding; + dst_fc->mapped_clock_class = src_fc->mapped_clock_class; + dst_fc->storing_index = src_fc->storing_index; +} + +static inline +struct ctf_field_class_int *_ctf_field_class_int_copy( + struct ctf_field_class_int *fc) +{ + struct ctf_field_class_int *copy_fc = ctf_field_class_int_create(); + + BT_ASSERT(copy_fc); + ctf_field_class_int_copy_content(copy_fc, fc); + return copy_fc; +} + +static inline +struct ctf_field_class_enum *_ctf_field_class_enum_copy( + struct ctf_field_class_enum *fc) +{ + struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create(); + uint64_t i; + + BT_ASSERT(copy_fc); + ctf_field_class_int_copy_content((void *) copy_fc, (void *) fc); + + for (i = 0; i < fc->mappings->len; i++) { + struct ctf_field_class_enum_mapping *mapping = + &g_array_index(fc->mappings, + struct ctf_field_class_enum_mapping, i); + + ctf_field_class_enum_append_mapping(copy_fc, mapping->label->str, + mapping->range.lower.u, mapping->range.upper.u); + } + + return copy_fc; +} + +static inline +struct ctf_field_class_float *_ctf_field_class_float_copy( + struct ctf_field_class_float *fc) +{ + struct ctf_field_class_float *copy_fc = ctf_field_class_float_create(); + + BT_ASSERT(copy_fc); + ctf_field_class_bit_array_copy_content((void *) copy_fc, (void *) fc); + return copy_fc; +} + +static inline +struct ctf_field_class_string *_ctf_field_class_string_copy( + struct ctf_field_class_string *fc) +{ + struct ctf_field_class_string *copy_fc = ctf_field_class_string_create(); + + BT_ASSERT(copy_fc); + return copy_fc; +} + +static inline +struct ctf_field_class_struct *_ctf_field_class_struct_copy( + struct ctf_field_class_struct *fc) +{ + struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create(); + uint64_t i; + + BT_ASSERT(copy_fc); + + for (i = 0; i < fc->members->len; i++) { + struct ctf_named_field_class *named_fc = + &g_array_index(fc->members, + struct ctf_named_field_class, i); + + ctf_field_class_struct_append_member(copy_fc, + named_fc->name->str, + ctf_field_class_copy(named_fc->fc)); + } + + return copy_fc; +} + +static inline +void ctf_field_path_copy_content(struct ctf_field_path *dst_fp, + struct ctf_field_path *src_fp) +{ + uint64_t i; + + BT_ASSERT(dst_fp); + BT_ASSERT(src_fp); + dst_fp->root = src_fp->root; + ctf_field_path_clear(dst_fp); + + for (i = 0; i < src_fp->path->len; i++) { + int64_t index = ctf_field_path_borrow_index_by_index( + src_fp, i); + + ctf_field_path_append_index(dst_fp, index); + } +} + +static inline +struct ctf_field_class_variant *_ctf_field_class_variant_copy( + struct ctf_field_class_variant *fc) +{ + struct ctf_field_class_variant *copy_fc = + ctf_field_class_variant_create(); + uint64_t i; + + BT_ASSERT(copy_fc); + + for (i = 0; i < fc->options->len; i++) { + struct ctf_named_field_class *named_fc = + &g_array_index(fc->options, + struct ctf_named_field_class, i); + + ctf_field_class_variant_append_option(copy_fc, + named_fc->name->str, + ctf_field_class_copy(named_fc->fc)); + } + + for (i = 0; i < fc->ranges->len; i++) { + struct ctf_field_class_variant_range *range = + &g_array_index(fc->ranges, + struct ctf_field_class_variant_range, i); + + g_array_append_val(copy_fc->ranges, *range); + } + + ctf_field_path_copy_content(©_fc->tag_path, &fc->tag_path); + g_string_assign(copy_fc->tag_ref, fc->tag_ref->str); + copy_fc->stored_tag_index = fc->stored_tag_index; + return copy_fc; +} + +static inline +void ctf_field_class_array_base_copy_content( + struct ctf_field_class_array_base *dst_fc, + struct ctf_field_class_array_base *src_fc) +{ + BT_ASSERT(dst_fc); + BT_ASSERT(src_fc); + dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc); + dst_fc->is_text = src_fc->is_text; +} + +static inline +struct ctf_field_class_array *_ctf_field_class_array_copy( + struct ctf_field_class_array *fc) +{ + struct ctf_field_class_array *copy_fc = ctf_field_class_array_create(); + + BT_ASSERT(copy_fc); + ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc); + copy_fc->length = fc->length; + return copy_fc; +} + +static inline +struct ctf_field_class_sequence *_ctf_field_class_sequence_copy( + struct ctf_field_class_sequence *fc) +{ + struct ctf_field_class_sequence *copy_fc = + ctf_field_class_sequence_create(); + + BT_ASSERT(copy_fc); + ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc); + ctf_field_path_copy_content(©_fc->length_path, &fc->length_path); + g_string_assign(copy_fc->length_ref, fc->length_ref->str); + copy_fc->stored_length_index = fc->stored_length_index; + return copy_fc; +} + +static inline +struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc) +{ + struct ctf_field_class *copy_fc = NULL; + + if (!fc) { + goto end; + } + + /* + * Translation should not have happened yet. + */ + BT_ASSERT(!fc->ir_fc); + + switch (fc->type) { + case CTF_FIELD_CLASS_TYPE_INT: + copy_fc = (void *) _ctf_field_class_int_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ENUM: + copy_fc = (void *) _ctf_field_class_enum_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_FLOAT: + copy_fc = (void *) _ctf_field_class_float_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRING: + copy_fc = (void *) _ctf_field_class_string_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_STRUCT: + copy_fc = (void *) _ctf_field_class_struct_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_ARRAY: + copy_fc = (void *) _ctf_field_class_array_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_SEQUENCE: + copy_fc = (void *) _ctf_field_class_sequence_copy((void *) fc); + break; + case CTF_FIELD_CLASS_TYPE_VARIANT: + copy_fc = (void *) _ctf_field_class_variant_copy((void *) fc); + break; + default: + abort(); + } + + copy_fc->type = fc->type; + copy_fc->alignment = fc->alignment; + copy_fc->in_ir = fc->in_ir; + +end: + return copy_fc; +} + +static inline +struct ctf_event_class *ctf_event_class_create(void) +{ + struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1); + + BT_ASSERT(ec); + ec->name = g_string_new(NULL); + BT_ASSERT(ec->name); + ec->emf_uri = g_string_new(NULL); + BT_ASSERT(ec->emf_uri); + ec->log_level = -1; + return ec; +} + +static inline +void ctf_event_class_destroy(struct ctf_event_class *ec) +{ + if (!ec) { + return; + } + + if (ec->name) { + g_string_free(ec->name, TRUE); + } + + if (ec->emf_uri) { + g_string_free(ec->emf_uri, TRUE); + } + + ctf_field_class_destroy(ec->spec_context_fc); + ctf_field_class_destroy(ec->payload_fc); + g_free(ec); +} + +static inline +struct ctf_stream_class *ctf_stream_class_create(void) +{ + struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1); + + BT_ASSERT(sc); + sc->event_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_event_class_destroy); + BT_ASSERT(sc->event_classes); + sc->event_classes_by_id = g_hash_table_new(g_direct_hash, + g_direct_equal); + BT_ASSERT(sc->event_classes_by_id); + return sc; +} + +static inline +void ctf_stream_class_destroy(struct ctf_stream_class *sc) +{ + if (!sc) { + return; + } + + if (sc->event_classes) { + g_ptr_array_free(sc->event_classes, TRUE); + } + + if (sc->event_classes_by_id) { + g_hash_table_destroy(sc->event_classes_by_id); + } + + ctf_field_class_destroy(sc->packet_context_fc); + ctf_field_class_destroy(sc->event_header_fc); + ctf_field_class_destroy(sc->event_common_context_fc); + g_free(sc); +} + +static inline +void ctf_stream_class_append_event_class(struct ctf_stream_class *sc, + struct ctf_event_class *ec) +{ + g_ptr_array_add(sc->event_classes, ec); + g_hash_table_insert(sc->event_classes_by_id, + GUINT_TO_POINTER((guint) ec->id), ec); +} + +static inline +struct ctf_event_class *ctf_stream_class_borrow_event_class_by_id( + struct ctf_stream_class *sc, uint64_t type) +{ + BT_ASSERT(sc); + return g_hash_table_lookup(sc->event_classes_by_id, + GUINT_TO_POINTER((guint) type)); +} + +static inline +void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry) +{ + BT_ASSERT(entry); + entry->name = g_string_new(NULL); + BT_ASSERT(entry->name); + entry->value.str = g_string_new(NULL); + BT_ASSERT(entry->value.str); +} + +static inline +void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry) +{ + BT_ASSERT(entry); + + if (entry->name) { + g_string_free(entry->name, TRUE); + } + + if (entry->value.str) { + g_string_free(entry->value.str, TRUE); + } +} + +static inline +struct ctf_clock_class *ctf_clock_class_create(void) +{ + struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1); + + BT_ASSERT(cc); + cc->name = g_string_new(NULL); + BT_ASSERT(cc->name); + cc->description = g_string_new(NULL); + BT_ASSERT(cc->description); + return cc; +} + +static inline +void ctf_clock_class_destroy(struct ctf_clock_class *cc) +{ + if (!cc) { + return; + } + + if (cc->name) { + g_string_free(cc->name, TRUE); + } + + if (cc->description) { + g_string_free(cc->description, TRUE); + } + + bt_clock_class_put_ref(cc->ir_cc); + g_free(cc); +} + +static inline +struct ctf_trace_class *ctf_trace_class_create(void) +{ + struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1); + + BT_ASSERT(tc); + tc->default_byte_order = -1; + tc->clock_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_clock_class_destroy); + BT_ASSERT(tc->clock_classes); + tc->stream_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_stream_class_destroy); + BT_ASSERT(tc->stream_classes); + tc->env_entries = g_array_new(FALSE, TRUE, + sizeof(struct ctf_trace_class_env_entry)); + return tc; +} + +static inline +void ctf_trace_class_destroy(struct ctf_trace_class *tc) +{ + if (!tc) { + return; + } + + ctf_field_class_destroy(tc->packet_header_fc); + + if (tc->clock_classes) { + g_ptr_array_free(tc->clock_classes, TRUE); + } + + if (tc->stream_classes) { + g_ptr_array_free(tc->stream_classes, TRUE); + } + + if (tc->env_entries) { + uint64_t i; + + for (i = 0; i < tc->env_entries->len; i++) { + struct ctf_trace_class_env_entry *entry = + &g_array_index(tc->env_entries, + struct ctf_trace_class_env_entry, i); + + _ctf_trace_class_env_entry_fini(entry); + } + + g_array_free(tc->env_entries, TRUE); + } + + g_free(tc); +} + +static inline +void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc, + const char *name, enum ctf_trace_class_env_entry_type type, + const char *str_value, int64_t i_value) +{ + struct ctf_trace_class_env_entry *entry; + + BT_ASSERT(tc); + BT_ASSERT(name); + g_array_set_size(tc->env_entries, tc->env_entries->len + 1); + + entry = &g_array_index(tc->env_entries, + struct ctf_trace_class_env_entry, tc->env_entries->len - 1); + entry->type = type; + _ctf_trace_class_env_entry_init(entry); + g_string_assign(entry->name, name); + + if (str_value) { + g_string_assign(entry->value.str, str_value); + } + + entry->value.i = i_value; +} + +static inline +struct ctf_stream_class *ctf_trace_class_borrow_stream_class_by_id( + struct ctf_trace_class *tc, uint64_t id) +{ + uint64_t i; + struct ctf_stream_class *ret_sc = NULL; + + BT_ASSERT(tc); + + for (i = 0; i < tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = tc->stream_classes->pdata[i]; + + if (sc->id == id) { + ret_sc = sc; + goto end; + } + } + +end: + return ret_sc; +} + +static inline +struct ctf_clock_class *ctf_trace_class_borrow_clock_class_by_name( + struct ctf_trace_class *tc, const char *name) +{ + uint64_t i; + struct ctf_clock_class *ret_cc = NULL; + + BT_ASSERT(tc); + BT_ASSERT(name); + + for (i = 0; i < tc->clock_classes->len; i++) { + struct ctf_clock_class *cc = tc->clock_classes->pdata[i]; + + BT_ASSERT(cc->name); + if (strcmp(cc->name->str, name) == 0) { + ret_cc = cc; + goto end; + } + } + +end: + return ret_cc; +} + +static inline +struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_index( + struct ctf_trace_class *tc, uint64_t index) +{ + BT_ASSERT(tc); + BT_ASSERT(index < tc->env_entries->len); + return &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry, + index); +} + +static inline +struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_name( + struct ctf_trace_class *tc, const char *name) +{ + struct ctf_trace_class_env_entry *ret_entry = NULL; + uint64_t i; + + BT_ASSERT(tc); + BT_ASSERT(name); + + for (i = 0; i < tc->env_entries->len; i++) { + struct ctf_trace_class_env_entry *env_entry = + ctf_trace_class_borrow_env_entry_by_index(tc, i); + + if (strcmp(env_entry->name->str, name) == 0) { + ret_entry = env_entry; + goto end; + } + } + +end: + return ret_entry; +} + +#endif /* _CTF_META_H */ diff --git a/src/plugins/ctf/common/metadata/decoder.c b/src/plugins/ctf/common/metadata/decoder.c new file mode 100644 index 00000000..53749a7f --- /dev/null +++ b/src/plugins/ctf/common/metadata/decoder.c @@ -0,0 +1,564 @@ +/* + * Copyright 2016-2017 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-DECODER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include "common/assert.h" +#include "compat/uuid.h" +#include "compat/memstream.h" +#include +#include +#include + +#include "ast.h" +#include "decoder.h" +#include "scanner.h" + +#define TSDL_MAGIC 0x75d11d57 + +extern +int yydebug; + +struct ctf_metadata_decoder { + struct ctf_visitor_generate_ir *visitor; + uint8_t uuid[16]; + bool is_uuid_set; + int bo; + struct ctf_metadata_decoder_config config; +}; + +struct packet_header { + uint32_t magic; + uint8_t uuid[16]; + uint32_t checksum; + uint32_t content_size; + uint32_t packet_size; + uint8_t compression_scheme; + uint8_t encryption_scheme; + uint8_t checksum_scheme; + uint8_t major; + uint8_t minor; +} __attribute__((__packed__)); + +BT_HIDDEN +bool ctf_metadata_decoder_is_packetized(FILE *fp, int *byte_order) +{ + uint32_t magic; + size_t len; + int ret = 0; + + len = fread(&magic, sizeof(magic), 1, fp); + if (len != 1) { + BT_LOGD_STR("Cannot reade first metadata packet header: assuming the stream is not packetized."); + goto end; + } + + if (byte_order) { + if (magic == TSDL_MAGIC) { + ret = 1; + *byte_order = BYTE_ORDER; + } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) { + ret = 1; + *byte_order = BYTE_ORDER == BIG_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN; + } + } + +end: + rewind(fp); + + return ret; +} + +static +bool is_version_valid(unsigned int major, unsigned int minor) +{ + return major == 1 && minor == 8; +} + +static +int decode_packet(struct ctf_metadata_decoder *mdec, FILE *in_fp, FILE *out_fp, + int byte_order) +{ + struct packet_header header; + size_t readlen, writelen, toread; + uint8_t buf[512 + 1]; /* + 1 for debug-mode \0 */ + int ret = 0; + const long offset = ftell(in_fp); + + if (offset < 0) { + BT_LOGE_ERRNO("Failed to get current metadata file position", + "."); + goto error; + } + BT_LOGV("Decoding metadata packet: mdec-addr=%p, offset=%ld", + mdec, offset); + readlen = fread(&header, sizeof(header), 1, in_fp); + if (feof(in_fp) != 0) { + BT_LOGV("Reached end of file: offset=%ld", ftell(in_fp)); + goto end; + } + if (readlen < 1) { + BT_LOGV("Cannot decode metadata packet: offset=%ld", offset); + goto error; + } + + if (byte_order != BYTE_ORDER) { + header.magic = GUINT32_SWAP_LE_BE(header.magic); + header.checksum = GUINT32_SWAP_LE_BE(header.checksum); + header.content_size = GUINT32_SWAP_LE_BE(header.content_size); + header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size); + } + + if (header.compression_scheme) { + BT_LOGE("Metadata packet compression is not supported as of this version: " + "compression-scheme=%u, offset=%ld", + (unsigned int) header.compression_scheme, offset); + goto error; + } + + if (header.encryption_scheme) { + BT_LOGE("Metadata packet encryption is not supported as of this version: " + "encryption-scheme=%u, offset=%ld", + (unsigned int) header.encryption_scheme, offset); + goto error; + } + + if (header.checksum || header.checksum_scheme) { + BT_LOGE("Metadata packet checksum verification is not supported as of this version: " + "checksum-scheme=%u, checksum=%x, offset=%ld", + (unsigned int) header.checksum_scheme, header.checksum, + offset); + goto error; + } + + if (!is_version_valid(header.major, header.minor)) { + BT_LOGE("Invalid metadata packet version: " + "version=%u.%u, offset=%ld", + header.major, header.minor, offset); + goto error; + } + + /* Set expected trace UUID if not set; otherwise validate it */ + if (mdec) { + if (!mdec->is_uuid_set) { + memcpy(mdec->uuid, header.uuid, sizeof(header.uuid)); + mdec->is_uuid_set = true; + } else if (bt_uuid_compare(header.uuid, mdec->uuid)) { + BT_LOGE("Metadata UUID mismatch between packets of the same stream: " + "packet-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " + "expected-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " + "offset=%ld", + (unsigned int) header.uuid[0], + (unsigned int) header.uuid[1], + (unsigned int) header.uuid[2], + (unsigned int) header.uuid[3], + (unsigned int) header.uuid[4], + (unsigned int) header.uuid[5], + (unsigned int) header.uuid[6], + (unsigned int) header.uuid[7], + (unsigned int) header.uuid[8], + (unsigned int) header.uuid[9], + (unsigned int) header.uuid[10], + (unsigned int) header.uuid[11], + (unsigned int) header.uuid[12], + (unsigned int) header.uuid[13], + (unsigned int) header.uuid[14], + (unsigned int) header.uuid[15], + (unsigned int) mdec->uuid[0], + (unsigned int) mdec->uuid[1], + (unsigned int) mdec->uuid[2], + (unsigned int) mdec->uuid[3], + (unsigned int) mdec->uuid[4], + (unsigned int) mdec->uuid[5], + (unsigned int) mdec->uuid[6], + (unsigned int) mdec->uuid[7], + (unsigned int) mdec->uuid[8], + (unsigned int) mdec->uuid[9], + (unsigned int) mdec->uuid[10], + (unsigned int) mdec->uuid[11], + (unsigned int) mdec->uuid[12], + (unsigned int) mdec->uuid[13], + (unsigned int) mdec->uuid[14], + (unsigned int) mdec->uuid[15], + offset); + goto error; + } + } + + if ((header.content_size / CHAR_BIT) < sizeof(header)) { + BT_LOGE("Bad metadata packet content size: content-size=%u, " + "offset=%ld", header.content_size, offset); + goto error; + } + + toread = header.content_size / CHAR_BIT - sizeof(header); + + for (;;) { + size_t loop_read; + + loop_read = MIN(sizeof(buf) - 1, toread); + readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp); + if (ferror(in_fp)) { + BT_LOGE("Cannot read metadata packet buffer: " + "offset=%ld, read-size=%zu", + ftell(in_fp), loop_read); + goto error; + } + if (readlen > loop_read) { + BT_LOGE("fread returned more byte than expected: " + "read-size-asked=%zu, read-size-returned=%zu", + loop_read, readlen); + goto error; + } + + writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp); + if (writelen < readlen || ferror(out_fp)) { + BT_LOGE("Cannot write decoded metadata text to buffer: " + "read-offset=%ld, write-size=%zu", + ftell(in_fp), readlen); + goto error; + } + + toread -= readlen; + if (toread == 0) { + int fseek_ret; + + /* Read leftover padding */ + toread = (header.packet_size - header.content_size) / + CHAR_BIT; + fseek_ret = fseek(in_fp, toread, SEEK_CUR); + if (fseek_ret < 0) { + BT_LOGW_STR("Missing padding at the end of the metadata stream."); + } + break; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( + struct ctf_metadata_decoder *mdec, FILE *fp, + char **buf, int byte_order) +{ + FILE *out_fp; + size_t size; + int ret = 0; + int tret; + size_t packet_index = 0; + + out_fp = bt_open_memstream(buf, &size); + if (out_fp == NULL) { + BT_LOGE("Cannot open memory stream: %s: mdec-addr=%p", + strerror(errno), mdec); + goto error; + } + + for (;;) { + if (feof(fp) != 0) { + break; + } + + tret = decode_packet(mdec, fp, out_fp, byte_order); + if (tret) { + BT_LOGE("Cannot decode packet: index=%zu, mdec-addr=%p", + packet_index, mdec); + goto error; + } + + packet_index++; + } + + /* Make sure the whole string ends with a null character */ + tret = fputc('\0', out_fp); + if (tret == EOF) { + BT_LOGE("Cannot append '\\0' to the decoded metadata buffer: " + "mdec-addr=%p", mdec); + goto error; + } + + /* Close stream, which also flushes the buffer */ + ret = bt_close_memstream(buf, &size, out_fp); + /* + * See fclose(3). Further access to out_fp after both success + * and error, even through another bt_close_memstream(), results + * in undefined behavior. Nullify out_fp to ensure we don't + * fclose it twice on error. + */ + out_fp = NULL; + if (ret < 0) { + BT_LOGE("Cannot close memory stream: %s: mdec-addr=%p", + strerror(errno), mdec); + goto error; + } + + goto end; + +error: + ret = -1; + + if (out_fp) { + if (bt_close_memstream(buf, &size, out_fp)) { + BT_LOGE("Cannot close memory stream: %s: mdec-addr=%p", + strerror(errno), mdec); + } + } + + if (*buf) { + free(*buf); + *buf = NULL; + } + +end: + return ret; +} + +BT_HIDDEN +int ctf_metadata_decoder_packetized_file_stream_to_buf( + FILE *fp, char **buf, int byte_order) +{ + return ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( + NULL, fp, buf, byte_order); +} + +BT_HIDDEN +struct ctf_metadata_decoder *ctf_metadata_decoder_create( + bt_self_component_source *self_comp, + const struct ctf_metadata_decoder_config *config) +{ + struct ctf_metadata_decoder *mdec = + g_new0(struct ctf_metadata_decoder, 1); + struct ctf_metadata_decoder_config default_config = { + .clock_class_offset_s = 0, + .clock_class_offset_ns = 0, + }; + + if (!config) { + config = &default_config; + } + + BT_LOGD("Creating CTF metadata decoder: " + "clock-class-offset-s=%" PRId64 ", " + "clock-class-offset-ns=%" PRId64, + config->clock_class_offset_s, config->clock_class_offset_ns); + + if (!mdec) { + BT_LOGE_STR("Failed to allocate one CTF metadata decoder."); + goto end; + } + + mdec->config = *config; + mdec->visitor = ctf_visitor_generate_ir_create(self_comp, config); + if (!mdec->visitor) { + BT_LOGE("Failed to create a CTF IR metadata AST visitor: " + "mdec-addr=%p", mdec); + ctf_metadata_decoder_destroy(mdec); + mdec = NULL; + goto end; + } + + BT_LOGD("Creating CTF metadata decoder: " + "clock-class-offset-s=%" PRId64 ", " + "clock-class-offset-ns=%" PRId64 ", addr=%p", + config->clock_class_offset_s, config->clock_class_offset_ns, + mdec); + +end: + return mdec; +} + +BT_HIDDEN +void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec) +{ + if (!mdec) { + return; + } + + BT_LOGD("Destroying CTF metadata decoder: addr=%p", mdec); + ctf_visitor_generate_ir_destroy(mdec->visitor); + g_free(mdec); +} + +BT_HIDDEN +enum ctf_metadata_decoder_status ctf_metadata_decoder_decode( + struct ctf_metadata_decoder *mdec, FILE *fp) +{ + enum ctf_metadata_decoder_status status = + CTF_METADATA_DECODER_STATUS_OK; + int ret; + struct ctf_scanner *scanner = NULL; + char *buf = NULL; + bool close_fp = false; + + BT_ASSERT(mdec); + + if (ctf_metadata_decoder_is_packetized(fp, &mdec->bo)) { + BT_LOGD("Metadata stream is packetized: mdec-addr=%p", mdec); + ret = ctf_metadata_decoder_packetized_file_stream_to_buf_with_mdec( + mdec, fp, &buf, mdec->bo); + if (ret) { + BT_LOGE("Cannot decode packetized metadata packets to metadata text: " + "mdec-addr=%p, ret=%d", mdec, ret); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + + if (strlen(buf) == 0) { + /* An empty metadata packet is OK. */ + goto end; + } + + /* Convert the real file pointer to a memory file pointer */ + fp = bt_fmemopen(buf, strlen(buf), "rb"); + close_fp = true; + if (!fp) { + BT_LOGE("Cannot memory-open metadata buffer: %s: " + "mdec-addr=%p", strerror(errno), mdec); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + } else { + unsigned int major, minor; + ssize_t nr_items; + const long init_pos = ftell(fp); + + BT_LOGD("Metadata stream is plain text: mdec-addr=%p", mdec); + + if (init_pos < 0) { + BT_LOGE_ERRNO("Failed to get current file position", "."); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + + /* Check text-only metadata header and version */ + nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor); + if (nr_items < 2) { + BT_LOGW("Missing \"/* CTF major.minor\" signature in plain text metadata file stream: " + "mdec-addr=%p", mdec); + } + + BT_LOGD("Found metadata stream version in signature: version=%u.%u", major, minor); + + if (!is_version_valid(major, minor)) { + BT_LOGE("Invalid metadata version found in plain text signature: " + "version=%u.%u, mdec-addr=%p", major, minor, + mdec); + status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION; + goto end; + } + + if (fseek(fp, init_pos, SEEK_SET)) { + BT_LOGE("Cannot seek metadata file stream to initial position: %s: " + "mdec-addr=%p", strerror(errno), mdec); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + } + + if (BT_LOG_ON_VERBOSE) { + yydebug = 1; + } + + /* Allocate a scanner and append the metadata text content */ + scanner = ctf_scanner_alloc(); + if (!scanner) { + BT_LOGE("Cannot allocate a metadata lexical scanner: " + "mdec-addr=%p", mdec); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + + BT_ASSERT(fp); + ret = ctf_scanner_append_ast(scanner, fp); + if (ret) { + BT_LOGE("Cannot create the metadata AST out of the metadata text: " + "mdec-addr=%p", mdec); + status = CTF_METADATA_DECODER_STATUS_INCOMPLETE; + goto end; + } + + ret = ctf_visitor_semantic_check(0, &scanner->ast->root); + if (ret) { + BT_LOGE("Validation of the metadata semantics failed: " + "mdec-addr=%p", mdec); + status = CTF_METADATA_DECODER_STATUS_ERROR; + goto end; + } + + ret = ctf_visitor_generate_ir_visit_node(mdec->visitor, + &scanner->ast->root); + switch (ret) { + case 0: + /* Success */ + break; + case -EINCOMPLETE: + BT_LOGD("While visiting metadata AST: incomplete data: " + "mdec-addr=%p", mdec); + status = CTF_METADATA_DECODER_STATUS_INCOMPLETE; + goto end; + default: + BT_LOGE("Failed to visit AST node to create CTF IR objects: " + "mdec-addr=%p, ret=%d", mdec, ret); + status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR; + goto end; + } + +end: + if (scanner) { + ctf_scanner_free(scanner); + } + + yydebug = 0; + + if (fp && close_fp) { + if (fclose(fp)) { + BT_LOGE("Cannot close metadata file stream: " + "mdec-addr=%p", mdec); + } + } + + if (buf) { + free(buf); + } + + return status; +} + +BT_HIDDEN +bt_trace_class *ctf_metadata_decoder_get_ir_trace_class( + struct ctf_metadata_decoder *mdec) +{ + return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor); +} + +BT_HIDDEN +struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class( + struct ctf_metadata_decoder *mdec) +{ + return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor); +} diff --git a/src/plugins/ctf/common/metadata/decoder.h b/src/plugins/ctf/common/metadata/decoder.h new file mode 100644 index 00000000..b0f179be --- /dev/null +++ b/src/plugins/ctf/common/metadata/decoder.h @@ -0,0 +1,113 @@ +#ifndef _METADATA_DECODER_H +#define _METADATA_DECODER_H + +/* + * Copyright 2016-2017 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include + +#include + +/* A CTF metadata decoder object */ +struct ctf_metadata_decoder; + +/* CTF metadata decoder status */ +enum ctf_metadata_decoder_status { + CTF_METADATA_DECODER_STATUS_OK = 0, + CTF_METADATA_DECODER_STATUS_ERROR = -1, + CTF_METADATA_DECODER_STATUS_INCOMPLETE = -2, + CTF_METADATA_DECODER_STATUS_INVAL_VERSION = -3, + CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR = -4, +}; + +/* Decoding configuration */ +struct ctf_metadata_decoder_config { + int64_t clock_class_offset_s; + int64_t clock_class_offset_ns; +}; + +/* + * Creates a CTF metadata decoder. + * + * Returns `NULL` on error. + */ +BT_HIDDEN +struct ctf_metadata_decoder *ctf_metadata_decoder_create( + bt_self_component_source *self_comp, + const struct ctf_metadata_decoder_config *config); + +/* + * Destroys a CTF metadata decoder that you created with + * ctf_metadata_decoder_create(). + */ +BT_HIDDEN +void ctf_metadata_decoder_destroy( + struct ctf_metadata_decoder *metadata_decoder); + +/* + * Decodes a new chunk of CTF metadata. + * + * This function reads the metadata from the current position of `fp` + * until the end of this file stream. If it finds new information (new + * event class, new stream class, or new clock class), it appends this + * information to the decoder's trace object (as returned by + * ctf_metadata_decoder_get_ir_trace_class()), or it creates this trace. + * + * The metadata can be packetized or not. + * + * The metadata chunk needs to be complete and scannable, that is, + * zero or more complete top-level blocks. If it's incomplete, this + * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this + * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you + * need to call it again with the same metadata and more to make it + * complete. For example: + * + * First call: event { name = hell + * Second call: event { name = hello_world; ... }; + * + * If the conversion from the metadata text to CTF IR objects fails, + * this function returns `CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR`. + * + * If everything goes as expected, this function returns + * `CTF_METADATA_DECODER_STATUS_OK`. + */ +BT_HIDDEN +enum ctf_metadata_decoder_status ctf_metadata_decoder_decode( + struct ctf_metadata_decoder *metadata_decoder, FILE *fp); + +BT_HIDDEN +bt_trace_class *ctf_metadata_decoder_get_ir_trace_class( + struct ctf_metadata_decoder *mdec); + +BT_HIDDEN +struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class( + struct ctf_metadata_decoder *mdec); + +/* + * Checks whether or not a given metadata file stream is packetized, and + * if so, sets `*byte_order` to the byte order of the first packet. + */ +BT_HIDDEN +bool ctf_metadata_decoder_is_packetized(FILE *fp, int *byte_order); + +/* + * Decodes a packetized metadata file stream to a NULL-terminated + * text buffer using the given byte order. + */ +BT_HIDDEN +int ctf_metadata_decoder_packetized_file_stream_to_buf( + FILE *fp, char **buf, int byte_order); + +#endif /* _METADATA_DECODER_H */ diff --git a/src/plugins/ctf/common/metadata/lexer.l b/src/plugins/ctf/common/metadata/lexer.l new file mode 100644 index 00000000..694ddc3a --- /dev/null +++ b/src/plugins/ctf/common/metadata/lexer.l @@ -0,0 +1,148 @@ +%{ +/* + * lexer.l + * + * Common Trace Formal Lexer + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-LEXER" +#include "logging.h" + +#include +#include +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +#define YY_FATAL_ERROR(_msg) BT_LOGF_STR(_msg) + +#define PARSE_INTEGER_LITERAL(base) \ + do { \ + errno = 0; \ + yylval->ull = strtoull(yytext, NULL, base); \ + if (errno) { \ + _BT_LOGE_LINENO(yylineno, \ + "Cannot parser constant integer: " \ + "base=%d, text=\"%s\"", base, yytext); \ + return CTF_ERROR; \ + } \ + } while (0) + +BT_HIDDEN +void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src); + +static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) + __attribute__((unused)); +static int input (yyscan_t yyscanner) __attribute__((unused)); + +BT_HIDDEN +int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim); + +%} + +%x comment_ml comment_sl string_lit char_const +%option reentrant yylineno noyywrap bison-bridge +%option extra-type="struct ctf_scanner *" + /* bison-locations */ +INTEGER_SUFFIX (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu) +DIGIT [0-9] +NONDIGIT [a-zA-Z_] +HEXDIGIT [0-9A-Fa-f] +OCTALDIGIT [0-7] +UCHARLOWERCASE \\u{HEXDIGIT}{4} +UCHARUPPERCASE \\U{HEXDIGIT}{8} +ID_NONDIGIT {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE} +IDENTIFIER {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})* +%% + + /* + * Using start conditions to deal with comments + * and strings. + */ + +"/*" BEGIN(comment_ml); +[^*\n]* /* eat anything that's not a '*' */ +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +\n +"*"+"/" BEGIN(INITIAL); + +"//"[^\n]*\n /* skip comment */ + +L?\"(\\.|[^\\"])*\" { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; } +L?\'(\\.|[^\\'])*\' { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; } + +"[" return CTF_LSBRAC; +"]" return CTF_RSBRAC; +"(" return CTF_LPAREN; +")" return CTF_RPAREN; +"{" return CTF_LBRAC; +"}" return CTF_RBRAC; +"->" return CTF_RARROW; +"*" return CTF_STAR; +"+" return CTF_PLUS; +"-" return CTF_MINUS; +"<" return CTF_LT; +">" return CTF_GT; +:= return CTF_TYPEASSIGN; +: return CTF_COLON; +; return CTF_SEMICOLON; +"..." return CTF_DOTDOTDOT; +"." return CTF_DOT; += return CTF_EQUAL; +"," return CTF_COMMA; +align setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN; +const setstring(yyextra, yylval, yytext); return CTF_CONST; +char setstring(yyextra, yylval, yytext); return CTF_CHAR; +clock setstring(yyextra, yylval, yytext); return CTF_CLOCK; +double setstring(yyextra, yylval, yytext); return CTF_DOUBLE; +enum setstring(yyextra, yylval, yytext); return CTF_ENUM; +env setstring(yyextra, yylval, yytext); return CTF_ENV; +event setstring(yyextra, yylval, yytext); return CTF_EVENT; +floating_point setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT; +float setstring(yyextra, yylval, yytext); return CTF_FLOAT; +integer setstring(yyextra, yylval, yytext); return CTF_INTEGER; +int setstring(yyextra, yylval, yytext); return CTF_INT; +long setstring(yyextra, yylval, yytext); return CTF_LONG; +short setstring(yyextra, yylval, yytext); return CTF_SHORT; +signed setstring(yyextra, yylval, yytext); return CTF_SIGNED; +stream setstring(yyextra, yylval, yytext); return CTF_STREAM; +string setstring(yyextra, yylval, yytext); return CTF_STRING; +struct setstring(yyextra, yylval, yytext); return CTF_STRUCT; +trace setstring(yyextra, yylval, yytext); return CTF_TRACE; +callsite setstring(yyextra, yylval, yytext); return CTF_CALLSITE; +typealias setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS; +typedef setstring(yyextra, yylval, yytext); return CTF_TYPEDEF; +unsigned setstring(yyextra, yylval, yytext); return CTF_UNSIGNED; +variant setstring(yyextra, yylval, yytext); return CTF_VARIANT; +void setstring(yyextra, yylval, yytext); return CTF_VOID; +_Bool setstring(yyextra, yylval, yytext); return CTF_BOOL; +_Complex setstring(yyextra, yylval, yytext); return CTF_COMPLEX; +_Imaginary setstring(yyextra, yylval, yytext); return CTF_IMAGINARY; +[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL; +0{OCTALDIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL; +0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL; + +{IDENTIFIER} BT_LOGV("Got identifier: id=\"%s\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER; +[ \t\r\n] ; /* ignore */ +. _BT_LOGE_LINENO(yylineno, "Invalid character: char=\"%c\", val=0x%02x", isprint(yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR; +%% diff --git a/src/plugins/ctf/common/metadata/logging.c b/src/plugins/ctf/common/metadata/logging.c new file mode 100644 index 00000000..c7b514f4 --- /dev/null +++ b/src/plugins/ctf/common/metadata/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL metadata_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL"); diff --git a/src/plugins/ctf/common/metadata/logging.h b/src/plugins/ctf/common/metadata/logging.h new file mode 100644 index 00000000..81f76a2a --- /dev/null +++ b/src/plugins/ctf/common/metadata/logging.h @@ -0,0 +1,40 @@ +#ifndef CTF_METADATA_LOGGING_H +#define CTF_METADATA_LOGGING_H + +/* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL metadata_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(metadata_log_level); + +#define _BT_LOGV_LINENO(_lineno, _msg, args...) \ + BT_LOGV("At line %u in metadata stream: " _msg, _lineno, ## args) + +#define _BT_LOGW_LINENO(_lineno, _msg, args...) \ + BT_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args) + +#define _BT_LOGE_LINENO(_lineno, _msg, args...) \ + BT_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args) + +#endif /* CTF_METADATA_LOGGING_H */ diff --git a/src/plugins/ctf/common/metadata/objstack.c b/src/plugins/ctf/common/metadata/objstack.c new file mode 100644 index 00000000..15b6d26a --- /dev/null +++ b/src/plugins/ctf/common/metadata/objstack.c @@ -0,0 +1,144 @@ +/* + * objstack.c + * + * Common Trace Format Object Stack. + * + * Copyright 2013 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-OBJSTACK" +#include "logging.h" + +#include +#include "common/list.h" +#include "common/babeltrace.h" +#include "common/align.h" + +#define OBJSTACK_ALIGN 8 /* Object stack alignment */ +#define OBJSTACK_INIT_LEN 128 +#define OBJSTACK_POISON 0xcc + +struct objstack { + struct bt_list_head head; /* list of struct objstack_node */ +}; + +struct objstack_node { + struct bt_list_head node; + size_t len; + size_t used_len; + char __attribute__ ((aligned (OBJSTACK_ALIGN))) data[]; +}; + +BT_HIDDEN +struct objstack *objstack_create(void) +{ + struct objstack *objstack; + struct objstack_node *node; + + objstack = calloc(1, sizeof(*objstack)); + if (!objstack) { + BT_LOGE_STR("Failed to allocate one object stack."); + return NULL; + } + node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, + sizeof(char)); + if (!node) { + BT_LOGE_STR("Failed to allocate one object stack node."); + free(objstack); + return NULL; + } + BT_INIT_LIST_HEAD(&objstack->head); + bt_list_add_tail(&node->node, &objstack->head); + node->len = OBJSTACK_INIT_LEN; + return objstack; +} + +static +void objstack_node_free(struct objstack_node *node) +{ + size_t offset, len; + char *p; + + if (!node) + return; + p = (char *) node; + len = sizeof(*node) + node->len; + for (offset = 0; offset < len; offset++) + p[offset] = OBJSTACK_POISON; + free(node); +} + +BT_HIDDEN +void objstack_destroy(struct objstack *objstack) +{ + struct objstack_node *node, *p; + + if (!objstack) + return; + bt_list_for_each_entry_safe(node, p, &objstack->head, node) { + bt_list_del(&node->node); + objstack_node_free(node); + } + free(objstack); +} + +static +struct objstack_node *objstack_append_node(struct objstack *objstack) +{ + struct objstack_node *last_node, *new_node; + + /* Get last node */ + last_node = bt_list_entry(objstack->head.prev, + struct objstack_node, node); + + /* Allocate new node with double of size of last node */ + new_node = calloc(sizeof(struct objstack_node) + (last_node->len << 1), + sizeof(char)); + if (!new_node) { + BT_LOGE_STR("Failed to allocate one object stack node."); + return NULL; + } + bt_list_add_tail(&new_node->node, &objstack->head); + new_node->len = last_node->len << 1; + return new_node; +} + +BT_HIDDEN +void *objstack_alloc(struct objstack *objstack, size_t len) +{ + struct objstack_node *last_node; + void *p; + + len = ALIGN(len, OBJSTACK_ALIGN); + + /* Get last node */ + last_node = bt_list_entry(objstack->head.prev, + struct objstack_node, node); + while (last_node->len - last_node->used_len < len) { + last_node = objstack_append_node(objstack); + if (!last_node) { + return NULL; + } + } + p = &last_node->data[last_node->used_len]; + last_node->used_len += len; + return p; +} diff --git a/src/plugins/ctf/common/metadata/objstack.h b/src/plugins/ctf/common/metadata/objstack.h new file mode 100644 index 00000000..c026eb54 --- /dev/null +++ b/src/plugins/ctf/common/metadata/objstack.h @@ -0,0 +1,44 @@ +#ifndef _OBJSTACK_H +#define _OBJSTACK_H + +/* + * objstack.h + * + * Common Trace Format Object Stack. + * + * Copyright 2013 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +struct objstack; + +BT_HIDDEN +struct objstack *objstack_create(void); +BT_HIDDEN +void objstack_destroy(struct objstack *objstack); + +/* + * Allocate len bytes of zeroed memory. + * Return NULL on error. + */ +BT_HIDDEN +void *objstack_alloc(struct objstack *objstack, size_t len); + +#endif /* _OBJSTACK_H */ diff --git a/src/plugins/ctf/common/metadata/parser.y b/src/plugins/ctf/common/metadata/parser.y new file mode 100644 index 00000000..8082a0b0 --- /dev/null +++ b/src/plugins/ctf/common/metadata/parser.y @@ -0,0 +1,2592 @@ +%{ +/* + * ctf-parser.y + * + * Common Trace Format Metadata Grammar. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-PARSER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/list.h" +#include "common/assert.h" +#include "scanner.h" +#include "parser.h" +#include "ast.h" +#include "objstack.h" + +#if BT_LOG_ENABLED_VERBOSE +# define YYDEBUG 1 +# define YYFPRINTF(_stream, _fmt, args...) BT_LOGV(_fmt, ## args) +#else +# define YYDEBUG 0 +#endif + +/* Join two lists, put "add" at the end of "head". */ +static inline void +_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head) +{ + /* Do nothing if the list which gets added is empty. */ + if (add != add->next) { + add->next->prev = head->prev; + add->prev->next = head; + head->prev->next = add->next; + head->prev = add->prev; + } +} + +BT_HIDDEN +int yyparse(struct ctf_scanner *scanner, yyscan_t yyscanner); +BT_HIDDEN +int yylex(union YYSTYPE *yyval, yyscan_t yyscanner); +BT_HIDDEN +int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals); +BT_HIDDEN +int yylex_destroy(yyscan_t yyscanner); +BT_HIDDEN +void yyrestart(FILE * in_str, yyscan_t yyscanner); +BT_HIDDEN +int yyget_lineno(yyscan_t yyscanner); +BT_HIDDEN +char *yyget_text(yyscan_t yyscanner); + +static const char *node_type_to_str[] = { +#define ENTRY(S) [S] = #S, + FOREACH_CTF_NODES(ENTRY) +#undef ENTRY +}; + +/* + * Static node for out of memory errors. Only "type" is used. lineno is + * always left at 0. The rest of the node content can be overwritten, + * but is never used. + */ +static struct ctf_node error_node = { + .type = NODE_ERROR, +}; + +BT_HIDDEN +const char *node_type(struct ctf_node *node) +{ + if (node->type < NR_NODE_TYPES) + return node_type_to_str[node->type]; + else + return NULL; +} + +void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src) +{ + lvalp->s = objstack_alloc(scanner->objstack, strlen(src) + 1); + strcpy(lvalp->s, src); +} + +static +int str_check(size_t str_len, size_t offset, size_t len) +{ + /* check overflow */ + if (offset + len < offset) + return -1; + if (offset + len > str_len) + return -1; + return 0; +} + +static +int bt_isodigit(int c) +{ + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + return 1; + default: + return 0; + } +} + +static +int parse_base_sequence(const char *src, size_t len, size_t pos, + char *buffer, size_t *buf_len, int base) +{ + const size_t max_char = 3; + int nr_char = 0; + + while (!str_check(len, pos, 1) && nr_char < max_char) { + char c = src[pos++]; + + if (base == 8) { + if (bt_isodigit(c)) + buffer[nr_char++] = c; + else + break; + } else if (base == 16) { + if (isxdigit(c)) + buffer[nr_char++] = c; + else + break; + + } else { + /* Unsupported base */ + return -1; + } + } + BT_ASSERT(nr_char > 0); + buffer[nr_char] = '\0'; + *buf_len = nr_char; + return 0; +} + +static +int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, + size_t len, const char *src, char delim) +{ + size_t pos = 0, dpos = 0; + + if (str_check(len, pos, 1)) + return -1; + if (src[pos++] != delim) + return -1; + + while (src[pos] != delim) { + char c; + + if (str_check(len, pos, 1)) + return -1; + c = src[pos++]; + if (c == '\\') { + if (str_check(len, pos, 1)) + return -1; + c = src[pos++]; + + switch (c) { + case 'a': + c = '\a'; + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case '\\': + c = '\\'; + break; + case '\'': + c = '\''; + break; + case '\"': + c = '\"'; + break; + case '?': + c = '?'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + char oct_buffer[4]; + size_t oct_len; + + if (parse_base_sequence(src, len, pos - 1, + oct_buffer, &oct_len, 8)) + return -1; + c = strtoul(&oct_buffer[0], NULL, 8); + pos += oct_len - 1; + break; + } + case 'x': + { + char hex_buffer[4]; + size_t hex_len; + + if (parse_base_sequence(src, len, pos, + hex_buffer, &hex_len, 16)) + return -1; + c = strtoul(&hex_buffer[0], NULL, 16); + pos += hex_len; + break; + } + default: + return -1; + } + } + if (str_check(len, dpos, 1)) + return -1; + lvalp->s[dpos++] = c; + } + + if (str_check(len, dpos, 1)) + return -1; + lvalp->s[dpos++] = '\0'; + + if (str_check(len, pos, 1)) + return -1; + if (src[pos++] != delim) + return -1; + + if (str_check(len, pos, 1)) + return -1; + if (src[pos] != '\0') + return -1; + return 0; +} + +int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, + const char *src, char delim) +{ + size_t len; + + len = strlen(src) + 1; + lvalp->s = objstack_alloc(scanner->objstack, len); + if (src[0] == 'L') { + // TODO: import wide string + _BT_LOGE_LINENO(yyget_lineno(scanner), + "wide characters are not supported as of this version: " + "scanner-addr=%p", scanner); + return -1; + } else { + return import_basic_string(scanner, lvalp, len, src, delim); + } +} + +static void init_scope(struct ctf_scanner_scope *scope, + struct ctf_scanner_scope *parent) +{ + scope->parent = parent; + scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, NULL); +} + +static void finalize_scope(struct ctf_scanner_scope *scope) +{ + g_hash_table_destroy(scope->classes); +} + +static void push_scope(struct ctf_scanner *scanner) +{ + struct ctf_scanner_scope *ns; + + BT_LOGV("Pushing scope: scanner-addr=%p", scanner); + ns = malloc(sizeof(struct ctf_scanner_scope)); + init_scope(ns, scanner->cs); + scanner->cs = ns; +} + +static void pop_scope(struct ctf_scanner *scanner) +{ + struct ctf_scanner_scope *os; + + BT_LOGV("Popping scope: scanner-addr=%p", scanner); + os = scanner->cs; + scanner->cs = os->parent; + finalize_scope(os); + free(os); +} + +static int lookup_type(struct ctf_scanner_scope *s, const char *id) +{ + int ret; + + ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id)); + BT_LOGV("Looked up type: scanner-addr=%p, id=\"%s\", ret=%d", + s, id, ret); + return ret; +} + +BT_HIDDEN +int is_type(struct ctf_scanner *scanner, const char *id) +{ + struct ctf_scanner_scope *it; + int ret = 0; + + for (it = scanner->cs; it != NULL; it = it->parent) { + if (lookup_type(it, id)) { + ret = 1; + break; + } + } + BT_LOGV("Found if ID is type: scanner-addr=%p, id=\"%s\", ret=%d", + scanner, id, ret); + return ret; +} + +static void add_type(struct ctf_scanner *scanner, char *id) +{ + BT_LOGV("Adding type: scanner-addr=%p, id=\"%s\"", + scanner, id); + if (lookup_type(scanner->cs, id)) + return; + g_hash_table_insert(scanner->cs->classes, id, id); +} + +static struct ctf_node *make_node(struct ctf_scanner *scanner, + enum node_type type) +{ + struct ctf_node *node; + + node = objstack_alloc(scanner->objstack, sizeof(*node)); + if (!node) { + _BT_LOGE_LINENO(yyget_lineno(scanner->scanner), + "failed to allocate one stack entry: " + "scanner-addr=%p", scanner); + return &error_node; + } + node->type = type; + node->lineno = yyget_lineno(scanner->scanner); + BT_INIT_LIST_HEAD(&node->tmp_head); + bt_list_add(&node->siblings, &node->tmp_head); + + switch (type) { + case NODE_ROOT: + node->type = NODE_ERROR; + BT_LOGE("Trying to create root node: scanner-addr=%p", + scanner); + break; + case NODE_EVENT: + BT_INIT_LIST_HEAD(&node->u.event.declaration_list); + break; + case NODE_STREAM: + BT_INIT_LIST_HEAD(&node->u.stream.declaration_list); + break; + case NODE_ENV: + BT_INIT_LIST_HEAD(&node->u.env.declaration_list); + break; + case NODE_TRACE: + BT_INIT_LIST_HEAD(&node->u.trace.declaration_list); + break; + case NODE_CLOCK: + BT_INIT_LIST_HEAD(&node->u.clock.declaration_list); + break; + case NODE_CALLSITE: + BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list); + break; + case NODE_CTF_EXPRESSION: + BT_INIT_LIST_HEAD(&node->u.ctf_expression.left); + BT_INIT_LIST_HEAD(&node->u.ctf_expression.right); + break; + case NODE_UNARY_EXPRESSION: + break; + case NODE_TYPEDEF: + BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators); + break; + case NODE_TYPEALIAS_TARGET: + BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators); + break; + case NODE_TYPEALIAS_ALIAS: + BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators); + break; + case NODE_TYPEALIAS: + break; + case NODE_TYPE_SPECIFIER: + break; + case NODE_TYPE_SPECIFIER_LIST: + BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head); + break; + case NODE_POINTER: + break; + case NODE_TYPE_DECLARATOR: + BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers); + break; + case NODE_FLOATING_POINT: + BT_INIT_LIST_HEAD(&node->u.floating_point.expressions); + break; + case NODE_INTEGER: + BT_INIT_LIST_HEAD(&node->u.integer.expressions); + break; + case NODE_STRING: + BT_INIT_LIST_HEAD(&node->u.string.expressions); + break; + case NODE_ENUMERATOR: + BT_INIT_LIST_HEAD(&node->u.enumerator.values); + break; + case NODE_ENUM: + BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list); + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators); + break; + case NODE_VARIANT: + BT_INIT_LIST_HEAD(&node->u.variant.declaration_list); + break; + case NODE_STRUCT: + BT_INIT_LIST_HEAD(&node->u._struct.declaration_list); + BT_INIT_LIST_HEAD(&node->u._struct.min_align); + break; + case NODE_UNKNOWN: + default: + node->type = NODE_ERROR; + BT_LOGE("Unknown node type: scanner-addr=%p, node-type=%d", + scanner, type); + break; + } + + return node; +} + +static int reparent_ctf_expression(struct ctf_node *node, + struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_EVENT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); + break; + case NODE_STREAM: + _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); + break; + case NODE_ENV: + _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); + break; + case NODE_TRACE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); + break; + case NODE_CLOCK: + _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); + break; + case NODE_CALLSITE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); + break; + case NODE_FLOATING_POINT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions); + break; + case NODE_INTEGER: + _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions); + break; + case NODE_STRING: + _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions); + break; + + case NODE_ROOT: + case NODE_CTF_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_VARIANT: + case NODE_STRUCT: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_ROOT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list); + break; + case NODE_EVENT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); + break; + case NODE_STREAM: + _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); + break; + case NODE_ENV: + _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); + break; + case NODE_TRACE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); + break; + case NODE_CLOCK: + _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); + break; + case NODE_CALLSITE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); + break; + case NODE_VARIANT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); + break; + case NODE_STRUCT: + _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); + break; + + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_CTF_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_ROOT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list); + break; + case NODE_EVENT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list); + break; + case NODE_STREAM: + _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list); + break; + case NODE_ENV: + _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list); + break; + case NODE_TRACE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list); + break; + case NODE_CLOCK: + _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list); + break; + case NODE_CALLSITE: + _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list); + break; + case NODE_VARIANT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); + break; + case NODE_STRUCT: + _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); + break; + + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_CTF_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_field_class_specifier(struct ctf_node *node, + struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_TYPE_SPECIFIER_LIST: + _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head); + break; + + case NODE_TYPE_SPECIFIER: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_VARIANT: + case NODE_STRUCT: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPE_DECLARATOR: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_TYPEALIAS: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_CTF_EXPRESSION: + case NODE_POINTER: + case NODE_ENUMERATOR: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_field_class_specifier_list(struct ctf_node *node, + struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_ROOT: + bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list); + break; + case NODE_EVENT: + bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list); + break; + case NODE_STREAM: + bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list); + break; + case NODE_ENV: + bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list); + break; + case NODE_TRACE: + bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list); + break; + case NODE_CLOCK: + bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list); + break; + case NODE_CALLSITE: + bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list); + break; + case NODE_VARIANT: + bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list); + break; + case NODE_STRUCT: + bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list); + break; + case NODE_TYPEDEF: + parent->u.field_class_def.field_class_specifier_list = node; + break; + case NODE_TYPEALIAS_TARGET: + parent->u.field_class_alias_target.field_class_specifier_list = node; + break; + case NODE_TYPEALIAS_ALIAS: + parent->u.field_class_alias_name.field_class_specifier_list = node; + break; + case NODE_ENUM: + parent->u._enum.container_field_class = node; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + parent->u.struct_or_variant_declaration.field_class_specifier_list = node; + break; + case NODE_TYPE_DECLARATOR: + case NODE_TYPE_SPECIFIER: + case NODE_TYPEALIAS: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_CTF_EXPRESSION: + case NODE_POINTER: + case NODE_ENUMERATOR: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_field_class_declarator(struct ctf_node *node, + struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_TYPE_DECLARATOR: + parent->u.field_class_declarator.type = TYPEDEC_NESTED; + parent->u.field_class_declarator.u.nested.field_class_declarator = node; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators); + break; + case NODE_TYPEDEF: + _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators); + break; + case NODE_TYPEALIAS_TARGET: + _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators); + break; + case NODE_TYPEALIAS_ALIAS: + _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators); + break; + + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_VARIANT: + case NODE_STRUCT: + case NODE_TYPEALIAS: + case NODE_ENUM: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_CTF_EXPRESSION: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_ENUMERATOR: + case NODE_UNARY_EXPRESSION: + return -EPERM; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +/* + * set_parent_node + * + * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to + * create the link declared by the input, -ENOENT if node or parent is NULL, + * -EINVAL if there is an internal structure problem. + */ +static int set_parent_node(struct ctf_node *node, + struct ctf_node *parent) +{ + if (!node || !parent) + return -ENOENT; + + /* Note: Linking to parent will be done only by an external visitor */ + + switch (node->type) { + case NODE_ROOT: + BT_LOGE_STR("Trying to reparent root node."); + return -EINVAL; + + case NODE_EVENT: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event); + } else { + return -EPERM; + } + break; + case NODE_STREAM: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream); + } else { + return -EPERM; + } + break; + case NODE_ENV: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env); + } else { + return -EPERM; + } + break; + case NODE_TRACE: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace); + } else { + return -EPERM; + } + break; + case NODE_CLOCK: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock); + } else { + return -EPERM; + } + break; + case NODE_CALLSITE: + if (parent->type == NODE_ROOT) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite); + } else { + return -EPERM; + } + break; + + case NODE_CTF_EXPRESSION: + return reparent_ctf_expression(node, parent); + case NODE_UNARY_EXPRESSION: + if (parent->type == NODE_TYPE_DECLARATOR) + parent->u.field_class_declarator.bitfield_len = node; + else + return -EPERM; + break; + + case NODE_TYPEDEF: + return reparent_typedef(node, parent); + case NODE_TYPEALIAS_TARGET: + if (parent->type == NODE_TYPEALIAS) + parent->u.field_class_alias.target = node; + else + return -EINVAL; + /* fall-through */ + case NODE_TYPEALIAS_ALIAS: + if (parent->type == NODE_TYPEALIAS) + parent->u.field_class_alias.alias = node; + else + return -EINVAL; + /* fall-through */ + case NODE_TYPEALIAS: + return reparent_field_class_alias(node, parent); + + case NODE_POINTER: + if (parent->type == NODE_TYPE_DECLARATOR) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers); + } else + return -EPERM; + break; + case NODE_TYPE_DECLARATOR: + return reparent_field_class_declarator(node, parent); + + case NODE_TYPE_SPECIFIER_LIST: + return reparent_field_class_specifier_list(node, parent); + + case NODE_TYPE_SPECIFIER: + return reparent_field_class_specifier(node, parent); + + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUM: + case NODE_VARIANT: + case NODE_STRUCT: + return -EINVAL; /* Dealt with internally within grammar */ + + case NODE_ENUMERATOR: + if (parent->type == NODE_ENUM) { + _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list); + } else { + return -EPERM; + } + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + switch (parent->type) { + case NODE_STRUCT: + _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list); + break; + case NODE_VARIANT: + _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list); + break; + default: + return -EINVAL; + } + break; + + case NODE_UNKNOWN: + default: + BT_LOGE("Unknown node type: node-type=%d", parent->type); + return -EINVAL; + } + return 0; +} + +BT_HIDDEN +void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str) +{ + _BT_LOGE_LINENO(yyget_lineno(scanner->scanner), + "%s: token=\"%s\"", str, yyget_text(scanner->scanner)); +} + +BT_HIDDEN +int yywrap(void) +{ + return 1; +} + +#define reparent_error(scanner, str) \ +do { \ + yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \ + YYERROR; \ +} while (0) + +static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner) +{ + struct ctf_ast *ast; + + ast = objstack_alloc(scanner->objstack, sizeof(*ast)); + if (!ast) + return NULL; + ast->root.type = NODE_ROOT; + BT_INIT_LIST_HEAD(&ast->root.tmp_head); + BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list); + BT_INIT_LIST_HEAD(&ast->root.u.root.trace); + BT_INIT_LIST_HEAD(&ast->root.u.root.env); + BT_INIT_LIST_HEAD(&ast->root.u.root.stream); + BT_INIT_LIST_HEAD(&ast->root.u.root.event); + BT_INIT_LIST_HEAD(&ast->root.u.root.clock); + BT_INIT_LIST_HEAD(&ast->root.u.root.callsite); + return ast; +} + +int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input) +{ + /* Start processing new stream */ + yyrestart(input, scanner->scanner); + return yyparse(scanner, scanner->scanner); +} + +struct ctf_scanner *ctf_scanner_alloc(void) +{ + struct ctf_scanner *scanner; + int ret; + + scanner = malloc(sizeof(*scanner)); + if (!scanner) + return NULL; + memset(scanner, 0, sizeof(*scanner)); + ret = yylex_init_extra(scanner, &scanner->scanner); + if (ret) { + BT_LOGE("yylex_init_extra() failed: ret=%d", ret); + goto cleanup_scanner; + } + scanner->objstack = objstack_create(); + if (!scanner->objstack) + goto cleanup_lexer; + scanner->ast = ctf_ast_alloc(scanner); + if (!scanner->ast) + goto cleanup_objstack; + init_scope(&scanner->root_scope, NULL); + scanner->cs = &scanner->root_scope; + + return scanner; + +cleanup_objstack: + objstack_destroy(scanner->objstack); +cleanup_lexer: + ret = yylex_destroy(scanner->scanner); + if (!ret) + BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d", + scanner, ret); +cleanup_scanner: + free(scanner); + return NULL; +} + +void ctf_scanner_free(struct ctf_scanner *scanner) +{ + int ret; + + if (!scanner) + return; + finalize_scope(&scanner->root_scope); + objstack_destroy(scanner->objstack); + ret = yylex_destroy(scanner->scanner); + if (ret) + BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d", + scanner, ret); + free(scanner); +} + +%} + +%define api.pure + /* %locations */ +%error-verbose +%parse-param {struct ctf_scanner *scanner} +%parse-param {yyscan_t yyscanner} +%lex-param {yyscan_t yyscanner} +/* + * Expect two shift-reduce conflicts. Caused by enum name-opt : type {} + * vs struct { int :value; } (unnamed bit-field). The default is to + * shift, so whenever we encounter an enumeration, we are doing the + * proper thing (shift). It is illegal to declare an enumeration + * "bit-field", so it is OK if this situation ends up in a parsing + * error. + */ +%expect 2 +%start file +%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN +%token IDENTIFIER ID_TYPE +%token CTF_ERROR +%union +{ + long long ll; + unsigned long long ull; + char c; + char *s; + struct ctf_node *n; +} + +%type CTF_STRING_LITERAL CTF_CHARACTER_LITERAL + +%type keywords + +%type CTF_INTEGER_LITERAL +%type postfix_expression unary_expression unary_expression_or_range + +%type declaration +%type event_declaration +%type stream_declaration +%type env_declaration +%type trace_declaration +%type clock_declaration +%type callsite_declaration +%type integer_declaration_specifiers +%type declaration_specifiers +%type alias_declaration_specifiers + +%type field_class_declarator_list +%type integer_field_class_specifier +%type field_class_specifier +%type struct_class_specifier +%type variant_field_class_specifier +%type enum_field_class_specifier +%type struct_or_variant_declaration_list +%type struct_or_variant_declaration +%type struct_or_variant_declarator_list +%type struct_or_variant_declarator +%type enumerator_list +%type enumerator +%type abstract_declarator_list +%type abstract_declarator +%type direct_abstract_declarator +%type alias_abstract_declarator_list +%type alias_abstract_declarator +%type direct_alias_abstract_declarator +%type declarator +%type direct_declarator +%type field_class_declarator +%type direct_field_class_declarator +%type pointer +%type ctf_assignment_expression_list +%type ctf_assignment_expression + +%% + +file: + declaration + { + if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root)) + reparent_error(scanner, "error reparenting to root"); + } + | file declaration + { + if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root)) + reparent_error(scanner, "error reparenting to root"); + } + ; + +keywords: + CTF_VOID + { $$ = yylval.s; } + | CTF_CHAR + { $$ = yylval.s; } + | CTF_SHORT + { $$ = yylval.s; } + | CTF_INT + { $$ = yylval.s; } + | CTF_LONG + { $$ = yylval.s; } + | CTF_FLOAT + { $$ = yylval.s; } + | CTF_DOUBLE + { $$ = yylval.s; } + | CTF_SIGNED + { $$ = yylval.s; } + | CTF_UNSIGNED + { $$ = yylval.s; } + | CTF_BOOL + { $$ = yylval.s; } + | CTF_COMPLEX + { $$ = yylval.s; } + | CTF_IMAGINARY + { $$ = yylval.s; } + | CTF_FLOATING_POINT + { $$ = yylval.s; } + | CTF_INTEGER + { $$ = yylval.s; } + | CTF_STRING + { $$ = yylval.s; } + | CTF_ENUM + { $$ = yylval.s; } + | CTF_VARIANT + { $$ = yylval.s; } + | CTF_STRUCT + { $$ = yylval.s; } + | CTF_CONST + { $$ = yylval.s; } + | CTF_TYPEDEF + { $$ = yylval.s; } + | CTF_EVENT + { $$ = yylval.s; } + | CTF_STREAM + { $$ = yylval.s; } + | CTF_ENV + { $$ = yylval.s; } + | CTF_TRACE + { $$ = yylval.s; } + | CTF_CLOCK + { $$ = yylval.s; } + | CTF_CALLSITE + { $$ = yylval.s; } + | CTF_TOK_ALIGN + { $$ = yylval.s; } + ; + + +/* 2: Phrase structure grammar */ + +postfix_expression: + IDENTIFIER + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + } + | keywords + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + } + | CTF_INTEGER_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT; + $$->u.unary_expression.u.unsigned_constant = $1; + } + | CTF_STRING_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = $1; + } + | CTF_CHARACTER_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = $1; + } + | CTF_LPAREN unary_expression CTF_RPAREN + { + $$ = $2; + } + | postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_SBRAC; + $$->u.unary_expression.u.sbrac_exp = $3; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + | postfix_expression CTF_DOT IDENTIFIER + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + $$->u.unary_expression.link = UNARY_DOTLINK; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + | postfix_expression CTF_DOT ID_TYPE + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + $$->u.unary_expression.link = UNARY_DOTLINK; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + | postfix_expression CTF_DOT keywords + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + $$->u.unary_expression.link = UNARY_DOTLINK; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + | postfix_expression CTF_RARROW IDENTIFIER + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + $$->u.unary_expression.link = UNARY_ARROWLINK; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + | postfix_expression CTF_RARROW ID_TYPE + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = yylval.s; + $$->u.unary_expression.link = UNARY_ARROWLINK; + bt_list_splice(&($1)->tmp_head, &($$)->tmp_head); + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + ; + +unary_expression: + postfix_expression + { $$ = $1; } + | CTF_PLUS postfix_expression + { + $$ = $2; + if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT + && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) { + reparent_error(scanner, "expecting numeric constant"); + } + } + | CTF_MINUS postfix_expression + { + $$ = $2; + if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) { + $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT; + $$->u.unary_expression.u.signed_constant = + -($$->u.unary_expression.u.unsigned_constant); + } else if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) { + $$->u.unary_expression.u.signed_constant = + -($$->u.unary_expression.u.signed_constant); + } else { + reparent_error(scanner, "expecting numeric constant"); + } + } + ; + +unary_expression_or_range: + unary_expression CTF_DOTDOTDOT unary_expression + { + $$ = $1; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head); + $3->u.unary_expression.link = UNARY_DOTDOTDOT; + } + | unary_expression + { $$ = $1; } + ; + +/* 2.2: Declarations */ + +declaration: + declaration_specifiers CTF_SEMICOLON + { $$ = $1; } + | event_declaration + { $$ = $1; } + | stream_declaration + { $$ = $1; } + | env_declaration + { $$ = $1; } + | trace_declaration + { $$ = $1; } + | clock_declaration + { $$ = $1; } + | callsite_declaration + { $$ = $1; } + | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; + _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); + } + ; + +event_declaration: + event_declaration_begin event_declaration_end + { + $$ = make_node(scanner, NODE_EVENT); + } + | event_declaration_begin ctf_assignment_expression_list event_declaration_end + { + $$ = make_node(scanner, NODE_EVENT); + if (set_parent_node($2, $$)) + reparent_error(scanner, "event_declaration"); + } + ; + +event_declaration_begin: + CTF_EVENT CTF_LBRAC + { push_scope(scanner); } + ; + +event_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + + +stream_declaration: + stream_declaration_begin stream_declaration_end + { + $$ = make_node(scanner, NODE_STREAM); + } + | stream_declaration_begin ctf_assignment_expression_list stream_declaration_end + { + $$ = make_node(scanner, NODE_STREAM); + if (set_parent_node($2, $$)) + reparent_error(scanner, "stream_declaration"); + } + ; + +stream_declaration_begin: + CTF_STREAM CTF_LBRAC + { push_scope(scanner); } + ; + +stream_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + +env_declaration: + env_declaration_begin env_declaration_end + { + $$ = make_node(scanner, NODE_ENV); + } + | env_declaration_begin ctf_assignment_expression_list env_declaration_end + { + $$ = make_node(scanner, NODE_ENV); + if (set_parent_node($2, $$)) + reparent_error(scanner, "env declaration"); + } + ; + +env_declaration_begin: + CTF_ENV CTF_LBRAC + { push_scope(scanner); } + ; + +env_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + +trace_declaration: + trace_declaration_begin trace_declaration_end + { + $$ = make_node(scanner, NODE_TRACE); + } + | trace_declaration_begin ctf_assignment_expression_list trace_declaration_end + { + $$ = make_node(scanner, NODE_TRACE); + if (set_parent_node($2, $$)) + reparent_error(scanner, "trace_declaration"); + } + ; + +trace_declaration_begin: + CTF_TRACE CTF_LBRAC + { push_scope(scanner); } + ; + +trace_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + +clock_declaration: + CTF_CLOCK clock_declaration_begin clock_declaration_end + { + $$ = make_node(scanner, NODE_CLOCK); + } + | CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end + { + $$ = make_node(scanner, NODE_CLOCK); + if (set_parent_node($3, $$)) + reparent_error(scanner, "trace_declaration"); + } + ; + +clock_declaration_begin: + CTF_LBRAC + { push_scope(scanner); } + ; + +clock_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + +callsite_declaration: + CTF_CALLSITE callsite_declaration_begin callsite_declaration_end + { + $$ = make_node(scanner, NODE_CALLSITE); + } + | CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end + { + $$ = make_node(scanner, NODE_CALLSITE); + if (set_parent_node($3, $$)) + reparent_error(scanner, "trace_declaration"); + } + ; + +callsite_declaration_begin: + CTF_LBRAC + { push_scope(scanner); } + ; + +callsite_declaration_end: + CTF_RBRAC CTF_SEMICOLON + { pop_scope(scanner); } + ; + +integer_declaration_specifiers: + CTF_CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | integer_field_class_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | integer_declaration_specifiers CTF_CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | integer_declaration_specifiers integer_field_class_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); + } + ; + +declaration_specifiers: + CTF_CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | field_class_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | declaration_specifiers CTF_CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | declaration_specifiers field_class_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); + } + ; + +field_class_declarator_list: + field_class_declarator + { $$ = $1; } + | field_class_declarator_list CTF_COMMA field_class_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +integer_field_class_specifier: + CTF_CHAR + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_CHAR; + } + | CTF_SHORT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_SHORT; + } + | CTF_INT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INT; + } + | CTF_LONG + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_LONG; + } + | CTF_SIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_SIGNED; + } + | CTF_UNSIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED; + } + | CTF_BOOL + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_BOOL; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE; + $$->u.field_class_specifier.id_type = yylval.s; + } + | CTF_INTEGER CTF_LBRAC CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INTEGER; + $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); + } + | CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INTEGER; + $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); + if (set_parent_node($3, $$->u.field_class_specifier.node)) + reparent_error(scanner, "integer reparent error"); + } + ; + +field_class_specifier: + CTF_VOID + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_VOID; + } + | CTF_CHAR + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_CHAR; + } + | CTF_SHORT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_SHORT; + } + | CTF_INT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INT; + } + | CTF_LONG + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_LONG; + } + | CTF_FLOAT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_FLOAT; + } + | CTF_DOUBLE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_DOUBLE; + } + | CTF_SIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_SIGNED; + } + | CTF_UNSIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED; + } + | CTF_BOOL + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_BOOL; + } + | CTF_COMPLEX + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_COMPLEX; + } + | CTF_IMAGINARY + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE; + $$->u.field_class_specifier.id_type = yylval.s; + } + | CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT; + $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT); + } + | CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT; + $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT); + if (set_parent_node($3, $$->u.field_class_specifier.node)) + reparent_error(scanner, "floating point reparent error"); + } + | CTF_INTEGER CTF_LBRAC CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INTEGER; + $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); + } + | CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_INTEGER; + $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER); + if (set_parent_node($3, $$->u.field_class_specifier.node)) + reparent_error(scanner, "integer reparent error"); + } + | CTF_STRING + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_STRING; + $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); + } + | CTF_STRING CTF_LBRAC CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_STRING; + $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); + } + | CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_STRING; + $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING); + if (set_parent_node($3, $$->u.field_class_specifier.node)) + reparent_error(scanner, "string reparent error"); + } + | CTF_ENUM enum_field_class_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_ENUM; + $$->u.field_class_specifier.node = $2; + } + | CTF_VARIANT variant_field_class_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_VARIANT; + $$->u.field_class_specifier.node = $2; + } + | CTF_STRUCT struct_class_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.field_class_specifier.type = TYPESPEC_STRUCT; + $$->u.field_class_specifier.node = $2; + } + ; + +struct_class_specifier: + struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + if ($2 && set_parent_node($2, $$)) + reparent_error(scanner, "struct reparent error"); + } + | IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + $$->u._struct.name = $1; + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "struct reparent error"); + } + | ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + $$->u._struct.name = $1; + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "struct reparent error"); + } + | IDENTIFIER + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 0; + $$->u._struct.name = $1; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 0; + $$->u._struct.name = $1; + } + | struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align); + if ($2 && set_parent_node($2, $$)) + reparent_error(scanner, "struct reparent error"); + } + | IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + $$->u._struct.name = $1; + bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align); + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "struct reparent error"); + } + | ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN + { + $$ = make_node(scanner, NODE_STRUCT); + $$->u._struct.has_body = 1; + $$->u._struct.name = $1; + bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align); + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "struct reparent error"); + } + ; + +struct_declaration_begin: + CTF_LBRAC + { push_scope(scanner); } + ; + +struct_declaration_end: + CTF_RBRAC + { pop_scope(scanner); } + ; + +variant_field_class_specifier: + variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + if ($2 && set_parent_node($2, $$)) + reparent_error(scanner, "variant reparent error"); + } + | CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.choice = $2; + if ($5 && set_parent_node($5, $$)) + reparent_error(scanner, "variant reparent error"); + } + | CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.choice = $2; + if ($5 && set_parent_node($5, $$)) + reparent_error(scanner, "variant reparent error"); + } + | IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "variant reparent error"); + } + | IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + if ($6 && set_parent_node($6, $$)) + reparent_error(scanner, "variant reparent error"); + } + | IDENTIFIER CTF_LT IDENTIFIER CTF_GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + | IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + if ($6 && set_parent_node($6, $$)) + reparent_error(scanner, "variant reparent error"); + } + | IDENTIFIER CTF_LT ID_TYPE CTF_GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + | ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + if ($3 && set_parent_node($3, $$)) + reparent_error(scanner, "variant reparent error"); + } + | ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + if ($6 && set_parent_node($6, $$)) + reparent_error(scanner, "variant reparent error"); + } + | ID_TYPE CTF_LT IDENTIFIER CTF_GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + | ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 1; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + if ($6 && set_parent_node($6, $$)) + reparent_error(scanner, "variant reparent error"); + } + | ID_TYPE CTF_LT ID_TYPE CTF_GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + ; + +variant_declaration_begin: + CTF_LBRAC + { push_scope(scanner); } + ; + +variant_declaration_end: + CTF_RBRAC + { pop_scope(scanner); } + ; + +enum_field_class_specifier: + CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); + } + | CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + ($$)->u._enum.container_field_class = $2; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_field_class = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); + } + | ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_field_class = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); + } + | CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + ($$)->u._enum.container_field_class = $2; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_field_class = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 0; + $$->u._enum.enum_id = $1; + } + | ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list); + } + | ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_field_class = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 0; + $$->u._enum.enum_id = $1; + } + ; + +struct_or_variant_declaration_list: + /* empty */ + { $$ = NULL; } + | struct_or_variant_declaration_list struct_or_variant_declaration + { + if ($1) { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); + } else { + $$ = $2; + bt_list_add_tail(&($$)->siblings, &($$)->tmp_head); + } + } + ; + +struct_or_variant_declaration: + declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION); + ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators); + } + | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; + _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); + } + ; + +alias_declaration_specifiers: + CTF_CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | field_class_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | IDENTIFIER + { + struct ctf_node *node; + + add_type(scanner, $1); + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_ID_TYPE; + node->u.field_class_specifier.id_type = yylval.s; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | alias_declaration_specifiers CTF_CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + | alias_declaration_specifiers field_class_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head); + } + | alias_declaration_specifiers IDENTIFIER + { + struct ctf_node *node; + + add_type(scanner, $2); + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.field_class_specifier.type = TYPESPEC_ID_TYPE; + node->u.field_class_specifier.id_type = yylval.s; + bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head); + } + ; + +struct_or_variant_declarator_list: + struct_or_variant_declarator + { $$ = $1; } + | struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +struct_or_variant_declarator: + declarator + { $$ = $1; } + | CTF_COLON unary_expression + { $$ = $2; } + | declarator CTF_COLON unary_expression + { + $$ = $1; + if (set_parent_node($3, $1)) + reparent_error(scanner, "struct_or_variant_declarator"); + } + ; + +enumerator_list: + enumerator + { $$ = $1; } + | enumerator_list CTF_COMMA enumerator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +enumerator: + IDENTIFIER + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + } + | keywords + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + } + | CTF_STRING_LITERAL + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + } + | IDENTIFIER CTF_EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + | ID_TYPE CTF_EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + | keywords CTF_EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + | CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + ; + +abstract_declarator_list: + abstract_declarator + { $$ = $1; } + | abstract_declarator_list CTF_COMMA abstract_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +abstract_declarator: + direct_abstract_declarator + { $$ = $1; } + | pointer direct_abstract_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); + } + ; + +direct_abstract_declarator: + /* empty */ + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_ID; + /* id is NULL */ + } + | IDENTIFIER + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_ID; + $$->u.field_class_declarator.u.id = $1; + } + | CTF_LPAREN abstract_declarator CTF_RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $2; + } + | direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); + } + | direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + $$->u.field_class_declarator.u.nested.abstract_array = 1; + } + ; + +alias_abstract_declarator_list: + alias_abstract_declarator + { $$ = $1; } + | alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +alias_abstract_declarator: + direct_alias_abstract_declarator + { $$ = $1; } + | pointer direct_alias_abstract_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); + } + ; + +direct_alias_abstract_declarator: + /* empty */ + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_ID; + /* id is NULL */ + } + | CTF_LPAREN alias_abstract_declarator CTF_RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $2; + } + | direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); + } + | direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + $$->u.field_class_declarator.u.nested.abstract_array = 1; + } + ; + +declarator: + direct_declarator + { $$ = $1; } + | pointer direct_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); + } + ; + +direct_declarator: + IDENTIFIER + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_ID; + $$->u.field_class_declarator.u.id = $1; + } + | CTF_LPAREN declarator CTF_RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $2; + } + | direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); + } + ; + +field_class_declarator: + direct_field_class_declarator + { $$ = $1; } + | pointer direct_field_class_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers); + } + ; + +direct_field_class_declarator: + IDENTIFIER + { + add_type(scanner, $1); + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_ID; + $$->u.field_class_declarator.u.id = $1; + } + | CTF_LPAREN field_class_declarator CTF_RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $2; + } + | direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.field_class_declarator.type = TYPEDEC_NESTED; + $$->u.field_class_declarator.u.nested.field_class_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length); + } + ; + +pointer: + CTF_STAR + { + $$ = make_node(scanner, NODE_POINTER); + } + | CTF_STAR pointer + { + $$ = make_node(scanner, NODE_POINTER); + bt_list_splice(&($2)->tmp_head, &($$)->tmp_head); + } + | CTF_STAR type_qualifier_list pointer + { + $$ = make_node(scanner, NODE_POINTER); + $$->u.pointer.const_qualifier = 1; + bt_list_splice(&($3)->tmp_head, &($$)->tmp_head); + } + ; + +type_qualifier_list: + /* pointer assumes only const type qualifier */ + CTF_CONST + | type_qualifier_list CTF_CONST + ; + +/* 2.3: CTF-specific declarations */ + +ctf_assignment_expression_list: + ctf_assignment_expression CTF_SEMICOLON + { $$ = $1; } + | ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); + } + ; + +ctf_assignment_expression: + unary_expression CTF_EQUAL unary_expression + { + /* + * Because we have left and right, cannot use + * set_parent_node. + */ + $$ = make_node(scanner, NODE_CTF_EXPRESSION); + _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left); + if ($1->u.unary_expression.type != UNARY_STRING) + reparent_error(scanner, "ctf_assignment_expression left expects string"); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right); + } + | unary_expression CTF_TYPEASSIGN declaration_specifiers /* Only allow struct */ + { + /* + * Because we have left and right, cannot use + * set_parent_node. + */ + $$ = make_node(scanner, NODE_CTF_EXPRESSION); + _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left); + if ($1->u.unary_expression.type != UNARY_STRING) + reparent_error(scanner, "ctf_assignment_expression left expects string"); + bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right); + } + | declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEDEF declaration_specifiers field_class_declarator_list + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_def.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | declaration_specifiers CTF_TYPEDEF field_class_declarator_list + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators); + } + | CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list; + _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list; + _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators); + } + ; diff --git a/src/plugins/ctf/common/metadata/scanner-symbols.h b/src/plugins/ctf/common/metadata/scanner-symbols.h new file mode 100644 index 00000000..9b9e3631 --- /dev/null +++ b/src/plugins/ctf/common/metadata/scanner-symbols.h @@ -0,0 +1,50 @@ +#ifndef _CTF_SCANNER_SYMBOLS +#define _CTF_SCANNER_SYMBOLS + +/* + * ctf-scanner-symbols.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define yy_create_buffer bt_yy_create_buffer +#define yy_delete_buffer bt_yy_delete_buffer +#define yy_flush_buffer bt_yy_flush_buffer +#define yy_scan_buffer bt_yy_scan_buffer +#define yy_scan_bytes bt_yy_scan_bytes +#define yy_scan_string bt_yy_scan_string +#define yy_switch_to_buffer bt_yy_switch_to_buffer +#define yyalloc bt_yyalloc +#define yyfree bt_yyfree +#define yyget_column bt_yyget_column +#define yyget_debug bt_yyget_debug +#define yyget_extra bt_yyget_extra +#define yyget_in bt_yyget_in +#define yyget_leng bt_yyget_leng +#define yyget_lineno bt_yyget_lineno +#define yyget_lval bt_yyget_lval +#define yyget_out bt_yyget_out +#define yyget_text bt_yyget_text +#define yylex_init bt_yylex_init +#define yypop_buffer_state bt_yypop_buffer_state +#define yypush_buffer_state bt_yypush_buffer_state +#define yyrealloc bt_yyrealloc +#define yyset_column bt_yyset_column +#define yyset_debug bt_yyset_debug +#define yyset_extra bt_yyset_extra +#define yyset_in bt_yyset_in +#define yyset_lineno bt_yyset_lineno +#define yyset_lval bt_yyset_lval +#define yyset_out bt_yyset_out + +#endif /* _CTF_SCANNER_SYMBOLS */ diff --git a/src/plugins/ctf/common/metadata/scanner.h b/src/plugins/ctf/common/metadata/scanner.h new file mode 100644 index 00000000..34d6c462 --- /dev/null +++ b/src/plugins/ctf/common/metadata/scanner.h @@ -0,0 +1,55 @@ +#ifndef _CTF_SCANNER_H +#define _CTF_SCANNER_H + +/* + * ctf-scanner.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include "ast.h" + +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +struct ctf_scanner_scope; +struct ctf_scanner_scope { + struct ctf_scanner_scope *parent; + GHashTable *classes; +}; + +struct ctf_scanner { + yyscan_t scanner; + struct ctf_ast *ast; + struct ctf_scanner_scope root_scope; + struct ctf_scanner_scope *cs; + struct objstack *objstack; +}; + +struct ctf_scanner *ctf_scanner_alloc(void); +void ctf_scanner_free(struct ctf_scanner *scanner); +int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input); + +static inline +struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner) +{ + return scanner->ast; +} + +BT_HIDDEN +int is_type(struct ctf_scanner *scanner, const char *id); + +#endif /* _CTF_SCANNER_H */ diff --git a/src/plugins/ctf/common/metadata/visitor-generate-ir.c b/src/plugins/ctf/common/metadata/visitor-generate-ir.c new file mode 100644 index 00000000..d2f80d0f --- /dev/null +++ b/src/plugins/ctf/common/metadata/visitor-generate-ir.c @@ -0,0 +1,5090 @@ +/* + * ctf-visitor-generate-ir.c + * + * Common Trace Format metadata visitor (generates CTF IR objects). + * + * Based on older ctf-visitor-generate-io-struct.c. + * + * Copyright 2010 - Mathieu Desnoyers + * Copyright 2015-2018 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-IR-VISITOR" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include "common/common.h" +#include "compat/uuid.h" +#include "compat/endian.h" +#include + +#include "scanner.h" +#include "parser.h" +#include "ast.h" +#include "decoder.h" +#include "ctf-meta.h" +#include "ctf-meta-visitors.h" + +/* Bit value (left shift) */ +#define _BV(_val) (1 << (_val)) + +/* Bit is set in a set of bits */ +#define _IS_SET(_set, _mask) (*(_set) & (_mask)) + +/* Set bit in a set of bits */ +#define _SET(_set, _mask) (*(_set) |= (_mask)) + +/* Try to push scope, or go to the `error` label */ +#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR() \ + do { \ + ret = ctx_push_scope(ctx); \ + if (ret) { \ + BT_LOGE_STR("Cannot push scope."); \ + goto error; \ + } \ + } while (0) + +/* Bits for verifying existing attributes in various declarations */ +enum { + _CLOCK_NAME_SET = _BV(0), + _CLOCK_UUID_SET = _BV(1), + _CLOCK_FREQ_SET = _BV(2), + _CLOCK_PRECISION_SET = _BV(3), + _CLOCK_OFFSET_S_SET = _BV(4), + _CLOCK_OFFSET_SET = _BV(5), + _CLOCK_ABSOLUTE_SET = _BV(6), + _CLOCK_DESCRIPTION_SET = _BV(7), +}; + +enum { + _INTEGER_ALIGN_SET = _BV(0), + _INTEGER_SIZE_SET = _BV(1), + _INTEGER_BASE_SET = _BV(2), + _INTEGER_ENCODING_SET = _BV(3), + _INTEGER_BYTE_ORDER_SET = _BV(4), + _INTEGER_SIGNED_SET = _BV(5), + _INTEGER_MAP_SET = _BV(6), +}; + +enum { + _FLOAT_ALIGN_SET = _BV(0), + _FLOAT_MANT_DIG_SET = _BV(1), + _FLOAT_EXP_DIG_SET = _BV(2), + _FLOAT_BYTE_ORDER_SET = _BV(3), +}; + +enum { + _STRING_ENCODING_SET = _BV(0), +}; + +enum { + _TRACE_MINOR_SET = _BV(0), + _TRACE_MAJOR_SET = _BV(1), + _TRACE_BYTE_ORDER_SET = _BV(2), + _TRACE_UUID_SET = _BV(3), + _TRACE_PACKET_HEADER_SET = _BV(4), +}; + +enum { + _STREAM_ID_SET = _BV(0), + _STREAM_PACKET_CONTEXT_SET = _BV(1), + _STREAM_EVENT_HEADER_SET = _BV(2), + _STREAM_EVENT_CONTEXT_SET = _BV(3), +}; + +enum { + _EVENT_NAME_SET = _BV(0), + _EVENT_ID_SET = _BV(1), + _EVENT_MODEL_EMF_URI_SET = _BV(2), + _EVENT_STREAM_ID_SET = _BV(3), + _EVENT_LOG_LEVEL_SET = _BV(4), + _EVENT_CONTEXT_SET = _BV(5), + _EVENT_FIELDS_SET = _BV(6), +}; + +enum loglevel { + LOG_LEVEL_EMERG = 0, + LOG_LEVEL_ALERT = 1, + LOG_LEVEL_CRIT = 2, + LOG_LEVEL_ERR = 3, + LOG_LEVEL_WARNING = 4, + LOG_LEVEL_NOTICE = 5, + LOG_LEVEL_INFO = 6, + LOG_LEVEL_DEBUG_SYSTEM = 7, + LOG_LEVEL_DEBUG_PROGRAM = 8, + LOG_LEVEL_DEBUG_PROCESS = 9, + LOG_LEVEL_DEBUG_MODULE = 10, + LOG_LEVEL_DEBUG_UNIT = 11, + LOG_LEVEL_DEBUG_FUNCTION = 12, + LOG_LEVEL_DEBUG_LINE = 13, + LOG_LEVEL_DEBUG = 14, + _NR_LOGLEVELS = 15, +}; + +/* Prefixes of class aliases */ +#define _PREFIX_ALIAS 'a' +#define _PREFIX_ENUM 'e' +#define _PREFIX_STRUCT 's' +#define _PREFIX_VARIANT 'v' + +/* First entry in a BT list */ +#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member) \ + bt_list_entry((_ptr)->next, _class, _member) + +#define _BT_LOGE_DUP_ATTR(_node, _attr, _entity) \ + _BT_LOGE_LINENO((_node)->lineno, \ + "Duplicate attribute in %s: attr-name=\"%s\"", \ + _entity, _attr) + +#define _BT_LOGE_NODE(_node, _msg, args...) \ + _BT_LOGE_LINENO((_node)->lineno, _msg, ## args) + +#define _BT_LOGW_NODE(_node, _msg, args...) \ + _BT_LOGW_LINENO((_node)->lineno, _msg, ## args) + +#define _BT_LOGV_NODE(_node, _msg, args...) \ + _BT_LOGV_LINENO((_node)->lineno, _msg, ## args) + +/* + * Declaration scope of a visitor context. This represents a TSDL + * lexical scope, so that aliases and named structures, variants, + * and enumerations may be registered and looked up hierarchically. + */ +struct ctx_decl_scope { + /* + * Alias name to field class. + * + * GQuark -> struct ctf_field_class * (owned by this) + */ + GHashTable *decl_map; + + /* Parent scope; NULL if this is the root declaration scope */ + struct ctx_decl_scope *parent_scope; +}; + +/* + * Visitor context (private). + */ +struct ctx { + bt_self_component_source *self_comp; + /* Trace IR trace class being filled (owned by this) */ + bt_trace_class *trace_class; + + /* CTF meta trace being filled (owned by this) */ + struct ctf_trace_class *ctf_tc; + + /* Current declaration scope (top of the stack) (owned by this) */ + struct ctx_decl_scope *current_scope; + + /* True if trace declaration is visited */ + bool is_trace_visited; + + /* True if this is an LTTng trace */ + bool is_lttng; + + /* Config passed by the user */ + struct ctf_metadata_decoder_config decoder_config; +}; + +/* + * Visitor (public). + */ +struct ctf_visitor_generate_ir; + +/** + * Creates a new declaration scope. + * + * @param par_scope Parent scope (NULL if creating a root scope) + * @returns New declaration scope, or NULL on error + */ +static +struct ctx_decl_scope *ctx_decl_scope_create(struct ctx_decl_scope *par_scope) +{ + struct ctx_decl_scope *scope; + + scope = g_new(struct ctx_decl_scope, 1); + if (!scope) { + BT_LOGE_STR("Failed to allocate one declaration scope."); + goto end; + } + + scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) ctf_field_class_destroy); + scope->parent_scope = par_scope; + +end: + return scope; +} + +/** + * Destroys a declaration scope. + * + * This function does not destroy the parent scope. + * + * @param scope Scope to destroy + */ +static +void ctx_decl_scope_destroy(struct ctx_decl_scope *scope) +{ + if (!scope) { + goto end; + } + + g_hash_table_destroy(scope->decl_map); + g_free(scope); + +end: + return; +} + +/** + * Returns the GQuark of a prefixed alias. + * + * @param prefix Prefix character + * @param name Name + * @returns Associated GQuark, or 0 on error + */ +static +GQuark get_prefixed_named_quark(char prefix, const char *name) +{ + GQuark qname = 0; + + BT_ASSERT(name); + + /* Prefix character + original string + '\0' */ + char *prname = g_new(char, strlen(name) + 2); + if (!prname) { + BT_LOGE_STR("Failed to allocate a string."); + goto end; + } + + sprintf(prname, "%c%s", prefix, name); + qname = g_quark_from_string(prname); + g_free(prname); + +end: + return qname; +} + +/** + * Looks up a prefixed class alias within a declaration scope. + * + * @param scope Declaration scope + * @param prefix Prefix character + * @param name Alias name + * @param levels Number of levels to dig into (-1 means infinite) + * @param copy True to return a copy + * @returns Declaration (owned by caller if \p copy is true), + * or NULL if not found + */ +static +struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias( + struct ctx_decl_scope *scope, char prefix, const char *name, + int levels, bool copy) +{ + GQuark qname = 0; + int cur_levels = 0; + struct ctf_field_class *decl = NULL; + struct ctx_decl_scope *cur_scope = scope; + + BT_ASSERT(scope); + BT_ASSERT(name); + qname = get_prefixed_named_quark(prefix, name); + if (!qname) { + goto end; + } + + if (levels < 0) { + levels = INT_MAX; + } + + while (cur_scope && cur_levels < levels) { + decl = g_hash_table_lookup(cur_scope->decl_map, + (gconstpointer) GUINT_TO_POINTER(qname)); + if (decl) { + /* Caller's reference */ + if (copy) { + decl = ctf_field_class_copy(decl); + BT_ASSERT(decl); + } + + goto end; + } + + cur_scope = cur_scope->parent_scope; + cur_levels++; + } + +end: + return decl; +} + +/** + * Looks up a class alias within a declaration scope. + * + * @param scope Declaration scope + * @param name Alias name + * @param levels Number of levels to dig into (-1 means infinite) + * @param copy True to return a copy + * @returns Declaration (owned by caller if \p copy is true), + * or NULL if not found + */ +static +struct ctf_field_class *ctx_decl_scope_lookup_alias( + struct ctx_decl_scope *scope, const char *name, int levels, + bool copy) +{ + return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS, + name, levels, copy); +} + +/** + * Looks up an enumeration within a declaration scope. + * + * @param scope Declaration scope + * @param name Enumeration name + * @param levels Number of levels to dig into (-1 means infinite) + * @param copy True to return a copy + * @returns Declaration (owned by caller if \p copy is true), + * or NULL if not found + */ +static +struct ctf_field_class_enum *ctx_decl_scope_lookup_enum( + struct ctx_decl_scope *scope, const char *name, int levels, + bool copy) +{ + return (void *) ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM, + name, levels, copy); +} + +/** + * Looks up a structure within a declaration scope. + * + * @param scope Declaration scope + * @param name Structure name + * @param levels Number of levels to dig into (-1 means infinite) + * @param copy True to return a copy + * @returns Declaration (owned by caller if \p copy is true), + * or NULL if not found + */ +static +struct ctf_field_class_struct *ctx_decl_scope_lookup_struct( + struct ctx_decl_scope *scope, const char *name, int levels, + bool copy) +{ + return (void *) ctx_decl_scope_lookup_prefix_alias(scope, + _PREFIX_STRUCT, name, levels, copy); +} + +/** + * Looks up a variant within a declaration scope. + * + * @param scope Declaration scope + * @param name Variant name + * @param levels Number of levels to dig into (-1 means infinite) + * @param copy True to return a copy + * @returns Declaration (owned by caller if \p copy is true), + * or NULL if not found + */ +static +struct ctf_field_class_variant *ctx_decl_scope_lookup_variant( + struct ctx_decl_scope *scope, const char *name, int levels, + bool copy) +{ + return (void *) ctx_decl_scope_lookup_prefix_alias(scope, + _PREFIX_VARIANT, name, levels, copy); +} + +/** + * Registers a prefixed class alias within a declaration scope. + * + * @param scope Declaration scope + * @param prefix Prefix character + * @param name Alias name (non-NULL) + * @param decl Field class to register (copied) + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope, + char prefix, const char *name, struct ctf_field_class *decl) +{ + int ret = 0; + GQuark qname = 0; + + BT_ASSERT(scope); + BT_ASSERT(name); + BT_ASSERT(decl); + qname = get_prefixed_named_quark(prefix, name); + if (!qname) { + ret = -ENOMEM; + goto end; + } + + /* Make sure alias does not exist in local scope */ + if (ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1, + false)) { + ret = -EEXIST; + goto end; + } + + decl = ctf_field_class_copy(decl); + BT_ASSERT(decl); + g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl); + +end: + return ret; +} + +/** + * Registers a class alias within a declaration scope. + * + * @param scope Declaration scope + * @param name Alias name (non-NULL) + * @param decl Field class to register (copied) + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope, + const char *name, struct ctf_field_class *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ALIAS, + name, (void *) decl); +} + +/** + * Registers an enumeration declaration within a declaration scope. + * + * @param scope Declaration scope + * @param name Enumeration name (non-NULL) + * @param decl Enumeration field class to register (copied) + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope, + const char *name, struct ctf_field_class_enum *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM, + name, (void *) decl); +} + +/** + * Registers a structure declaration within a declaration scope. + * + * @param scope Declaration scope + * @param name Structure name (non-NULL) + * @param decl Structure field class to register (copied) + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope, + const char *name, struct ctf_field_class_struct *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT, + name, (void *) decl); +} + +/** + * Registers a variant declaration within a declaration scope. + * + * @param scope Declaration scope + * @param name Variant name (non-NULL) + * @param decl Variant field class to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope, + const char *name, struct ctf_field_class_variant *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT, + name, (void *) decl); +} + +/** + * Destroys a visitor context. + * + * @param ctx Visitor context to destroy + */ +static +void ctx_destroy(struct ctx *ctx) +{ + struct ctx_decl_scope *scope; + + if (!ctx) { + goto end; + } + + scope = ctx->current_scope; + + /* + * Destroy all scopes, from current one to the root scope. + */ + while (scope) { + struct ctx_decl_scope *parent_scope = scope->parent_scope; + + ctx_decl_scope_destroy(scope); + scope = parent_scope; + } + + bt_trace_class_put_ref(ctx->trace_class); + + if (ctx->ctf_tc) { + ctf_trace_class_destroy(ctx->ctf_tc); + } + + g_free(ctx); + +end: + return; +} + +/** + * Creates a new visitor context. + * + * @param trace Associated trace + * @returns New visitor context, or NULL on error + */ +static +struct ctx *ctx_create(bt_self_component_source *self_comp, + const struct ctf_metadata_decoder_config *decoder_config) +{ + struct ctx *ctx = NULL; + + BT_ASSERT(decoder_config); + + ctx = g_new0(struct ctx, 1); + if (!ctx) { + BT_LOGE_STR("Failed to allocate one visitor context."); + goto error; + } + + if (self_comp) { + ctx->trace_class = bt_trace_class_create( + bt_self_component_source_as_self_component(self_comp)); + if (!ctx->trace_class) { + BT_LOGE_STR("Cannot create empty trace class."); + goto error; + } + ctx->self_comp = self_comp; + } + + ctx->ctf_tc = ctf_trace_class_create(); + if (!ctx->ctf_tc) { + BT_LOGE_STR("Cannot create CTF trace class."); + goto error; + } + + /* Root declaration scope */ + ctx->current_scope = ctx_decl_scope_create(NULL); + if (!ctx->current_scope) { + BT_LOGE_STR("Cannot create declaration scope."); + goto error; + } + + ctx->decoder_config = *decoder_config; + goto end; + +error: + ctx_destroy(ctx); + ctx = NULL; + +end: + return ctx; +} + +/** + * Pushes a new declaration scope on top of a visitor context's + * declaration scope stack. + * + * @param ctx Visitor context + * @returns 0 on success, or a negative value on error + */ +static +int ctx_push_scope(struct ctx *ctx) +{ + int ret = 0; + struct ctx_decl_scope *new_scope; + + BT_ASSERT(ctx); + new_scope = ctx_decl_scope_create(ctx->current_scope); + if (!new_scope) { + BT_LOGE_STR("Cannot create declaration scope."); + ret = -ENOMEM; + goto end; + } + + ctx->current_scope = new_scope; + +end: + return ret; +} + +static +void ctx_pop_scope(struct ctx *ctx) +{ + struct ctx_decl_scope *parent_scope = NULL; + + BT_ASSERT(ctx); + + if (!ctx->current_scope) { + goto end; + } + + parent_scope = ctx->current_scope->parent_scope; + ctx_decl_scope_destroy(ctx->current_scope); + ctx->current_scope = parent_scope; + +end: + return; +} + +static +int visit_field_class_specifier_list(struct ctx *ctx, struct ctf_node *ts_list, + struct ctf_field_class **decl); + +static +char *remove_underscores_from_field_ref(const char *field_ref) +{ + const char *in_ch; + char *out_ch; + char *ret; + enum { + UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE, + UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE, + } state = UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE; + + BT_ASSERT(field_ref); + ret = calloc(strlen(field_ref) + 1, 1); + if (!ret) { + BT_LOGE("Failed to allocate a string: size=%zu", + strlen(field_ref) + 1); + goto end; + } + + in_ch = field_ref; + out_ch = ret; + + while (*in_ch != '\0') { + switch (*in_ch) { + case ' ': + case '\t': + /* Remove whitespace */ + in_ch++; + continue; + case '_': + if (state == UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE) { + in_ch++; + state = UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE; + continue; + } + + goto copy; + case '.': + state = UNDERSCORE_REMOVE_STATE_REMOVE_NEXT_UNDERSCORE; + goto copy; + default: + state = UNDERSCORE_REMOVE_STATE_DO_NOT_REMOVE_NEXT_UNDERSCORE; + goto copy; + } + +copy: + *out_ch = *in_ch; + in_ch++; + out_ch++; + } + +end: + return ret; +} + +static +int is_unary_string(struct bt_list_head *head) +{ + int ret = TRUE; + struct ctf_node *node; + + bt_list_for_each_entry(node, head, siblings) { + if (node->type != NODE_UNARY_EXPRESSION) { + ret = FALSE; + } + + if (node->u.unary_expression.type != UNARY_STRING) { + ret = FALSE; + } + } + + return ret; +} + +static +char *concatenate_unary_strings(struct bt_list_head *head) +{ + int i = 0; + GString *str; + struct ctf_node *node; + + str = g_string_new(NULL); + BT_ASSERT(str); + + bt_list_for_each_entry(node, head, siblings) { + char *src_string; + + if ( + node->type != NODE_UNARY_EXPRESSION || + node->u.unary_expression.type != UNARY_STRING || + !( + ( + node->u.unary_expression.link != + UNARY_LINK_UNKNOWN + ) ^ (i == 0) + ) + ) { + goto error; + } + + switch (node->u.unary_expression.link) { + case UNARY_DOTLINK: + g_string_append(str, "."); + break; + case UNARY_ARROWLINK: + g_string_append(str, "->"); + break; + case UNARY_DOTDOTDOT: + g_string_append(str, "..."); + break; + default: + break; + } + + src_string = node->u.unary_expression.u.string; + g_string_append(str, src_string); + i++; + } + + /* Destroys the container, returns the underlying string */ + return g_string_free(str, FALSE); + +error: + /* This always returns NULL */ + return g_string_free(str, TRUE); +} + +static +const char *get_map_clock_name_value(struct bt_list_head *head) +{ + int i = 0; + struct ctf_node *node; + const char *name = NULL; + + bt_list_for_each_entry(node, head, siblings) { + char *src_string; + int uexpr_type = node->u.unary_expression.type; + int uexpr_link = node->u.unary_expression.link; + int cond = node->type != NODE_UNARY_EXPRESSION || + uexpr_type != UNARY_STRING || + !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0)); + if (cond) { + goto error; + } + + /* Needs to be chained with . */ + switch (node->u.unary_expression.link) { + case UNARY_DOTLINK: + break; + case UNARY_ARROWLINK: + case UNARY_DOTDOTDOT: + goto error; + default: + break; + } + + src_string = node->u.unary_expression.u.string; + + switch (i) { + case 0: + if (strcmp("clock", src_string)) { + goto error; + } + break; + case 1: + name = src_string; + break; + case 2: + if (strcmp("value", src_string)) { + goto error; + } + break; + default: + /* Extra identifier, unknown */ + goto error; + } + + i++; + } + + return name; + +error: + return NULL; +} + +static +int is_unary_unsigned(struct bt_list_head *head) +{ + int ret = TRUE; + struct ctf_node *node; + + bt_list_for_each_entry(node, head, siblings) { + if (node->type != NODE_UNARY_EXPRESSION) { + ret = FALSE; + } + + if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) { + ret = FALSE; + } + } + + return ret; +} + +static +int get_unary_unsigned(struct bt_list_head *head, uint64_t *value) +{ + int i = 0; + int ret = 0; + struct ctf_node *node; + + *value = 0; + + if (bt_list_empty(head)) { + ret = -1; + goto end; + } + + bt_list_for_each_entry(node, head, siblings) { + int uexpr_type = node->u.unary_expression.type; + int uexpr_link = node->u.unary_expression.link; + int cond = node->type != NODE_UNARY_EXPRESSION || + uexpr_type != UNARY_UNSIGNED_CONSTANT || + uexpr_link != UNARY_LINK_UNKNOWN || i != 0; + if (cond) { + _BT_LOGE_NODE(node, "Invalid constant unsigned integer."); + ret = -EINVAL; + goto end; + } + + *value = node->u.unary_expression.u.unsigned_constant; + i++; + } + +end: + return ret; +} + +static +int is_unary_signed(struct bt_list_head *head) +{ + int ret = TRUE; + struct ctf_node *node; + + bt_list_for_each_entry(node, head, siblings) { + if (node->type != NODE_UNARY_EXPRESSION) { + ret = FALSE; + } + + if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) { + ret = FALSE; + } + } + + return ret; +} + +static +int get_unary_signed(struct bt_list_head *head, int64_t *value) +{ + int i = 0; + int ret = 0; + struct ctf_node *node; + + bt_list_for_each_entry(node, head, siblings) { + int uexpr_type = node->u.unary_expression.type; + int uexpr_link = node->u.unary_expression.link; + int cond = node->type != NODE_UNARY_EXPRESSION || + (uexpr_type != UNARY_UNSIGNED_CONSTANT && + uexpr_type != UNARY_SIGNED_CONSTANT) || + uexpr_link != UNARY_LINK_UNKNOWN || i != 0; + if (cond) { + ret = -EINVAL; + goto end; + } + + switch (uexpr_type) { + case UNARY_UNSIGNED_CONSTANT: + *value = (int64_t) + node->u.unary_expression.u.unsigned_constant; + break; + case UNARY_SIGNED_CONSTANT: + *value = node->u.unary_expression.u.signed_constant; + break; + default: + ret = -EINVAL; + goto end; + } + + i++; + } + +end: + return ret; +} + +static +int get_unary_uuid(struct bt_list_head *head, unsigned char *uuid) +{ + int i = 0; + int ret = 0; + struct ctf_node *node; + + bt_list_for_each_entry(node, head, siblings) { + int uexpr_type = node->u.unary_expression.type; + int uexpr_link = node->u.unary_expression.link; + const char *src_string; + + if (node->type != NODE_UNARY_EXPRESSION || + uexpr_type != UNARY_STRING || + uexpr_link != UNARY_LINK_UNKNOWN || + i != 0) { + ret = -EINVAL; + goto end; + } + + src_string = node->u.unary_expression.u.string; + ret = bt_uuid_parse(src_string, uuid); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot parse UUID: uuid=\"%s\"", src_string); + goto end; + } + } + +end: + return ret; +} + +static +int get_boolean(struct ctf_node *unary_expr) +{ + int ret = 0; + + if (unary_expr->type != NODE_UNARY_EXPRESSION) { + _BT_LOGE_NODE(unary_expr, + "Expecting unary expression: node-type=%d", + unary_expr->type); + ret = -EINVAL; + goto end; + } + + switch (unary_expr->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0); + break; + case UNARY_SIGNED_CONSTANT: + ret = (unary_expr->u.unary_expression.u.signed_constant != 0); + break; + case UNARY_STRING: + { + const char *str = unary_expr->u.unary_expression.u.string; + + if (!strcmp(str, "true") || !strcmp(str, "TRUE")) { + ret = TRUE; + } else if (!strcmp(str, "false") || !strcmp(str, "FALSE")) { + ret = FALSE; + } else { + _BT_LOGE_NODE(unary_expr, + "Unexpected boolean value: value=\"%s\"", str); + ret = -EINVAL; + goto end; + } + break; + } + default: + _BT_LOGE_NODE(unary_expr, + "Unexpected unary expression type: node-type=%d", + unary_expr->u.unary_expression.type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +enum ctf_byte_order byte_order_from_unary_expr(struct ctf_node *unary_expr) +{ + const char *str; + enum ctf_byte_order bo = -1; + + if (unary_expr->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(unary_expr, + "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`."); + goto end; + } + + str = unary_expr->u.unary_expression.u.string; + + if (!strcmp(str, "be") || !strcmp(str, "network")) { + bo = CTF_BYTE_ORDER_BIG; + } else if (!strcmp(str, "le")) { + bo = CTF_BYTE_ORDER_LITTLE; + } else if (!strcmp(str, "native")) { + bo = CTF_BYTE_ORDER_DEFAULT; + } else { + _BT_LOGE_NODE(unary_expr, + "Unexpected \"byte_order\" attribute value: " + "expecting `be`, `le`, `network`, or `native`: value=\"%s\"", + str); + goto end; + } + +end: + return bo; +} + +static +enum ctf_byte_order get_real_byte_order(struct ctx *ctx, + struct ctf_node *uexpr) +{ + enum ctf_byte_order bo = byte_order_from_unary_expr(uexpr); + + if (bo == CTF_BYTE_ORDER_DEFAULT) { + bo = ctx->ctf_tc->default_byte_order; + } + + return bo; +} + +static +int is_align_valid(uint64_t align) +{ + return (align != 0) && !(align & (align - UINT64_C(1))); +} + +static +int get_class_specifier_name(struct ctx *ctx, struct ctf_node *cls_specifier, + GString *str) +{ + int ret = 0; + + if (cls_specifier->type != NODE_TYPE_SPECIFIER) { + _BT_LOGE_NODE(cls_specifier, + "Unexpected node type: node-type=%d", + cls_specifier->type); + ret = -EINVAL; + goto end; + } + + switch (cls_specifier->u.field_class_specifier.type) { + case TYPESPEC_VOID: + g_string_append(str, "void"); + break; + case TYPESPEC_CHAR: + g_string_append(str, "char"); + break; + case TYPESPEC_SHORT: + g_string_append(str, "short"); + break; + case TYPESPEC_INT: + g_string_append(str, "int"); + break; + case TYPESPEC_LONG: + g_string_append(str, "long"); + break; + case TYPESPEC_FLOAT: + g_string_append(str, "float"); + break; + case TYPESPEC_DOUBLE: + g_string_append(str, "double"); + break; + case TYPESPEC_SIGNED: + g_string_append(str, "signed"); + break; + case TYPESPEC_UNSIGNED: + g_string_append(str, "unsigned"); + break; + case TYPESPEC_BOOL: + g_string_append(str, "bool"); + break; + case TYPESPEC_COMPLEX: + g_string_append(str, "_Complex"); + break; + case TYPESPEC_IMAGINARY: + g_string_append(str, "_Imaginary"); + break; + case TYPESPEC_CONST: + g_string_append(str, "const"); + break; + case TYPESPEC_ID_TYPE: + if (cls_specifier->u.field_class_specifier.id_type) { + g_string_append(str, + cls_specifier->u.field_class_specifier.id_type); + } + break; + case TYPESPEC_STRUCT: + { + struct ctf_node *node = cls_specifier->u.field_class_specifier.node; + + if (!node->u._struct.name) { + _BT_LOGE_NODE(node, "Unexpected empty structure field class name."); + ret = -EINVAL; + goto end; + } + + g_string_append(str, "struct "); + g_string_append(str, node->u._struct.name); + break; + } + case TYPESPEC_VARIANT: + { + struct ctf_node *node = cls_specifier->u.field_class_specifier.node; + + if (!node->u.variant.name) { + _BT_LOGE_NODE(node, "Unexpected empty variant field class name."); + ret = -EINVAL; + goto end; + } + + g_string_append(str, "variant "); + g_string_append(str, node->u.variant.name); + break; + } + case TYPESPEC_ENUM: + { + struct ctf_node *node = cls_specifier->u.field_class_specifier.node; + + if (!node->u._enum.enum_id) { + _BT_LOGE_NODE(node, + "Unexpected empty enumeration field class (`enum`) name."); + ret = -EINVAL; + goto end; + } + + g_string_append(str, "enum "); + g_string_append(str, node->u._enum.enum_id); + break; + } + case TYPESPEC_FLOATING_POINT: + case TYPESPEC_INTEGER: + case TYPESPEC_STRING: + default: + _BT_LOGE_NODE(cls_specifier->u.field_class_specifier.node, + "Unexpected field class specifier type: %d", + cls_specifier->u.field_class_specifier.type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int get_class_specifier_list_name(struct ctx *ctx, + struct ctf_node *cls_specifier_list, GString *str) +{ + int ret = 0; + struct ctf_node *iter; + int alias_item_nr = 0; + struct bt_list_head *head = + &cls_specifier_list->u.field_class_specifier_list.head; + + bt_list_for_each_entry(iter, head, siblings) { + if (alias_item_nr != 0) { + g_string_append(str, " "); + } + + alias_item_nr++; + ret = get_class_specifier_name(ctx, iter, str); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +GQuark create_class_alias_identifier(struct ctx *ctx, + struct ctf_node *cls_specifier_list, + struct ctf_node *node_field_class_declarator) +{ + int ret; + char *str_c; + GString *str; + GQuark qalias = 0; + struct ctf_node *iter; + struct bt_list_head *pointers = + &node_field_class_declarator->u.field_class_declarator.pointers; + + str = g_string_new(""); + ret = get_class_specifier_list_name(ctx, cls_specifier_list, str); + if (ret) { + g_string_free(str, TRUE); + goto end; + } + + bt_list_for_each_entry(iter, pointers, siblings) { + g_string_append(str, " *"); + + if (iter->u.pointer.const_qualifier) { + g_string_append(str, " const"); + } + } + + str_c = g_string_free(str, FALSE); + qalias = g_quark_from_string(str_c); + g_free(str_c); + +end: + return qalias; +} + +static +int visit_field_class_declarator(struct ctx *ctx, + struct ctf_node *cls_specifier_list, + GQuark *field_name, struct ctf_node *node_field_class_declarator, + struct ctf_field_class **field_decl, + struct ctf_field_class *nested_decl) +{ + /* + * During this whole function, nested_decl is always OURS, + * whereas field_decl is an output which we create, but + * belongs to the caller (it is moved). + */ + int ret = 0; + *field_decl = NULL; + + /* Validate field class declarator node */ + if (node_field_class_declarator) { + if (node_field_class_declarator->u.field_class_declarator.type == + TYPEDEC_UNKNOWN) { + _BT_LOGE_NODE(node_field_class_declarator, + "Unexpected field class declarator type: type=%d", + node_field_class_declarator->u.field_class_declarator.type); + ret = -EINVAL; + goto error; + } + + /* TODO: GCC bitfields not supported yet */ + if (node_field_class_declarator->u.field_class_declarator.bitfield_len != + NULL) { + _BT_LOGE_NODE(node_field_class_declarator, + "GCC bitfields are not supported as of this version."); + ret = -EPERM; + goto error; + } + } + + /* Find the right nested declaration if not provided */ + if (!nested_decl) { + struct bt_list_head *pointers = + &node_field_class_declarator->u.field_class_declarator.pointers; + + if (node_field_class_declarator && !bt_list_empty(pointers)) { + GQuark qalias; + + /* + * If we have a pointer declarator, it HAS to + * be present in the field class aliases (else + * fail). + */ + qalias = create_class_alias_identifier(ctx, + cls_specifier_list, node_field_class_declarator); + nested_decl = + ctx_decl_scope_lookup_alias(ctx->current_scope, + g_quark_to_string(qalias), -1, true); + if (!nested_decl) { + _BT_LOGE_NODE(node_field_class_declarator, + "Cannot find class alias: name=\"%s\"", + g_quark_to_string(qalias)); + ret = -EINVAL; + goto error; + } + + if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) { + /* Pointer: force integer's base to 16 */ + struct ctf_field_class_int *int_fc = + (void *) nested_decl; + + int_fc->disp_base = + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; + } + } else { + ret = visit_field_class_specifier_list(ctx, + cls_specifier_list, &nested_decl); + if (ret) { + BT_ASSERT(!nested_decl); + goto error; + } + } + } + + BT_ASSERT(nested_decl); + + if (!node_field_class_declarator) { + *field_decl = nested_decl; + nested_decl = NULL; + goto end; + } + + if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) { + if (node_field_class_declarator->u.field_class_declarator.u.id) { + const char *id = + node_field_class_declarator->u.field_class_declarator.u.id; + + if (id[0] == '_') { + id++; + } + + *field_name = g_quark_from_string(id); + } else { + *field_name = 0; + } + + *field_decl = nested_decl; + nested_decl = NULL; + goto end; + } else { + struct ctf_node *first; + struct ctf_field_class *decl = NULL; + struct ctf_field_class *outer_field_decl = NULL; + struct bt_list_head *length = + &node_field_class_declarator-> + u.field_class_declarator.u.nested.length; + + /* Create array/sequence, pass nested_decl as child */ + if (bt_list_empty(length)) { + _BT_LOGE_NODE(node_field_class_declarator, + "Expecting length field reference or value."); + ret = -EINVAL; + goto error; + } + + first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings); + if (first->type != NODE_UNARY_EXPRESSION) { + _BT_LOGE_NODE(first, + "Unexpected node type: node-type=%d", + first->type); + ret = -EINVAL; + goto error; + } + + switch (first->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + { + struct ctf_field_class_array *array_decl = NULL; + + array_decl = ctf_field_class_array_create(); + BT_ASSERT(array_decl); + array_decl->length = + first->u.unary_expression.u.unsigned_constant; + array_decl->base.elem_fc = nested_decl; + nested_decl = NULL; + decl = (void *) array_decl; + break; + } + case UNARY_STRING: + { + /* Lookup unsigned integer definition, create seq. */ + struct ctf_field_class_sequence *seq_decl = NULL; + char *length_name = concatenate_unary_strings(length); + + if (!length_name) { + _BT_LOGE_NODE(node_field_class_declarator, + "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (strncmp(length_name, "env.", 4) == 0) { + /* This is, in fact, an array */ + const char *env_entry_name = &length_name[4]; + struct ctf_trace_class_env_entry *env_entry = + ctf_trace_class_borrow_env_entry_by_name( + ctx->ctf_tc, env_entry_name); + struct ctf_field_class_array *array_decl; + + if (!env_entry) { + _BT_LOGE_NODE(node_field_class_declarator, + "Cannot find environment entry: " + "name=\"%s\"", env_entry_name); + ret = -EINVAL; + goto error; + } + + if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) { + _BT_LOGE_NODE(node_field_class_declarator, + "Wrong environment entry type " + "(expecting integer): " + "name=\"%s\"", env_entry_name); + ret = -EINVAL; + goto error; + } + + if (env_entry->value.i < 0) { + _BT_LOGE_NODE(node_field_class_declarator, + "Invalid, negative array length: " + "env-entry-name=\"%s\", " + "value=%" PRId64, + env_entry_name, + env_entry->value.i); + ret = -EINVAL; + goto error; + } + + array_decl = ctf_field_class_array_create(); + BT_ASSERT(array_decl); + array_decl->length = + (uint64_t) env_entry->value.i; + array_decl->base.elem_fc = nested_decl; + nested_decl = NULL; + decl = (void *) array_decl; + } else { + char *length_name_no_underscore = + remove_underscores_from_field_ref( + length_name); + if (!length_name_no_underscore) { + /* + * remove_underscores_from_field_ref() + * logs errors + */ + ret = -EINVAL; + goto error; + } + seq_decl = ctf_field_class_sequence_create(); + BT_ASSERT(seq_decl); + seq_decl->base.elem_fc = nested_decl; + nested_decl = NULL; + g_string_assign(seq_decl->length_ref, + length_name_no_underscore); + free(length_name_no_underscore); + decl = (void *) seq_decl; + } + + g_free(length_name); + break; + } + default: + ret = -EINVAL; + goto error; + } + + BT_ASSERT(!nested_decl); + BT_ASSERT(decl); + BT_ASSERT(!*field_decl); + + /* + * At this point, we found the next nested declaration. + * We currently own this (and lost the ownership of + * nested_decl in the meantime). Pass this next + * nested declaration as the content of the outer + * container, MOVING its ownership. + */ + ret = visit_field_class_declarator(ctx, cls_specifier_list, + field_name, + node_field_class_declarator-> + u.field_class_declarator.u.nested.field_class_declarator, + &outer_field_decl, decl); + decl = NULL; + if (ret) { + BT_ASSERT(!outer_field_decl); + ret = -EINVAL; + goto error; + } + + BT_ASSERT(outer_field_decl); + *field_decl = outer_field_decl; + outer_field_decl = NULL; + } + + BT_ASSERT(*field_decl); + goto end; + +error: + ctf_field_class_destroy(*field_decl); + *field_decl = NULL; + + if (ret >= 0) { + ret = -1; + } + +end: + ctf_field_class_destroy(nested_decl); + nested_decl = NULL; + return ret; +} + +static +int visit_struct_decl_field(struct ctx *ctx, + struct ctf_field_class_struct *struct_decl, + struct ctf_node *cls_specifier_list, + struct bt_list_head *field_class_declarators) +{ + int ret = 0; + struct ctf_node *iter; + struct ctf_field_class *field_decl = NULL; + + bt_list_for_each_entry(iter, field_class_declarators, siblings) { + field_decl = NULL; + GQuark qfield_name; + const char *field_name; + + ret = visit_field_class_declarator(ctx, cls_specifier_list, + &qfield_name, iter, &field_decl, NULL); + if (ret) { + BT_ASSERT(!field_decl); + _BT_LOGE_NODE(cls_specifier_list, + "Cannot visit field class declarator: ret=%d", ret); + goto error; + } + + BT_ASSERT(field_decl); + field_name = g_quark_to_string(qfield_name); + + /* Check if field with same name already exists */ + if (ctf_field_class_struct_borrow_member_by_name( + struct_decl, field_name)) { + _BT_LOGE_NODE(cls_specifier_list, + "Duplicate field in structure field class: " + "field-name=\"%s\"", field_name); + ret = -EINVAL; + goto error; + } + + /* Add field to structure */ + ctf_field_class_struct_append_member(struct_decl, + field_name, field_decl); + field_decl = NULL; + } + + return 0; + +error: + ctf_field_class_destroy(field_decl); + field_decl = NULL; + return ret; +} + +static +int visit_variant_decl_field(struct ctx *ctx, + struct ctf_field_class_variant *variant_decl, + struct ctf_node *cls_specifier_list, + struct bt_list_head *field_class_declarators) +{ + int ret = 0; + struct ctf_node *iter; + struct ctf_field_class *field_decl = NULL; + + bt_list_for_each_entry(iter, field_class_declarators, siblings) { + field_decl = NULL; + GQuark qfield_name; + const char *field_name; + + ret = visit_field_class_declarator(ctx, cls_specifier_list, + &qfield_name, iter, &field_decl, NULL); + if (ret) { + BT_ASSERT(!field_decl); + _BT_LOGE_NODE(cls_specifier_list, + "Cannot visit field class declarator: ret=%d", ret); + goto error; + } + + BT_ASSERT(field_decl); + field_name = g_quark_to_string(qfield_name); + + /* Check if field with same name already exists */ + if (ctf_field_class_variant_borrow_option_by_name( + variant_decl, field_name)) { + _BT_LOGE_NODE(cls_specifier_list, + "Duplicate field in variant field class: " + "field-name=\"%s\"", field_name); + ret = -EINVAL; + goto error; + } + + /* Add field to structure */ + ctf_field_class_variant_append_option(variant_decl, + field_name, field_decl); + field_decl = NULL; + } + + return 0; + +error: + ctf_field_class_destroy(field_decl); + field_decl = NULL; + return ret; +} + +static +int visit_field_class_def(struct ctx *ctx, struct ctf_node *cls_specifier_list, + struct bt_list_head *field_class_declarators) +{ + int ret = 0; + GQuark qidentifier; + struct ctf_node *iter; + struct ctf_field_class *class_decl = NULL; + + bt_list_for_each_entry(iter, field_class_declarators, siblings) { + ret = visit_field_class_declarator(ctx, cls_specifier_list, + &qidentifier, iter, &class_decl, NULL); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit field class declarator: ret=%d", ret); + ret = -EINVAL; + goto end; + } + + /* Do not allow field class def and alias of untagged variants */ + if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) { + struct ctf_field_class_variant *var_fc = + (void *) class_decl; + + if (var_fc->tag_path.path->len == 0) { + _BT_LOGE_NODE(iter, + "Type definition of untagged variant field class is not allowed."); + ret = -EPERM; + goto end; + } + } + + ret = ctx_decl_scope_register_alias(ctx->current_scope, + g_quark_to_string(qidentifier), class_decl); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot register field class alias: name=\"%s\"", + g_quark_to_string(qidentifier)); + goto end; + } + } + +end: + ctf_field_class_destroy(class_decl); + class_decl = NULL; + return ret; +} + +static +int visit_field_class_alias(struct ctx *ctx, struct ctf_node *target, + struct ctf_node *alias) +{ + int ret = 0; + GQuark qalias; + struct ctf_node *node; + GQuark qdummy_field_name; + struct ctf_field_class *class_decl = NULL; + + /* Create target field class */ + if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) { + node = NULL; + } else { + node = _BT_LIST_FIRST_ENTRY( + &target->u.field_class_alias_target.field_class_declarators, + struct ctf_node, siblings); + } + + ret = visit_field_class_declarator(ctx, + target->u.field_class_alias_target.field_class_specifier_list, + &qdummy_field_name, node, &class_decl, NULL); + if (ret) { + BT_ASSERT(!class_decl); + _BT_LOGE_NODE(node, + "Cannot visit field class declarator: ret=%d", ret); + goto end; + } + + /* Do not allow field class def and alias of untagged variants */ + if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) { + struct ctf_field_class_variant *var_fc = (void *) class_decl; + + if (var_fc->tag_path.path->len == 0) { + _BT_LOGE_NODE(target, + "Type definition of untagged variant field class is not allowed."); + ret = -EPERM; + goto end; + } + } + + /* + * The semantic validator does not check whether the target is + * abstract or not (if it has an identifier). Check it here. + */ + if (qdummy_field_name != 0) { + _BT_LOGE_NODE(target, + "Expecting empty identifier: id=\"%s\"", + g_quark_to_string(qdummy_field_name)); + ret = -EINVAL; + goto end; + } + + /* Create alias identifier */ + node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators, + struct ctf_node, siblings); + qalias = create_class_alias_identifier(ctx, + alias->u.field_class_alias_name.field_class_specifier_list, node); + ret = ctx_decl_scope_register_alias(ctx->current_scope, + g_quark_to_string(qalias), class_decl); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot register class alias: name=\"%s\"", + g_quark_to_string(qalias)); + goto end; + } + +end: + ctf_field_class_destroy(class_decl); + class_decl = NULL; + return ret; +} + +static +int visit_struct_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct ctf_field_class_struct *struct_decl) +{ + int ret = 0; + + switch (entry_node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, + entry_node->u.field_class_def.field_class_specifier_list, + &entry_node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot add field class found in structure field class: ret=%d", + ret); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target, + entry_node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot add field class alias found in structure field class: ret=%d", + ret); + goto end; + } + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + /* Field */ + ret = visit_struct_decl_field(ctx, struct_decl, + entry_node->u.struct_or_variant_declaration. + field_class_specifier_list, + &entry_node->u.struct_or_variant_declaration. + field_class_declarators); + if (ret) { + goto end; + } + break; + default: + _BT_LOGE_NODE(entry_node, + "Unexpected node type: node-type=%d", entry_node->type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int visit_variant_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct ctf_field_class_variant *variant_decl) +{ + int ret = 0; + + switch (entry_node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, + entry_node->u.field_class_def.field_class_specifier_list, + &entry_node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot add field class found in variant field class: ret=%d", + ret); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target, + entry_node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot add field class alias found in variant field class: ret=%d", + ret); + goto end; + } + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + /* Field */ + ret = visit_variant_decl_field(ctx, variant_decl, + entry_node->u.struct_or_variant_declaration. + field_class_specifier_list, + &entry_node->u.struct_or_variant_declaration. + field_class_declarators); + if (ret) { + goto end; + } + break; + default: + _BT_LOGE_NODE(entry_node, + "Unexpected node type: node-type=%d", + entry_node->type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int visit_struct_decl(struct ctx *ctx, const char *name, + struct bt_list_head *decl_list, int has_body, + struct bt_list_head *min_align, + struct ctf_field_class_struct **struct_decl) +{ + int ret = 0; + + BT_ASSERT(struct_decl); + *struct_decl = NULL; + + /* For named struct (without body), lookup in declaration scope */ + if (!has_body) { + if (!name) { + BT_LOGE_STR("Bodyless structure field class: missing name."); + ret = -EPERM; + goto error; + } + + *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope, + name, -1, true); + if (!*struct_decl) { + BT_LOGE("Cannot find structure field class: name=\"struct %s\"", + name); + ret = -EINVAL; + goto error; + } + } else { + struct ctf_node *entry_node; + uint64_t min_align_value = 0; + + if (name) { + if (ctx_decl_scope_lookup_struct( + ctx->current_scope, name, 1, false)) { + BT_LOGE("Structure field class already declared in local scope: " + "name=\"struct %s\"", name); + ret = -EINVAL; + goto error; + } + } + + if (!bt_list_empty(min_align)) { + ret = get_unary_unsigned(min_align, &min_align_value); + if (ret) { + BT_LOGE("Unexpected unary expression for structure field class's `align` attribute: " + "ret=%d", ret); + goto error; + } + } + + *struct_decl = ctf_field_class_struct_create(); + BT_ASSERT(*struct_decl); + + if (min_align_value != 0) { + (*struct_decl)->base.alignment = min_align_value; + } + + _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + ret = visit_struct_decl_entry(ctx, entry_node, + *struct_decl); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot visit structure field class entry: " + "ret=%d", ret); + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (name) { + ret = ctx_decl_scope_register_struct(ctx->current_scope, + name, *struct_decl); + if (ret) { + BT_LOGE("Cannot register structure field class in declaration scope: " + "name=\"struct %s\", ret=%d", name, ret); + goto error; + } + } + } + + return 0; + +error: + ctf_field_class_destroy((void *) *struct_decl); + *struct_decl = NULL; + return ret; +} + +static +int visit_variant_decl(struct ctx *ctx, const char *name, + const char *tag, struct bt_list_head *decl_list, + int has_body, struct ctf_field_class_variant **variant_decl) +{ + int ret = 0; + struct ctf_field_class_variant *untagged_variant_decl = NULL; + + BT_ASSERT(variant_decl); + *variant_decl = NULL; + + /* For named variant (without body), lookup in declaration scope */ + if (!has_body) { + if (!name) { + BT_LOGE_STR("Bodyless variant field class: missing name."); + ret = -EPERM; + goto error; + } + + untagged_variant_decl = + ctx_decl_scope_lookup_variant(ctx->current_scope, + name, -1, true); + if (!untagged_variant_decl) { + BT_LOGE("Cannot find variant field class: name=\"variant %s\"", + name); + ret = -EINVAL; + goto error; + } + } else { + struct ctf_node *entry_node; + + if (name) { + if (ctx_decl_scope_lookup_variant(ctx->current_scope, + name, 1, false)) { + BT_LOGE("Variant field class already declared in local scope: " + "name=\"variant %s\"", name); + ret = -EINVAL; + goto error; + } + } + + untagged_variant_decl = ctf_field_class_variant_create(); + BT_ASSERT(untagged_variant_decl); + _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + ret = visit_variant_decl_entry(ctx, entry_node, + untagged_variant_decl); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot visit variant field class entry: " + "ret=%d", ret); + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (name) { + ret = ctx_decl_scope_register_variant( + ctx->current_scope, name, + untagged_variant_decl); + if (ret) { + BT_LOGE("Cannot register variant field class in declaration scope: " + "name=\"variant %s\", ret=%d", name, ret); + goto error; + } + } + } + + /* + * If tagged, create tagged variant and return; otherwise + * return untagged variant. + */ + if (!tag) { + *variant_decl = untagged_variant_decl; + untagged_variant_decl = NULL; + } else { + /* + * At this point, we have a fresh untagged variant; nobody + * else owns it. Set its tag now. + */ + char *tag_no_underscore = + remove_underscores_from_field_ref(tag); + + if (!tag_no_underscore) { + /* remove_underscores_from_field_ref() logs errors */ + goto error; + } + + g_string_assign(untagged_variant_decl->tag_ref, + tag_no_underscore); + free(tag_no_underscore); + *variant_decl = untagged_variant_decl; + untagged_variant_decl = NULL; + } + + BT_ASSERT(!untagged_variant_decl); + BT_ASSERT(*variant_decl); + return 0; + +error: + ctf_field_class_destroy((void *) untagged_variant_decl); + untagged_variant_decl = NULL; + ctf_field_class_destroy((void *) *variant_decl); + *variant_decl = NULL; + return ret; +} + +struct uori { + bool is_signed; + union { + uint64_t u; + uint64_t i; + } value; +}; + +static +int visit_enum_decl_entry(struct ctx *ctx, struct ctf_node *enumerator, + struct ctf_field_class_enum *enum_decl, struct uori *last) +{ + int ret = 0; + int nr_vals = 0; + struct ctf_node *iter; + struct uori start = { + .is_signed = false, + .value.u = 0, + }; + struct uori end = { + .is_signed = false, + .value.u = 0, + }; + const char *label = enumerator->u.enumerator.id; + const char *effective_label = label; + struct bt_list_head *values = &enumerator->u.enumerator.values; + + bt_list_for_each_entry(iter, values, siblings) { + struct uori *target; + + if (iter->type != NODE_UNARY_EXPRESSION) { + _BT_LOGE_NODE(iter, + "Wrong expression for enumeration field class label: " + "node-type=%d, label=\"%s\"", iter->type, + label); + ret = -EINVAL; + goto error; + } + + if (nr_vals == 0) { + target = &start; + } else { + target = &end; + } + + switch (iter->u.unary_expression.type) { + case UNARY_SIGNED_CONSTANT: + target->is_signed = true; + target->value.i = + iter->u.unary_expression.u.signed_constant; + break; + case UNARY_UNSIGNED_CONSTANT: + target->is_signed = false; + target->value.u = + iter->u.unary_expression.u.unsigned_constant; + break; + default: + _BT_LOGE_NODE(iter, + "Invalid enumeration field class entry: " + "expecting constant signed or unsigned integer: " + "node-type=%d, label=\"%s\"", + iter->u.unary_expression.type, label); + ret = -EINVAL; + goto error; + } + + if (nr_vals > 1) { + _BT_LOGE_NODE(iter, + "Invalid enumeration field class entry: label=\"%s\"", + label); + ret = -EINVAL; + goto error; + } + + nr_vals++; + } + + if (nr_vals == 0) { + start = *last; + } + + if (nr_vals <= 1) { + end = start; + } + + if (end.is_signed) { + last->value.i = end.value.i + 1; + } else { + last->value.u = end.value.u + 1; + } + + if (label[0] == '_') { + /* + * Strip the first underscore of any enumeration field + * class's label in case this enumeration FC is used as + * a variant FC tag later. The variant FC choice names + * could also start with `_`, in which case the prefix + * is removed, and it the resulting choice name needs to + * match tag labels. + */ + effective_label = &label[1]; + } + + ctf_field_class_enum_append_mapping(enum_decl, effective_label, + start.value.u, end.value.u); + return 0; + +error: + return ret; +} + +static +int visit_enum_decl(struct ctx *ctx, const char *name, + struct ctf_node *container_cls, + struct bt_list_head *enumerator_list, + int has_body, struct ctf_field_class_enum **enum_decl) +{ + int ret = 0; + GQuark qdummy_id; + struct ctf_field_class_int *integer_decl = NULL; + + BT_ASSERT(enum_decl); + *enum_decl = NULL; + + /* For named enum (without body), lookup in declaration scope */ + if (!has_body) { + if (!name) { + BT_LOGE_STR("Bodyless enumeration field class: missing name."); + ret = -EPERM; + goto error; + } + + *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope, + name, -1, true); + if (!*enum_decl) { + BT_LOGE("Cannot find enumeration field class: " + "name=\"enum %s\"", name); + ret = -EINVAL; + goto error; + } + } else { + struct ctf_node *iter; + struct uori last_value = { + .is_signed = false, + .value.u = 0, + }; + + if (name) { + if (ctx_decl_scope_lookup_enum(ctx->current_scope, + name, 1, false)) { + BT_LOGE("Enumeration field class already declared in local scope: " + "name=\"enum %s\"", name); + ret = -EINVAL; + goto error; + } + } + + if (!container_cls) { + integer_decl = (void *) ctx_decl_scope_lookup_alias( + ctx->current_scope, "int", -1, true); + if (!integer_decl) { + BT_LOGE_STR("Cannot find implicit `int` field class alias for enumeration field class."); + ret = -EINVAL; + goto error; + } + } else { + ret = visit_field_class_declarator(ctx, container_cls, + &qdummy_id, NULL, (void *) &integer_decl, + NULL); + if (ret) { + BT_ASSERT(!integer_decl); + ret = -EINVAL; + goto error; + } + } + + BT_ASSERT(integer_decl); + + if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) { + BT_LOGE("Container field class for enumeration field class is not an integer field class: " + "fc-type=%d", integer_decl->base.base.type); + ret = -EINVAL; + goto error; + } + + *enum_decl = ctf_field_class_enum_create(); + BT_ASSERT(*enum_decl); + (*enum_decl)->base.base.base.alignment = + integer_decl->base.base.alignment; + ctf_field_class_int_copy_content((void *) *enum_decl, + (void *) integer_decl); + last_value.is_signed = (*enum_decl)->base.is_signed; + + bt_list_for_each_entry(iter, enumerator_list, siblings) { + ret = visit_enum_decl_entry(ctx, iter, *enum_decl, + &last_value); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit enumeration field class entry: " + "ret=%d", ret); + goto error; + } + } + + if (name) { + ret = ctx_decl_scope_register_enum(ctx->current_scope, + name, *enum_decl); + if (ret) { + BT_LOGE("Cannot register enumeration field class in declaration scope: " + "ret=%d", ret); + goto error; + } + } + } + + goto end; + +error: + ctf_field_class_destroy((void *) *enum_decl); + *enum_decl = NULL; + +end: + ctf_field_class_destroy((void *) integer_decl); + integer_decl = NULL; + return ret; +} + +static +int visit_field_class_specifier(struct ctx *ctx, + struct ctf_node *cls_specifier_list, + struct ctf_field_class **decl) +{ + int ret = 0; + GString *str = NULL; + + *decl = NULL; + str = g_string_new(""); + ret = get_class_specifier_list_name(ctx, cls_specifier_list, str); + if (ret) { + _BT_LOGE_NODE(cls_specifier_list, + "Cannot get field class specifier list's name: ret=%d", ret); + goto error; + } + + *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1, + true); + if (!*decl) { + _BT_LOGE_NODE(cls_specifier_list, + "Cannot find field class alias: name=\"%s\"", str->str); + ret = -EINVAL; + goto error; + } + + goto end; + +error: + ctf_field_class_destroy(*decl); + *decl = NULL; + +end: + if (str) { + g_string_free(str, TRUE); + } + + return ret; +} + +static +int visit_integer_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct ctf_field_class_int **integer_decl) +{ + int set = 0; + int ret = 0; + int signedness = 0; + struct ctf_node *expression; + uint64_t alignment = 0, size = 0; + struct ctf_clock_class *mapped_clock_class = NULL; + enum ctf_encoding encoding = CTF_ENCODING_NONE; + bt_field_class_integer_preferred_display_base base = + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; + enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order; + + *integer_decl = NULL; + + bt_list_for_each_entry(expression, expressions, siblings) { + struct ctf_node *left, *right; + + left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, + struct ctf_node, siblings); + right = _BT_LIST_FIRST_ENTRY( + &expression->u.ctf_expression.right, struct ctf_node, + siblings); + + if (left->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(left, + "Unexpected unary expression type: type=%d", + left->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "signed")) { + if (_IS_SET(&set, _INTEGER_SIGNED_SET)) { + _BT_LOGE_DUP_ATTR(left, "signed", + "integer field class"); + ret = -EPERM; + goto error; + } + + signedness = get_boolean(right); + if (signedness < 0) { + _BT_LOGE_NODE(right, + "Invalid boolean value for integer field class's `signed` attribute: " + "ret=%d", ret); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_SIGNED_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "byte_order")) { + if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) { + _BT_LOGE_DUP_ATTR(left, "byte_order", + "integer field class"); + ret = -EPERM; + goto error; + } + + byte_order = get_real_byte_order(ctx, right); + if (byte_order == -1) { + _BT_LOGE_NODE(right, + "Invalid `byte_order` attribute in integer field class: " + "ret=%d", ret); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_BYTE_ORDER_SET); + } else if (!strcmp(left->u.unary_expression.u.string, "size")) { + if (_IS_SET(&set, _INTEGER_SIZE_SET)) { + _BT_LOGE_DUP_ATTR(left, "size", + "integer field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _BT_LOGE_NODE(right, + "Invalid `size` attribute in integer field class: " + "expecting unsigned constant integer: " + "node-type=%d", + right->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + size = right->u.unary_expression.u.unsigned_constant; + if (size == 0) { + _BT_LOGE_NODE(right, + "Invalid `size` attribute in integer field class: " + "expecting positive constant integer: " + "size=%" PRIu64, size); + ret = -EINVAL; + goto error; + } else if (size > 64) { + _BT_LOGE_NODE(right, + "Invalid `size` attribute in integer field class: " + "integer fields over 64 bits are not supported as of this version: " + "size=%" PRIu64, size); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_SIZE_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "align")) { + if (_IS_SET(&set, _INTEGER_ALIGN_SET)) { + _BT_LOGE_DUP_ATTR(left, "align", + "integer field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _BT_LOGE_NODE(right, + "Invalid `align` attribute in integer field class: " + "expecting unsigned constant integer: " + "node-type=%d", + right->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + alignment = + right->u.unary_expression.u.unsigned_constant; + if (!is_align_valid(alignment)) { + _BT_LOGE_NODE(right, + "Invalid `align` attribute in integer field class: " + "expecting power of two: " + "align=%" PRIu64, alignment); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_ALIGN_SET); + } else if (!strcmp(left->u.unary_expression.u.string, "base")) { + if (_IS_SET(&set, _INTEGER_BASE_SET)) { + _BT_LOGE_DUP_ATTR(left, "base", + "integer field class"); + ret = -EPERM; + goto error; + } + + switch (right->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + { + uint64_t constant = right->u.unary_expression. + u.unsigned_constant; + + switch (constant) { + case 2: + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY; + break; + case 8: + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL; + break; + case 10: + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; + break; + case 16: + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; + break; + default: + _BT_LOGE_NODE(right, + "Invalid `base` attribute in integer field class: " + "base=%" PRIu64, + right->u.unary_expression.u.unsigned_constant); + ret = -EINVAL; + goto error; + } + break; + } + case UNARY_STRING: + { + char *s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _BT_LOGE_NODE(right, + "Unexpected unary expression for integer field class's `base` attribute."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "decimal") || + !strcmp(s_right, "dec") || + !strcmp(s_right, "d") || + !strcmp(s_right, "i") || + !strcmp(s_right, "u")) { + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL; + } else if (!strcmp(s_right, "hexadecimal") || + !strcmp(s_right, "hex") || + !strcmp(s_right, "x") || + !strcmp(s_right, "X") || + !strcmp(s_right, "p")) { + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL; + } else if (!strcmp(s_right, "octal") || + !strcmp(s_right, "oct") || + !strcmp(s_right, "o")) { + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL; + } else if (!strcmp(s_right, "binary") || + !strcmp(s_right, "b")) { + base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY; + } else { + _BT_LOGE_NODE(right, + "Unexpected unary expression for integer field class's `base` attribute: " + "base=\"%s\"", s_right); + g_free(s_right); + ret = -EINVAL; + goto error; + } + + g_free(s_right); + break; + } + default: + _BT_LOGE_NODE(right, + "Invalid `base` attribute in integer field class: " + "expecting unsigned constant integer or unary string."); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_BASE_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "encoding")) { + char *s_right; + + if (_IS_SET(&set, _INTEGER_ENCODING_SET)) { + _BT_LOGE_DUP_ATTR(left, "encoding", + "integer field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(right, + "Invalid `encoding` attribute in integer field class: " + "expecting unary string."); + ret = -EINVAL; + goto error; + } + + s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _BT_LOGE_NODE(right, + "Unexpected unary expression for integer field class's `encoding` attribute."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "UTF8") || + !strcmp(s_right, "utf8") || + !strcmp(s_right, "utf-8") || + !strcmp(s_right, "UTF-8") || + !strcmp(s_right, "ASCII") || + !strcmp(s_right, "ascii")) { + encoding = CTF_ENCODING_UTF8; + } else if (!strcmp(s_right, "none")) { + encoding = CTF_ENCODING_NONE; + } else { + _BT_LOGE_NODE(right, + "Invalid `encoding` attribute in integer field class: " + "unknown encoding: encoding=\"%s\"", + s_right); + g_free(s_right); + ret = -EINVAL; + goto error; + } + + g_free(s_right); + _SET(&set, _INTEGER_ENCODING_SET); + } else if (!strcmp(left->u.unary_expression.u.string, "map")) { + const char *clock_name; + + if (_IS_SET(&set, _INTEGER_MAP_SET)) { + _BT_LOGE_DUP_ATTR(left, "map", + "integer field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(right, + "Invalid `map` attribute in integer field class: " + "expecting unary string."); + ret = -EINVAL; + goto error; + } + + clock_name = + get_map_clock_name_value( + &expression->u.ctf_expression.right); + if (!clock_name) { + char *s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + + if (!s_right) { + _BT_LOGE_NODE(right, + "Unexpected unary expression for integer field class's `map` attribute."); + ret = -EINVAL; + goto error; + } + + _BT_LOGE_NODE(right, + "Invalid `map` attribute in integer field class: " + "cannot find clock class at this point: name=\"%s\"", + s_right); + _SET(&set, _INTEGER_MAP_SET); + g_free(s_right); + continue; + } + + mapped_clock_class = + ctf_trace_class_borrow_clock_class_by_name( + ctx->ctf_tc, clock_name); + if (!mapped_clock_class) { + _BT_LOGE_NODE(right, + "Invalid `map` attribute in integer field class: " + "cannot find clock class at this point: name=\"%s\"", + clock_name); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_MAP_SET); + } else { + _BT_LOGW_NODE(left, + "Unknown attribute in integer field class: " + "attr-name=\"%s\"", + left->u.unary_expression.u.string); + } + } + + if (!_IS_SET(&set, _INTEGER_SIZE_SET)) { + BT_LOGE_STR("Missing `size` attribute in integer field class."); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) { + if (size % CHAR_BIT) { + /* Bit-packed alignment */ + alignment = 1; + } else { + /* Byte-packed alignment */ + alignment = CHAR_BIT; + } + } + + *integer_decl = ctf_field_class_int_create(); + BT_ASSERT(*integer_decl); + (*integer_decl)->base.base.alignment = alignment; + (*integer_decl)->base.byte_order = byte_order; + (*integer_decl)->base.size = size; + (*integer_decl)->is_signed = (signedness > 0); + (*integer_decl)->disp_base = base; + (*integer_decl)->encoding = encoding; + (*integer_decl)->mapped_clock_class = mapped_clock_class; + return 0; + +error: + ctf_field_class_destroy((void *) *integer_decl); + *integer_decl = NULL; + return ret; +} + +static +int visit_floating_point_number_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct ctf_field_class_float **float_decl) +{ + int set = 0; + int ret = 0; + struct ctf_node *expression; + uint64_t alignment = 1, exp_dig = 0, mant_dig = 0; + enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order; + + *float_decl = NULL; + + bt_list_for_each_entry(expression, expressions, siblings) { + struct ctf_node *left, *right; + + left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, + struct ctf_node, siblings); + right = _BT_LIST_FIRST_ENTRY( + &expression->u.ctf_expression.right, struct ctf_node, + siblings); + + if (left->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(left, + "Unexpected unary expression type: type=%d", + left->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "byte_order")) { + if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) { + _BT_LOGE_DUP_ATTR(left, "byte_order", + "floating point number field class"); + ret = -EPERM; + goto error; + } + + byte_order = get_real_byte_order(ctx, right); + if (byte_order == -1) { + _BT_LOGE_NODE(right, + "Invalid `byte_order` attribute in floating point number field class: " + "ret=%d", ret); + ret = -EINVAL; + goto error; + } + + _SET(&set, _FLOAT_BYTE_ORDER_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "exp_dig")) { + if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { + _BT_LOGE_DUP_ATTR(left, "exp_dig", + "floating point number field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _BT_LOGE_NODE(right, + "Invalid `exp_dig` attribute in floating point number field class: " + "expecting unsigned constant integer: " + "node-type=%d", + right->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + exp_dig = right->u.unary_expression.u.unsigned_constant; + _SET(&set, _FLOAT_EXP_DIG_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "mant_dig")) { + if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { + _BT_LOGE_DUP_ATTR(left, "mant_dig", + "floating point number field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _BT_LOGE_NODE(right, + "Invalid `mant_dig` attribute in floating point number field class: " + "expecting unsigned constant integer: " + "node-type=%d", + right->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + mant_dig = right->u.unary_expression.u. + unsigned_constant; + _SET(&set, _FLOAT_MANT_DIG_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "align")) { + if (_IS_SET(&set, _FLOAT_ALIGN_SET)) { + _BT_LOGE_DUP_ATTR(left, "align", + "floating point number field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _BT_LOGE_NODE(right, + "Invalid `align` attribute in floating point number field class: " + "expecting unsigned constant integer: " + "node-type=%d", + right->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + alignment = right->u.unary_expression.u. + unsigned_constant; + + if (!is_align_valid(alignment)) { + _BT_LOGE_NODE(right, + "Invalid `align` attribute in floating point number field class: " + "expecting power of two: " + "align=%" PRIu64, alignment); + ret = -EINVAL; + goto error; + } + + _SET(&set, _FLOAT_ALIGN_SET); + } else { + _BT_LOGW_NODE(left, + "Unknown attribute in floating point number field class: " + "attr-name=\"%s\"", + left->u.unary_expression.u.string); + } + } + + if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { + BT_LOGE_STR("Missing `mant_dig` attribute in floating point number field class."); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { + BT_LOGE_STR("Missing `exp_dig` attribute in floating point number field class."); + ret = -EPERM; + goto error; + } + + if (mant_dig != 24 && mant_dig != 53) { + BT_LOGE_STR("`mant_dig` attribute: expecting 24 or 53."); + ret = -EPERM; + goto error; + } + + if (mant_dig == 24 && exp_dig != 8) { + BT_LOGE_STR("`exp_dig` attribute: expecting 8 because `mant_dig` is 24."); + ret = -EPERM; + goto error; + } + + if (mant_dig == 53 && exp_dig != 11) { + BT_LOGE_STR("`exp_dig` attribute: expecting 11 because `mant_dig` is 53."); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) { + if ((mant_dig + exp_dig) % CHAR_BIT) { + /* Bit-packed alignment */ + alignment = 1; + } else { + /* Byte-packed alignment */ + alignment = CHAR_BIT; + } + } + + *float_decl = ctf_field_class_float_create(); + BT_ASSERT(*float_decl); + (*float_decl)->base.base.alignment = alignment; + (*float_decl)->base.byte_order = byte_order; + (*float_decl)->base.size = mant_dig + exp_dig; + return 0; + +error: + ctf_field_class_destroy((void *) *float_decl); + *float_decl = NULL; + return ret; +} + +static +int visit_string_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct ctf_field_class_string **string_decl) +{ + int set = 0; + int ret = 0; + struct ctf_node *expression; + enum ctf_encoding encoding = CTF_ENCODING_UTF8; + + *string_decl = NULL; + + bt_list_for_each_entry(expression, expressions, siblings) { + struct ctf_node *left, *right; + + left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left, + struct ctf_node, siblings); + right = _BT_LIST_FIRST_ENTRY( + &expression->u.ctf_expression.right, struct ctf_node, + siblings); + + if (left->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(left, + "Unexpected unary expression type: type=%d", + left->u.unary_expression.type); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "encoding")) { + char *s_right; + + if (_IS_SET(&set, _STRING_ENCODING_SET)) { + _BT_LOGE_DUP_ATTR(left, "encoding", + "string field class"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_NODE(right, + "Invalid `encoding` attribute in string field class: " + "expecting unary string."); + ret = -EINVAL; + goto error; + } + + s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _BT_LOGE_NODE(right, + "Unexpected unary expression for string field class's `encoding` attribute."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "UTF8") || + !strcmp(s_right, "utf8") || + !strcmp(s_right, "utf-8") || + !strcmp(s_right, "UTF-8") || + !strcmp(s_right, "ASCII") || + !strcmp(s_right, "ascii")) { + encoding = CTF_ENCODING_UTF8; + } else if (!strcmp(s_right, "none")) { + encoding = CTF_ENCODING_NONE; + } else { + _BT_LOGE_NODE(right, + "Invalid `encoding` attribute in string field class: " + "unknown encoding: encoding=\"%s\"", + s_right); + g_free(s_right); + ret = -EINVAL; + goto error; + } + + g_free(s_right); + _SET(&set, _STRING_ENCODING_SET); + } else { + _BT_LOGW_NODE(left, + "Unknown attribute in string field class: " + "attr-name=\"%s\"", + left->u.unary_expression.u.string); + } + } + + *string_decl = ctf_field_class_string_create(); + BT_ASSERT(*string_decl); + (*string_decl)->encoding = encoding; + return 0; + +error: + ctf_field_class_destroy((void *) *string_decl); + *string_decl = NULL; + return ret; +} + +static +int visit_field_class_specifier_list(struct ctx *ctx, + struct ctf_node *ts_list, struct ctf_field_class **decl) +{ + int ret = 0; + struct ctf_node *first, *node; + + *decl = NULL; + + if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) { + _BT_LOGE_NODE(ts_list, + "Unexpected node type: node-type=%d", ts_list->type); + ret = -EINVAL; + goto error; + } + + first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head, + struct ctf_node, siblings); + if (first->type != NODE_TYPE_SPECIFIER) { + _BT_LOGE_NODE(first, + "Unexpected node type: node-type=%d", first->type); + ret = -EINVAL; + goto error; + } + + node = first->u.field_class_specifier.node; + + switch (first->u.field_class_specifier.type) { + case TYPESPEC_INTEGER: + ret = visit_integer_decl(ctx, &node->u.integer.expressions, + (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_FLOATING_POINT: + ret = visit_floating_point_number_decl(ctx, + &node->u.floating_point.expressions, (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_STRING: + ret = visit_string_decl(ctx, + &node->u.string.expressions, (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_STRUCT: + ret = visit_struct_decl(ctx, node->u._struct.name, + &node->u._struct.declaration_list, + node->u._struct.has_body, + &node->u._struct.min_align, (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_VARIANT: + ret = visit_variant_decl(ctx, node->u.variant.name, + node->u.variant.choice, + &node->u.variant.declaration_list, + node->u.variant.has_body, (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_ENUM: + ret = visit_enum_decl(ctx, node->u._enum.enum_id, + node->u._enum.container_field_class, + &node->u._enum.enumerator_list, + node->u._enum.has_body, (void *) decl); + if (ret) { + BT_ASSERT(!*decl); + goto error; + } + break; + case TYPESPEC_VOID: + case TYPESPEC_CHAR: + case TYPESPEC_SHORT: + case TYPESPEC_INT: + case TYPESPEC_LONG: + case TYPESPEC_FLOAT: + case TYPESPEC_DOUBLE: + case TYPESPEC_SIGNED: + case TYPESPEC_UNSIGNED: + case TYPESPEC_BOOL: + case TYPESPEC_COMPLEX: + case TYPESPEC_IMAGINARY: + case TYPESPEC_CONST: + case TYPESPEC_ID_TYPE: + ret = visit_field_class_specifier(ctx, ts_list, decl); + if (ret) { + _BT_LOGE_NODE(first, + "Cannot visit field class specifier: ret=%d", + ret); + BT_ASSERT(!*decl); + goto error; + } + break; + default: + _BT_LOGE_NODE(first, + "Unexpected field class specifier type: node-type=%d", + first->u.field_class_specifier.type); + ret = -EINVAL; + goto error; + } + + BT_ASSERT(*decl); + return 0; + +error: + ctf_field_class_destroy((void *) *decl); + *decl = NULL; + return ret; +} + +static +int visit_event_decl_entry(struct ctx *ctx, struct ctf_node *node, + struct ctf_event_class *event_class, uint64_t *stream_id, + int *set) +{ + int ret = 0; + char *left = NULL; + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, + &node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class found in event class."); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, + node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class alias found in event class."); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "name")) { + /* This is already known at this stage */ + if (_IS_SET(set, _EVENT_NAME_SET)) { + _BT_LOGE_DUP_ATTR(node, "name", "event class"); + ret = -EPERM; + goto error; + } + + _SET(set, _EVENT_NAME_SET); + } else if (!strcmp(left, "id")) { + int64_t id = -1; + + if (_IS_SET(set, _EVENT_ID_SET)) { + _BT_LOGE_DUP_ATTR(node, "id", "event class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + (uint64_t *) &id); + /* Only read "id" if get_unary_unsigned() succeeded. */ + if (ret || (!ret && id < 0)) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for event class's `id` attribute."); + ret = -EINVAL; + goto error; + } + + event_class->id = id; + _SET(set, _EVENT_ID_SET); + } else if (!strcmp(left, "stream_id")) { + if (_IS_SET(set, _EVENT_STREAM_ID_SET)) { + _BT_LOGE_DUP_ATTR(node, "stream_id", + "event class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + stream_id); + + /* + * Only read "stream_id" if get_unary_unsigned() + * succeeded. + */ + if (ret) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for event class's `stream_id` attribute."); + ret = -EINVAL; + goto error; + } + + _SET(set, _EVENT_STREAM_ID_SET); + } else if (!strcmp(left, "context")) { + if (_IS_SET(set, _EVENT_CONTEXT_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `context` entry in event class."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &event_class->spec_context_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create event class's context field class."); + goto error; + } + + BT_ASSERT(event_class->spec_context_fc); + _SET(set, _EVENT_CONTEXT_SET); + } else if (!strcmp(left, "fields")) { + if (_IS_SET(set, _EVENT_FIELDS_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `fields` entry in event class."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &event_class->payload_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create event class's payload field class."); + goto error; + } + + BT_ASSERT(event_class->payload_fc); + _SET(set, _EVENT_FIELDS_SET); + } else if (!strcmp(left, "loglevel")) { + uint64_t loglevel_value; + bt_event_class_log_level log_level = -1; + + if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) { + _BT_LOGE_DUP_ATTR(node, "loglevel", + "event class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &loglevel_value); + if (ret) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for event class's `loglevel` attribute."); + ret = -EINVAL; + goto error; + } + + switch (loglevel_value) { + case 0: + log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY; + break; + case 1: + log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT; + break; + case 2: + log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL; + break; + case 3: + log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR; + break; + case 4: + log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING; + break; + case 5: + log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE; + break; + case 6: + log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO; + break; + case 7: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM; + break; + case 8: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM; + break; + case 9: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS; + break; + case 10: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE; + break; + case 11: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT; + break; + case 12: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION; + break; + case 13: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE; + break; + case 14: + log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG; + break; + default: + _BT_LOGW_NODE(node, "Not setting event class's log level because its value is unknown: " + "log-level=%" PRIu64, loglevel_value); + } + + if (log_level != -1) { + event_class->log_level = log_level; + } + + _SET(set, _EVENT_LOG_LEVEL_SET); + } else if (!strcmp(left, "model.emf.uri")) { + char *right; + + if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) { + _BT_LOGE_DUP_ATTR(node, "model.emf.uri", + "event class"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &node->u.ctf_expression.right); + if (!right) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for event class's `model.emf.uri` attribute."); + ret = -EINVAL; + goto error; + } + + if (strlen(right) == 0) { + _BT_LOGW_NODE(node, + "Not setting event class's EMF URI because it's empty."); + } else { + g_string_assign(event_class->emf_uri, + right); + } + + g_free(right); + _SET(set, _EVENT_MODEL_EMF_URI_SET); + } else { + _BT_LOGW_NODE(node, + "Unknown attribute in event class: " + "attr-name=\"%s\"", left); + } + + g_free(left); + left = NULL; + break; + } + default: + ret = -EPERM; + goto error; + } + + goto end; + +error: + if (left) { + g_free(left); + } + +end: + return ret; +} + +static +char *get_event_decl_name(struct ctx *ctx, struct ctf_node *node) +{ + char *left = NULL; + char *name = NULL; + struct ctf_node *iter; + struct bt_list_head *decl_list = &node->u.event.declaration_list; + + bt_list_for_each_entry(iter, decl_list, siblings) { + if (iter->type != NODE_CTF_EXPRESSION) { + continue; + } + + left = concatenate_unary_strings(&iter->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(iter, + "Cannot concatenate unary strings."); + goto error; + } + + if (!strcmp(left, "name")) { + name = concatenate_unary_strings( + &iter->u.ctf_expression.right); + if (!name) { + _BT_LOGE_NODE(iter, + "Unexpected unary expression for event class's `name` attribute."); + goto error; + } + } + + g_free(left); + left = NULL; + + if (name) { + break; + } + } + + return name; + +error: + g_free(left); + return NULL; +} + +static +int visit_event_decl(struct ctx *ctx, struct ctf_node *node) +{ + int ret = 0; + int set = 0; + struct ctf_node *iter; + uint64_t stream_id = 0; + char *event_name = NULL; + struct ctf_event_class *event_class = NULL; + struct ctf_stream_class *stream_class = NULL; + struct bt_list_head *decl_list = &node->u.event.declaration_list; + bool pop_scope = false; + + if (node->visited) { + goto end; + } + + node->visited = TRUE; + event_name = get_event_decl_name(ctx, node); + if (!event_name) { + _BT_LOGE_NODE(node, + "Missing `name` attribute in event class."); + ret = -EPERM; + goto error; + } + + event_class = ctf_event_class_create(); + BT_ASSERT(event_class); + g_string_assign(event_class->name, event_name); + _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); + pop_scope = true; + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_event_decl_entry(ctx, iter, event_class, + &stream_id, &set); + if (ret) { + _BT_LOGE_NODE(iter, "Cannot visit event class's entry: " + "ret=%d", ret); + goto error; + } + } + + if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) { + /* + * Allow missing stream_id if there is only a single + * stream class. + */ + switch (ctx->ctf_tc->stream_classes->len) { + case 0: + /* Create implicit stream class if there's none */ + stream_id = 0; + stream_class = ctf_stream_class_create(); + BT_ASSERT(stream_class); + stream_class->id = stream_id; + g_ptr_array_add(ctx->ctf_tc->stream_classes, + stream_class); + break; + case 1: + /* Single stream class: get its ID */ + stream_class = ctx->ctf_tc->stream_classes->pdata[0]; + stream_id = stream_class->id; + break; + default: + _BT_LOGE_NODE(node, + "Missing `stream_id` attribute in event class."); + ret = -EPERM; + goto error; + } + } + + /* We have the stream ID now; get the stream class if found */ + if (!stream_class) { + stream_class = ctf_trace_class_borrow_stream_class_by_id( + ctx->ctf_tc, stream_id); + if (!stream_class) { + _BT_LOGE_NODE(node, + "Cannot find stream class at this point: " + "id=%" PRId64, stream_id); + ret = -EINVAL; + goto error; + } + } + + BT_ASSERT(stream_class); + + if (!_IS_SET(&set, _EVENT_ID_SET)) { + /* Allow only one event without ID per stream */ + if (stream_class->event_classes->len != 0) { + _BT_LOGE_NODE(node, + "Missing `id` attribute in event class."); + ret = -EPERM; + goto error; + } + + /* Automatic ID */ + event_class->id = 0; + } + + if (ctf_stream_class_borrow_event_class_by_id(stream_class, + event_class->id)) { + _BT_LOGE_NODE(node, + "Duplicate event class (same ID) in the same stream class: " + "id=%" PRId64, event_class->id); + ret = -EEXIST; + goto error; + } + + ctf_stream_class_append_event_class(stream_class, event_class); + event_class = NULL; + goto end; + +error: + ctf_event_class_destroy(event_class); + event_class = NULL; + + if (ret >= 0) { + ret = -1; + } + +end: + if (pop_scope) { + ctx_pop_scope(ctx); + } + + if (event_name) { + g_free(event_name); + } + + return ret; +} + +static +int auto_map_field_to_trace_clock_class(struct ctx *ctx, + struct ctf_field_class *fc) +{ + struct ctf_clock_class *clock_class_to_map_to = NULL; + struct ctf_field_class_int *int_fc = (void *) fc; + int ret = 0; + uint64_t clock_class_count; + + if (!fc) { + goto end; + } + + if (fc->type != CTF_FIELD_CLASS_TYPE_INT && + fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + goto end; + } + + if (int_fc->mapped_clock_class) { + /* Already mapped */ + goto end; + } + + clock_class_count = ctx->ctf_tc->clock_classes->len; + + switch (clock_class_count) { + case 0: + /* + * No clock class exists in the trace at this point. Create an + * implicit one at 1 GHz, named `default`, and use this clock + * class. + */ + clock_class_to_map_to = ctf_clock_class_create(); + BT_ASSERT(clock_class_to_map_to); + clock_class_to_map_to->frequency = UINT64_C(1000000000); + g_string_assign(clock_class_to_map_to->name, "default"); + BT_ASSERT(ret == 0); + g_ptr_array_add(ctx->ctf_tc->clock_classes, + clock_class_to_map_to); + break; + case 1: + /* + * Only one clock class exists in the trace at this point: use + * this one. + */ + clock_class_to_map_to = ctx->ctf_tc->clock_classes->pdata[0]; + break; + default: + /* + * Timestamp field not mapped to a clock class and there's more + * than one clock class in the trace: this is an error. + */ + BT_LOGE_STR("Timestamp field found with no mapped clock class, " + "but there's more than one clock class in the trace at this point."); + ret = -1; + goto end; + } + + BT_ASSERT(clock_class_to_map_to); + int_fc->mapped_clock_class = clock_class_to_map_to; + +end: + return ret; +} + +static +int auto_map_fields_to_trace_clock_class(struct ctx *ctx, + struct ctf_field_class *root_fc, const char *field_name) +{ + int ret = 0; + uint64_t i, count; + struct ctf_field_class_struct *struct_fc = (void *) root_fc; + struct ctf_field_class_variant *var_fc = (void *) root_fc; + + if (!root_fc) { + goto end; + } + + if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT && + root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) { + goto end; + } + + if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) { + count = struct_fc->members->len; + } else { + count = var_fc->options->len; + } + + for (i = 0; i < count; i++) { + struct ctf_named_field_class *named_fc = NULL; + + if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) { + named_fc = ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) { + named_fc = ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + } + + if (strcmp(named_fc->name->str, field_name) == 0) { + ret = auto_map_field_to_trace_clock_class(ctx, + named_fc->fc); + if (ret) { + BT_LOGE("Cannot automatically map field to trace's clock class: " + "field-name=\"%s\"", field_name); + goto end; + } + } + + ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc, + field_name); + if (ret) { + BT_LOGE("Cannot automatically map structure or variant field class's fields to trace's clock class: " + "field-name=\"%s\", root-field-name=\"%s\"", + field_name, named_fc->name->str); + goto end; + } + } + +end: + return ret; +} + +static +int visit_stream_decl_entry(struct ctx *ctx, struct ctf_node *node, + struct ctf_stream_class *stream_class, int *set) +{ + int ret = 0; + char *left = NULL; + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, + &node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class found in stream class."); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, + node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class alias found in stream class."); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "id")) { + int64_t id; + + if (_IS_SET(set, _STREAM_ID_SET)) { + _BT_LOGE_DUP_ATTR(node, "id", + "stream declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + (uint64_t *) &id); + + /* Only read "id" if get_unary_unsigned() succeeded. */ + if (ret || (!ret && id < 0)) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for stream class's `id` attribute."); + ret = -EINVAL; + goto error; + } + + if (ctf_trace_class_borrow_stream_class_by_id( + ctx->ctf_tc, id)) { + _BT_LOGE_NODE(node, + "Duplicate stream class (same ID): id=%" PRId64, + id); + ret = -EEXIST; + goto error; + } + + stream_class->id = id; + _SET(set, _STREAM_ID_SET); + } else if (!strcmp(left, "event.header")) { + if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `event.header` entry in stream class."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &stream_class->event_header_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create stream class's event header field class."); + goto error; + } + + BT_ASSERT(stream_class->event_header_fc); + ret = auto_map_fields_to_trace_clock_class(ctx, + stream_class->event_header_fc, "timestamp"); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class."); + goto error; + } + + _SET(set, _STREAM_EVENT_HEADER_SET); + } else if (!strcmp(left, "event.context")) { + if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `event.context` entry in stream class."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &stream_class->event_common_context_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create stream class's event context field class."); + goto error; + } + + BT_ASSERT(stream_class->event_common_context_fc); + _SET(set, _STREAM_EVENT_CONTEXT_SET); + } else if (!strcmp(left, "packet.context")) { + if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `packet.context` entry in stream class."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &stream_class->packet_context_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create stream class's packet context field class."); + goto error; + } + + BT_ASSERT(stream_class->packet_context_fc); + ret = auto_map_fields_to_trace_clock_class(ctx, + stream_class->packet_context_fc, + "timestamp_begin"); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class."); + goto error; + } + + ret = auto_map_fields_to_trace_clock_class(ctx, + stream_class->packet_context_fc, + "timestamp_end"); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class."); + goto error; + } + + _SET(set, _STREAM_PACKET_CONTEXT_SET); + } else { + _BT_LOGW_NODE(node, + "Unknown attribute in stream class: " + "attr-name=\"%s\"", left); + } + + g_free(left); + left = NULL; + break; + } + + default: + ret = -EPERM; + goto error; + } + + return 0; + +error: + g_free(left); + return ret; +} + +static +int visit_stream_decl(struct ctx *ctx, struct ctf_node *node) +{ + int set = 0; + int ret = 0; + struct ctf_node *iter; + struct ctf_stream_class *stream_class = NULL; + struct bt_list_head *decl_list = &node->u.stream.declaration_list; + + if (node->visited) { + goto end; + } + + node->visited = TRUE; + stream_class = ctf_stream_class_create(); + BT_ASSERT(stream_class); + _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_stream_decl_entry(ctx, iter, stream_class, &set); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit stream class's entry: " + "ret=%d", ret); + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (_IS_SET(&set, _STREAM_ID_SET)) { + /* Check that packet header has `stream_id` field */ + struct ctf_named_field_class *named_fc = NULL; + + if (!ctx->ctf_tc->packet_header_fc) { + _BT_LOGE_NODE(node, + "Stream class has a `id` attribute, " + "but trace has no packet header field class."); + goto error; + } + + named_fc = ctf_field_class_struct_borrow_member_by_name( + (void *) ctx->ctf_tc->packet_header_fc, "stream_id"); + if (!named_fc) { + _BT_LOGE_NODE(node, + "Stream class has a `id` attribute, " + "but trace's packet header field class has no `stream_id` field."); + goto error; + } + + if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT && + named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) { + _BT_LOGE_NODE(node, + "Stream class has a `id` attribute, " + "but trace's packet header field class's `stream_id` field is not an integer field class."); + goto error; + } + } else { + /* Allow only _one_ ID-less stream */ + if (ctx->ctf_tc->stream_classes->len != 0) { + _BT_LOGE_NODE(node, + "Missing `id` attribute in stream class as there's more than one stream class in the trace."); + ret = -EPERM; + goto error; + } + + /* Automatic ID: 0 */ + stream_class->id = 0; + } + + /* + * Make sure that this stream class's ID is currently unique in + * the trace. + */ + if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc, + stream_class->id)) { + _BT_LOGE_NODE(node, + "Duplicate stream class (same ID): id=%" PRId64, + stream_class->id); + ret = -EINVAL; + goto error; + } + + g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class); + stream_class = NULL; + goto end; + +error: + ctf_stream_class_destroy(stream_class); + stream_class = NULL; + +end: + return ret; +} + +static +int visit_trace_decl_entry(struct ctx *ctx, struct ctf_node *node, int *set) +{ + int ret = 0; + char *left = NULL; + uint64_t val; + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list, + &node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class found in trace (`trace` block)."); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, node->u.field_class_alias.target, + node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot add field class alias found in trace (`trace` block)."); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(node, "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "major")) { + if (_IS_SET(set, _TRACE_MAJOR_SET)) { + _BT_LOGE_DUP_ATTR(node, "major", "trace"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &val); + if (ret) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for trace's `major` attribute."); + ret = -EINVAL; + goto error; + } + + if (val != 1) { + _BT_LOGE_NODE(node, + "Invalid trace's `minor` attribute: expecting 1."); + goto error; + } + + ctx->ctf_tc->major = val; + _SET(set, _TRACE_MAJOR_SET); + } else if (!strcmp(left, "minor")) { + if (_IS_SET(set, _TRACE_MINOR_SET)) { + _BT_LOGE_DUP_ATTR(node, "minor", "trace"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &val); + if (ret) { + _BT_LOGE_NODE(node, + "Unexpected unary expression for trace's `minor` attribute."); + ret = -EINVAL; + goto error; + } + + if (val != 8) { + _BT_LOGE_NODE(node, + "Invalid trace's `minor` attribute: expecting 8."); + goto error; + } + + ctx->ctf_tc->minor = val; + _SET(set, _TRACE_MINOR_SET); + } else if (!strcmp(left, "uuid")) { + if (_IS_SET(set, _TRACE_UUID_SET)) { + _BT_LOGE_DUP_ATTR(node, "uuid", "trace"); + ret = -EPERM; + goto error; + } + + ret = get_unary_uuid(&node->u.ctf_expression.right, + ctx->ctf_tc->uuid); + if (ret) { + _BT_LOGE_NODE(node, + "Invalid trace's `uuid` attribute."); + goto error; + } + + ctx->ctf_tc->is_uuid_set = true; + _SET(set, _TRACE_UUID_SET); + } else if (!strcmp(left, "byte_order")) { + /* Default byte order is already known at this stage */ + if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) { + _BT_LOGE_DUP_ATTR(node, "byte_order", + "trace"); + ret = -EPERM; + goto error; + } + + BT_ASSERT(ctx->ctf_tc->default_byte_order != -1); + _SET(set, _TRACE_BYTE_ORDER_SET); + } else if (!strcmp(left, "packet.header")) { + if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) { + _BT_LOGE_NODE(node, + "Duplicate `packet.header` entry in trace."); + ret = -EPERM; + goto error; + } + + ret = visit_field_class_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &ctx->ctf_tc->packet_header_fc); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot create trace's packet header field class."); + goto error; + } + + BT_ASSERT(ctx->ctf_tc->packet_header_fc); + _SET(set, _TRACE_PACKET_HEADER_SET); + } else { + _BT_LOGW_NODE(node, + "Unknown attribute in stream class: " + "attr-name=\"%s\"", left); + } + + g_free(left); + left = NULL; + break; + } + default: + _BT_LOGE_NODE(node, "Unknown expression in trace."); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + g_free(left); + return ret; +} + +static +int visit_trace_decl(struct ctx *ctx, struct ctf_node *node) +{ + int ret = 0; + int set = 0; + struct ctf_node *iter; + struct bt_list_head *decl_list = &node->u.trace.declaration_list; + + if (node->visited) { + goto end; + } + + node->visited = TRUE; + + if (ctx->is_trace_visited) { + _BT_LOGE_NODE(node, "Duplicate trace (`trace` block)."); + ret = -EEXIST; + goto error; + } + + _TRY_PUSH_SCOPE_OR_GOTO_ERROR(); + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_trace_decl_entry(ctx, iter, &set); + if (ret) { + _BT_LOGE_NODE(iter, "Cannot visit trace's entry (`trace` block): " + "ret=%d", ret); + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (!_IS_SET(&set, _TRACE_MAJOR_SET)) { + _BT_LOGE_NODE(node, + "Missing `major` attribute in trace (`trace` block)."); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _TRACE_MINOR_SET)) { + _BT_LOGE_NODE(node, + "Missing `minor` attribute in trace (`trace` block)."); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _BT_LOGE_NODE(node, + "Missing `byte_order` attribute in trace (`trace` block)."); + ret = -EPERM; + goto error; + } + + ctx->is_trace_visited = true; + +end: + return 0; + +error: + return ret; +} + +static +int visit_env(struct ctx *ctx, struct ctf_node *node) +{ + int ret = 0; + char *left = NULL; + struct ctf_node *entry_node; + struct bt_list_head *decl_list = &node->u.env.declaration_list; + + if (node->visited) { + goto end; + } + + node->visited = TRUE; + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + struct bt_list_head *right_head = + &entry_node->u.ctf_expression.right; + + if (entry_node->type != NODE_CTF_EXPRESSION) { + _BT_LOGE_NODE(entry_node, + "Wrong expression in environment entry: " + "node-type=%d", entry_node->type); + ret = -EPERM; + goto error; + } + + left = concatenate_unary_strings( + &entry_node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(entry_node, + "Cannot get environment entry's name."); + ret = -EINVAL; + goto error; + } + + if (is_unary_string(right_head)) { + char *right = concatenate_unary_strings(right_head); + + if (!right) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for environment entry's value: " + "name=\"%s\"", left); + ret = -EINVAL; + goto error; + } + + if (strcmp(left, "tracer_name") == 0) { + if (strncmp(right, "lttng", 5) == 0) { + BT_LOGI("Detected LTTng trace from `%s` environment value: " + "tracer-name=\"%s\"", + left, right); + ctx->is_lttng = true; + } + } + + ctf_trace_class_append_env_entry(ctx->ctf_tc, + left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR, + right, 0); + g_free(right); + } else if (is_unary_unsigned(right_head) || + is_unary_signed(right_head)) { + int64_t v; + + if (is_unary_unsigned(right_head)) { + ret = get_unary_unsigned(right_head, + (uint64_t *) &v); + } else { + ret = get_unary_signed(right_head, &v); + } + if (ret) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for environment entry's value: " + "name=\"%s\"", left); + ret = -EINVAL; + goto error; + } + + ctf_trace_class_append_env_entry(ctx->ctf_tc, + left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT, + NULL, v); + } else { + _BT_LOGW_NODE(entry_node, + "Environment entry has unknown type: " + "name=\"%s\"", left); + } + + g_free(left); + left = NULL; + } + +end: + return 0; + +error: + g_free(left); + return ret; +} + +static +int set_trace_byte_order(struct ctx *ctx, struct ctf_node *trace_node) +{ + int ret = 0; + int set = 0; + char *left = NULL; + struct ctf_node *node; + struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list; + + bt_list_for_each_entry(node, decl_list, siblings) { + if (node->type == NODE_CTF_EXPRESSION) { + struct ctf_node *right_node; + + left = concatenate_unary_strings( + &node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(node, + "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "byte_order")) { + enum ctf_byte_order bo; + + if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _BT_LOGE_DUP_ATTR(node, "byte_order", + "trace"); + ret = -EPERM; + goto error; + } + + _SET(&set, _TRACE_BYTE_ORDER_SET); + right_node = _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings); + bo = byte_order_from_unary_expr(right_node); + if (bo == -1) { + _BT_LOGE_NODE(node, + "Invalid `byte_order` attribute in trace (`trace` block): " + "expecting `le`, `be`, or `network`."); + ret = -EINVAL; + goto error; + } else if (bo == CTF_BYTE_ORDER_DEFAULT) { + _BT_LOGE_NODE(node, + "Invalid `byte_order` attribute in trace (`trace` block): " + "cannot be set to `native` here."); + ret = -EPERM; + goto error; + } + + ctx->ctf_tc->default_byte_order = bo; + } + + g_free(left); + left = NULL; + } + } + + if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _BT_LOGE_NODE(trace_node, + "Missing `byte_order` attribute in trace (`trace` block)."); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + g_free(left); + return ret; +} + +static +int visit_clock_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct ctf_clock_class *clock, int *set, int64_t *offset_seconds, + uint64_t *offset_cycles) +{ + int ret = 0; + char *left = NULL; + + if (entry_node->type != NODE_CTF_EXPRESSION) { + _BT_LOGE_NODE(entry_node, + "Unexpected node type: node-type=%d", + entry_node->type); + ret = -EPERM; + goto error; + } + + left = concatenate_unary_strings(&entry_node->u.ctf_expression.left); + if (!left) { + _BT_LOGE_NODE(entry_node, "Cannot concatenate unary strings."); + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "name")) { + char *right; + + if (_IS_SET(set, _CLOCK_NAME_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "name", "clock class"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &entry_node->u.ctf_expression.right); + if (!right) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `name` attribute."); + ret = -EINVAL; + goto error; + } + + g_string_assign(clock->name, right); + g_free(right); + _SET(set, _CLOCK_NAME_SET); + } else if (!strcmp(left, "uuid")) { + uint8_t uuid[BABELTRACE_UUID_LEN]; + + if (_IS_SET(set, _CLOCK_UUID_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "uuid", "clock class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_uuid(&entry_node->u.ctf_expression.right, uuid); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Invalid clock class's `uuid` attribute."); + goto error; + } + + clock->has_uuid = true; + memcpy(&clock->uuid[0], uuid, 16); + _SET(set, _CLOCK_UUID_SET); + } else if (!strcmp(left, "description")) { + char *right; + + if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "description", + "clock class"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &entry_node->u.ctf_expression.right); + if (!right) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `description` attribute."); + ret = -EINVAL; + goto error; + } + + g_string_assign(clock->description, right); + g_free(right); + _SET(set, _CLOCK_DESCRIPTION_SET); + } else if (!strcmp(left, "freq")) { + uint64_t freq = UINT64_C(-1); + + if (_IS_SET(set, _CLOCK_FREQ_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "freq", "clock class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &freq); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `freq` attribute."); + ret = -EINVAL; + goto error; + } + + if (freq == UINT64_C(-1) || freq == 0) { + _BT_LOGE_NODE(entry_node, + "Invalid clock class frequency: freq=%" PRIu64, + freq); + ret = -EINVAL; + goto error; + } + + clock->frequency = freq; + _SET(set, _CLOCK_FREQ_SET); + } else if (!strcmp(left, "precision")) { + uint64_t precision; + + if (_IS_SET(set, _CLOCK_PRECISION_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "precision", + "clock class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &precision); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `precision` attribute."); + ret = -EINVAL; + goto error; + } + + clock->precision = precision; + _SET(set, _CLOCK_PRECISION_SET); + } else if (!strcmp(left, "offset_s")) { + if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "offset_s", + "clock class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_signed( + &entry_node->u.ctf_expression.right, offset_seconds); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `offset_s` attribute."); + ret = -EINVAL; + goto error; + } + + _SET(set, _CLOCK_OFFSET_S_SET); + } else if (!strcmp(left, "offset")) { + if (_IS_SET(set, _CLOCK_OFFSET_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "offset", "clock class"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, offset_cycles); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `offset` attribute."); + ret = -EINVAL; + goto error; + } + + _SET(set, _CLOCK_OFFSET_SET); + } else if (!strcmp(left, "absolute")) { + struct ctf_node *right; + + if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) { + _BT_LOGE_DUP_ATTR(entry_node, "absolute", + "clock class"); + ret = -EPERM; + goto error; + } + + right = _BT_LIST_FIRST_ENTRY( + &entry_node->u.ctf_expression.right, + struct ctf_node, siblings); + ret = get_boolean(right); + if (ret < 0) { + _BT_LOGE_NODE(entry_node, + "Unexpected unary expression for clock class's `absolute` attribute."); + ret = -EINVAL; + goto error; + } + + clock->is_absolute = ret; + _SET(set, _CLOCK_ABSOLUTE_SET); + } else { + _BT_LOGW_NODE(entry_node, + "Unknown attribute in clock class: attr-name=\"%s\"", + left); + } + + g_free(left); + left = NULL; + return 0; + +error: + g_free(left); + return ret; +} + +static inline +uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns) +{ + uint64_t cycles; + + /* 1GHz */ + if (frequency == UINT64_C(1000000000)) { + cycles = ns; + } else { + cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9); + } + + return cycles; +} + +static +void calibrate_clock_class_offsets(int64_t *offset_seconds, + uint64_t *offset_cycles, uint64_t freq) +{ + if (*offset_cycles >= freq) { + const uint64_t s_in_offset_cycles = *offset_cycles / freq; + + *offset_seconds += (int64_t) s_in_offset_cycles; + *offset_cycles -= (s_in_offset_cycles * freq); + } +} + +static +void apply_clock_class_offset(struct ctx *ctx, + struct ctf_clock_class *clock) +{ + uint64_t freq; + int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s; + uint64_t offset_ns_to_apply; + int64_t cur_offset_s; + uint64_t cur_offset_cycles; + + if (ctx->decoder_config.clock_class_offset_s == 0 && + ctx->decoder_config.clock_class_offset_ns == 0) { + goto end; + } + + /* Transfer nanoseconds to seconds as much as possible */ + if (ctx->decoder_config.clock_class_offset_ns < 0) { + const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns; + const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1; + const int64_t extra_s = -abs_extra_s; + const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns - + (extra_s * INT64_C(1000000000)); + + BT_ASSERT(offset_ns > 0); + offset_ns_to_apply = (uint64_t) offset_ns; + offset_s_to_apply += extra_s; + } else { + const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns / + INT64_C(1000000000); + const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns - + (extra_s * INT64_C(1000000000)); + + BT_ASSERT(offset_ns >= 0); + offset_ns_to_apply = (uint64_t) offset_ns; + offset_s_to_apply += extra_s; + } + + freq = clock->frequency; + cur_offset_s = clock->offset_seconds; + cur_offset_cycles = clock->offset_cycles; + + /* Apply offsets */ + cur_offset_s += offset_s_to_apply; + cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply); + + /* + * Recalibrate offsets because the part in cycles can be greater + * than the frequency at this point. + */ + calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq); + + /* Set final offsets */ + clock->offset_seconds = cur_offset_s; + clock->offset_cycles = cur_offset_cycles; + +end: + return; +} + +static +int visit_clock_decl(struct ctx *ctx, struct ctf_node *clock_node) +{ + int ret = 0; + int set = 0; + struct ctf_clock_class *clock; + struct ctf_node *entry_node; + struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list; + const char *clock_class_name; + int64_t offset_seconds = 0; + uint64_t offset_cycles = 0; + uint64_t freq; + + if (clock_node->visited) { + return 0; + } + + clock_node->visited = TRUE; + + /* CTF 1.8's default frequency for a clock class is 1 GHz */ + clock = ctf_clock_class_create(); + if (!clock) { + _BT_LOGE_NODE(clock_node, + "Cannot create default clock class."); + ret = -ENOMEM; + goto end; + } + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + ret = visit_clock_decl_entry(ctx, entry_node, clock, &set, + &offset_seconds, &offset_cycles); + if (ret) { + _BT_LOGE_NODE(entry_node, + "Cannot visit clock class's entry: ret=%d", + ret); + goto end; + } + } + + if (!_IS_SET(&set, _CLOCK_NAME_SET)) { + _BT_LOGE_NODE(clock_node, + "Missing `name` attribute in clock class."); + ret = -EPERM; + goto end; + } + + clock_class_name = clock->name->str; + BT_ASSERT(clock_class_name); + if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) { + /* + * Old versions of LTTng forgot to set its clock class + * as absolute, even if it is. This is important because + * it's a condition to be able to sort messages + * from different sources. + */ + clock->is_absolute = true; + } + + /* + * Adjust offsets so that the part in cycles is less than the + * frequency (move to the part in seconds). + */ + freq = clock->frequency; + calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq); + BT_ASSERT(offset_cycles < clock->frequency); + clock->offset_seconds = offset_seconds; + clock->offset_cycles = offset_cycles; + apply_clock_class_offset(ctx, clock); + g_ptr_array_add(ctx->ctf_tc->clock_classes, clock); + clock = NULL; + +end: + if (clock) { + ctf_clock_class_destroy(clock); + } + + return ret; +} + +static +int visit_root_decl(struct ctx *ctx, struct ctf_node *root_decl_node) +{ + int ret = 0; + + if (root_decl_node->visited) { + goto end; + } + + root_decl_node->visited = TRUE; + + switch (root_decl_node->type) { + case NODE_TYPEDEF: + ret = visit_field_class_def(ctx, + root_decl_node->u.field_class_def.field_class_specifier_list, + &root_decl_node->u.field_class_def.field_class_declarators); + if (ret) { + _BT_LOGE_NODE(root_decl_node, + "Cannot add field class found in root scope."); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target, + root_decl_node->u.field_class_alias.alias); + if (ret) { + _BT_LOGE_NODE(root_decl_node, + "Cannot add field class alias found in root scope."); + goto end; + } + break; + case NODE_TYPE_SPECIFIER_LIST: + { + struct ctf_field_class *decl = NULL; + + /* + * Just add the field class specifier to the root + * declaration scope. Put local reference. + */ + ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl); + if (ret) { + _BT_LOGE_NODE(root_decl_node, + "Cannot visit root scope's field class: " + "ret=%d", ret); + BT_ASSERT(!decl); + goto end; + } + + ctf_field_class_destroy(decl); + decl = NULL; + break; + } + default: + _BT_LOGE_NODE(root_decl_node, + "Unexpected node type: node-type=%d", + root_decl_node->type); + ret = -EPERM; + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create( + bt_self_component_source *self_comp, + const struct ctf_metadata_decoder_config *decoder_config) +{ + struct ctx *ctx = NULL; + + /* Create visitor's context */ + ctx = ctx_create(self_comp, decoder_config); + if (!ctx) { + BT_LOGE_STR("Cannot create visitor's context."); + goto error; + } + + goto end; + +error: + ctx_destroy(ctx); + ctx = NULL; + +end: + return (void *) ctx; +} + +BT_HIDDEN +void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor) +{ + ctx_destroy((void *) visitor); +} + +BT_HIDDEN +bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class( + struct ctf_visitor_generate_ir *visitor) +{ + struct ctx *ctx = (void *) visitor; + + BT_ASSERT(ctx); + + if (ctx->trace_class) { + bt_trace_class_get_ref(ctx->trace_class); + } + + return ctx->trace_class; +} + +BT_HIDDEN +struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class( + struct ctf_visitor_generate_ir *visitor) +{ + struct ctx *ctx = (void *) visitor; + + BT_ASSERT(ctx); + BT_ASSERT(ctx->ctf_tc); + return ctx->ctf_tc; +} + +BT_HIDDEN +int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor, + struct ctf_node *node) +{ + int ret = 0; + struct ctx *ctx = (void *) visitor; + + BT_LOGI_STR("Visiting metadata's AST to generate CTF IR objects."); + + switch (node->type) { + case NODE_ROOT: + { + struct ctf_node *iter; + bool got_trace_decl = false; + + /* + * The first thing we need is the native byte order of + * the trace block, because early class aliases can have + * a `byte_order` attribute set to `native`. If we don't + * have the native byte order yet, and we don't have any + * trace block yet, then fail with EINCOMPLETE. + */ + if (ctx->ctf_tc->default_byte_order == -1) { + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + if (got_trace_decl) { + _BT_LOGE_NODE(node, + "Duplicate trace (`trace` block)."); + ret = -1; + goto end; + } + + ret = set_trace_byte_order(ctx, iter); + if (ret) { + _BT_LOGE_NODE(node, + "Cannot set trace's native byte order: " + "ret=%d", ret); + goto end; + } + + got_trace_decl = true; + } + + if (!got_trace_decl) { + BT_LOGD_STR("Incomplete AST: need trace (`trace` block)."); + ret = -EINCOMPLETE; + goto end; + } + } + + BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE || + ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG); + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* Environment */ + bt_list_for_each_entry(iter, &node->u.root.env, siblings) { + ret = visit_env(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit trace's environment (`env` block) entry: " + "ret=%d", ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* + * Visit clock blocks. + */ + bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { + ret = visit_clock_decl(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit clock class: ret=%d", + ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* + * Visit root declarations next, as they can be used by any + * following entity. + */ + bt_list_for_each_entry(iter, &node->u.root.declaration_list, + siblings) { + ret = visit_root_decl(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit root entry: ret=%d", + ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* Callsite blocks are not supported */ + bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { + _BT_LOGW_NODE(iter, + "\"callsite\" blocks are not supported as of this version."); + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* Trace */ + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + ret = visit_trace_decl(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit trace (`trace` block): " + "ret=%d", ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* Streams */ + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + ret = visit_stream_decl(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit stream class: ret=%d", + ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + + /* Events */ + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + ret = visit_event_decl(ctx, iter); + if (ret) { + _BT_LOGE_NODE(iter, + "Cannot visit event class: ret=%d", + ret); + goto end; + } + } + + BT_ASSERT(ctx->current_scope && + ctx->current_scope->parent_scope == NULL); + break; + } + default: + _BT_LOGE_NODE(node, + "Unexpected node type: node-type=%d", + node->type); + ret = -EINVAL; + goto end; + } + + /* Update default clock classes */ + ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* Update trace class meanings */ + ret = ctf_trace_class_update_meanings(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* Update stream class configuration */ + ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* Update text arrays and sequences */ + ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* Resolve sequence lengths and variant tags */ + ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + if (ctx->trace_class) { + /* + * Update "in IR" for field classes. + * + * If we have no IR trace class, then we'll have no way + * to create IR fields anyway, so we leave all the + * `in_ir` members false. + */ + ret = ctf_trace_class_update_in_ir(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + } + + /* Update saved value indexes */ + ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* Validate what we have so far */ + ret = ctf_trace_class_validate(ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + + /* + * If there are fields which are not related to the CTF format + * itself in the packet header and in event header field + * classes, warn about it because they are never translated. + */ + ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc); + + if (ctx->trace_class) { + /* Copy new CTF metadata -> new IR metadata */ + ret = ctf_trace_class_translate(ctx->self_comp, + ctx->trace_class, ctx->ctf_tc); + if (ret) { + ret = -EINVAL; + goto end; + } + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/metadata/visitor-parent-links.c b/src/plugins/ctf/common/metadata/visitor-parent-links.c new file mode 100644 index 00000000..64098a9a --- /dev/null +++ b/src/plugins/ctf/common/metadata/visitor-parent-links.c @@ -0,0 +1,466 @@ +/* + * ctf-visitor-parent-links.c + * + * Common Trace Format Metadata Parent Link Creator. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-PARENT-LINKS-VISITOR" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include "common/babeltrace.h" +#include "common/list.h" +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +static +int ctf_visitor_unary_expression(int depth, struct ctf_node *node) +{ + int ret = 0; + + switch (node->u.unary_expression.link) { + case UNARY_LINK_UNKNOWN: + case UNARY_DOTLINK: + case UNARY_ARROWLINK: + case UNARY_DOTDOTDOT: + break; + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown expression link type: type=%d\n", + node->u.unary_expression.link); + return -EINVAL; + } + + switch (node->u.unary_expression.type) { + case UNARY_STRING: + case UNARY_SIGNED_CONSTANT: + case UNARY_UNSIGNED_CONSTANT: + break; + case UNARY_SBRAC: + node->u.unary_expression.u.sbrac_exp->parent = node; + ret = ctf_visitor_unary_expression(depth + 1, + node->u.unary_expression.u.sbrac_exp); + if (ret) + return ret; + break; + + case UNARY_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown expression link type: type=%d\n", + node->u.unary_expression.link); + return -EINVAL; + } + return 0; +} + +static +int ctf_visitor_type_specifier(int depth, struct ctf_node *node) +{ + int ret; + + switch (node->u.field_class_specifier.type) { + case TYPESPEC_VOID: + case TYPESPEC_CHAR: + case TYPESPEC_SHORT: + case TYPESPEC_INT: + case TYPESPEC_LONG: + case TYPESPEC_FLOAT: + case TYPESPEC_DOUBLE: + case TYPESPEC_SIGNED: + case TYPESPEC_UNSIGNED: + case TYPESPEC_BOOL: + case TYPESPEC_COMPLEX: + case TYPESPEC_IMAGINARY: + case TYPESPEC_CONST: + case TYPESPEC_ID_TYPE: + break; + case TYPESPEC_FLOATING_POINT: + case TYPESPEC_INTEGER: + case TYPESPEC_STRING: + case TYPESPEC_STRUCT: + case TYPESPEC_VARIANT: + case TYPESPEC_ENUM: + node->u.field_class_specifier.node->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_specifier.node); + if (ret) + return ret; + break; + + case TYPESPEC_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown type specifier: type=%d\n", + node->u.field_class_specifier.type); + return -EINVAL; + } + return 0; +} + +static +int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + depth++; + + bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + + switch (node->u.field_class_declarator.type) { + case TYPEDEC_ID: + break; + case TYPEDEC_NESTED: + if (node->u.field_class_declarator.u.nested.field_class_declarator) { + node->u.field_class_declarator.u.nested.field_class_declarator->parent = node; + ret = ctf_visitor_parent_links(depth + 1, + node->u.field_class_declarator.u.nested.field_class_declarator); + if (ret) + return ret; + } + if (!node->u.field_class_declarator.u.nested.abstract_array) { + bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + } + if (node->u.field_class_declarator.bitfield_len) { + node->u.field_class_declarator.bitfield_len = node; + ret = ctf_visitor_parent_links(depth + 1, + node->u.field_class_declarator.bitfield_len); + if (ret) + return ret; + } + break; + case TYPEDEC_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown type declarator: type=%d\n", + node->u.field_class_declarator.type); + return -EINVAL; + } + depth--; + return 0; +} + +int ctf_visitor_parent_links(int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + if (node->visited) + return 0; + + switch (node->type) { + case NODE_ROOT: + bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_EVENT: + bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_STREAM: + bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENV: + bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_TRACE: + bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_CLOCK: + bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_CALLSITE: + bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_CTF_EXPRESSION: + depth++; + bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_UNARY_EXPRESSION: + return ctf_visitor_unary_expression(depth, node); + + case NODE_TYPEDEF: + depth++; + node->u.field_class_def.field_class_specifier_list->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_def.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS_TARGET: + depth++; + node->u.field_class_alias_target.field_class_specifier_list->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias_target.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS_ALIAS: + depth++; + node->u.field_class_alias_name.field_class_specifier_list->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias_name.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS: + node->u.field_class_alias.target->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.target); + if (ret) + return ret; + node->u.field_class_alias.alias->parent = node; + ret = ctf_visitor_parent_links(depth + 1, node->u.field_class_alias.alias); + if (ret) + return ret; + break; + + case NODE_TYPE_SPECIFIER_LIST: + bt_list_for_each_entry(iter, &node->u.field_class_specifier_list.head, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_TYPE_SPECIFIER: + ret = ctf_visitor_type_specifier(depth, node); + if (ret) + return ret; + break; + case NODE_POINTER: + break; + case NODE_TYPE_DECLARATOR: + ret = ctf_visitor_field_class_declarator(depth, node); + if (ret) + return ret; + break; + + case NODE_FLOATING_POINT: + bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_INTEGER: + bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_STRING: + bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENUMERATOR: + bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENUM: + depth++; + if (node->u._enum.container_field_class) { + ret = ctf_visitor_parent_links(depth + 1, node->u._enum.container_field_class); + if (ret) + return ret; + } + + bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node; + ret = ctf_visitor_parent_links(depth + 1, + node->u.struct_or_variant_declaration.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_VARIANT: + bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_STRUCT: + bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u._struct.min_align, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown node type: type=%d\n", node->type); + return -EINVAL; + } + return ret; +} diff --git a/src/plugins/ctf/common/metadata/visitor-semantic-validator.c b/src/plugins/ctf/common/metadata/visitor-semantic-validator.c new file mode 100644 index 00000000..5592d6d5 --- /dev/null +++ b/src/plugins/ctf/common/metadata/visitor-semantic-validator.c @@ -0,0 +1,1012 @@ +/* + * ctf-visitor-semantic-validator.c + * + * Common Trace Format Metadata Semantic Validator. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-METADATA-SEMANTIC-VALIDATOR-VISITOR" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include +#include "common/list.h" +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +#define _bt_list_first_entry(ptr, type, member) \ + bt_list_entry((ptr)->next, type, member) + +static +int _ctf_visitor_semantic_check(int depth, struct ctf_node *node); + +static +int ctf_visitor_unary_expression(int depth, struct ctf_node *node) +{ + struct ctf_node *iter; + int is_ctf_exp = 0, is_ctf_exp_left = 0; + + switch (node->parent->type) { + case NODE_CTF_EXPRESSION: + is_ctf_exp = 1; + bt_list_for_each_entry(iter, &node->parent->u.ctf_expression.left, + siblings) { + if (iter == node) { + is_ctf_exp_left = 1; + /* + * We are a left child of a ctf expression. + * We are only allowed to be a string. + */ + if (node->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_LINENO(node->lineno, + "Left child of a CTF expression is only allowed to be a string."); + goto errperm; + } + break; + } + } + /* Right child of a ctf expression can be any type of unary exp. */ + break; /* OK */ + case NODE_TYPE_DECLARATOR: + /* + * We are the length of a type declarator. + */ + switch (node->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + case UNARY_STRING: + break; + default: + _BT_LOGE_LINENO(node->lineno, + "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`)."); + goto errperm; + } + break; /* OK */ + + case NODE_STRUCT: + /* + * We are the size of a struct align attribute. + */ + switch (node->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + break; + default: + _BT_LOGE_LINENO(node->lineno, + "Structure alignment attribute can only be an unsigned numeric constant."); + goto errperm; + } + break; + + case NODE_ENUMERATOR: + /* The enumerator's parent has validated its validity already. */ + break; /* OK */ + + case NODE_UNARY_EXPRESSION: + /* + * We disallow nested unary expressions and "sbrac" unary + * expressions. + */ + _BT_LOGE_LINENO(node->lineno, + "Nested unary expressions not allowed (`()` and `[]`)."); + goto errperm; + + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_POINTER: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_VARIANT: + default: + goto errinval; + } + + switch (node->u.unary_expression.link) { + case UNARY_LINK_UNKNOWN: + /* We don't allow empty link except on the first node of the list */ + if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ? + &node->parent->u.ctf_expression.left : + &node->parent->u.ctf_expression.right, + struct ctf_node, + siblings) != node) { + _BT_LOGE_LINENO(node->lineno, + "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`)."); + goto errperm; + } + break; /* OK */ + case UNARY_DOTLINK: + case UNARY_ARROWLINK: + /* We only allow -> and . links between children of ctf_expression. */ + if (node->parent->type != NODE_CTF_EXPRESSION) { + _BT_LOGE_LINENO(node->lineno, + "Links `.` and `->` are only allowed as children of CTF expression."); + goto errperm; + } + /* + * Only strings can be separated linked by . or ->. + * This includes "", '' and non-quoted identifiers. + */ + if (node->u.unary_expression.type != UNARY_STRING) { + _BT_LOGE_LINENO(node->lineno, + "Links `.` and `->` are only allowed to separate strings and identifiers."); + goto errperm; + } + /* We don't allow link on the first node of the list */ + if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ? + &node->parent->u.ctf_expression.left : + &node->parent->u.ctf_expression.right, + struct ctf_node, + siblings) == node) { + _BT_LOGE_LINENO(node->lineno, + "Links `.` and `->` are not allowed before first node of the unary expression list."); + goto errperm; + } + break; + case UNARY_DOTDOTDOT: + /* We only allow ... link between children of enumerator. */ + if (node->parent->type != NODE_ENUMERATOR) { + _BT_LOGE_LINENO(node->lineno, + "Link `...` is only allowed within enumerator."); + goto errperm; + } + /* We don't allow link on the first node of the list */ + if (_bt_list_first_entry(&node->parent->u.enumerator.values, + struct ctf_node, + siblings) == node) { + _BT_LOGE_LINENO(node->lineno, + "Link `...` is not allowed on the first node of the unary expression list."); + goto errperm; + } + break; + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown expression link type: type=%d", + node->u.unary_expression.link); + return -EINVAL; + } + return 0; + +errinval: + _BT_LOGE_LINENO(node->lineno, + "Incoherent parent node's type: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EINVAL; /* Incoherent structure */ + +errperm: + _BT_LOGE_LINENO(node->lineno, + "Semantic error: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EPERM; /* Structure not allowed */ +} + +static +int ctf_visitor_field_class_specifier_list(int depth, struct ctf_node *node) +{ + switch (node->parent->type) { + case NODE_CTF_EXPRESSION: + case NODE_TYPE_DECLARATOR: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_ROOT: + break; /* OK */ + + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUMERATOR: + case NODE_VARIANT: + case NODE_STRUCT: + default: + goto errinval; + } + return 0; +errinval: + _BT_LOGE_LINENO(node->lineno, + "Incoherent parent node's type: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EINVAL; /* Incoherent structure */ +} + +static +int ctf_visitor_field_class_specifier(int depth, struct ctf_node *node) +{ + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER_LIST: + break; /* OK */ + + case NODE_CTF_EXPRESSION: + case NODE_TYPE_DECLARATOR: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_ENUM: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_POINTER: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUMERATOR: + case NODE_VARIANT: + case NODE_STRUCT: + default: + goto errinval; + } + return 0; +errinval: + _BT_LOGE_LINENO(node->lineno, + "Incoherent parent node's type: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EINVAL; /* Incoherent structure */ +} + +static +int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + depth++; + + switch (node->parent->type) { + case NODE_TYPE_DECLARATOR: + /* + * A nested field class declarator is not allowed to + * contain pointers. + */ + if (!bt_list_empty(&node->u.field_class_declarator.pointers)) + goto errperm; + break; /* OK */ + case NODE_TYPEALIAS_TARGET: + break; /* OK */ + case NODE_TYPEALIAS_ALIAS: + /* + * Only accept alias name containing: + * - identifier + * - identifier * (any number of pointers) + * NOT accepting alias names containing [] (would otherwise + * cause semantic clash for later declarations of + * arrays/sequences of elements, where elements could be + * arrays/sequences themselves (if allowed in field class alias). + * NOT accepting alias with identifier. The declarator should + * be either empty or contain pointer(s). + */ + if (node->u.field_class_declarator.type == TYPEDEC_NESTED) + goto errperm; + bt_list_for_each_entry(iter, &node->parent->u.field_class_alias_name.field_class_specifier_list->u.field_class_specifier_list.head, + siblings) { + switch (iter->u.field_class_specifier.type) { + case TYPESPEC_FLOATING_POINT: + case TYPESPEC_INTEGER: + case TYPESPEC_STRING: + case TYPESPEC_STRUCT: + case TYPESPEC_VARIANT: + case TYPESPEC_ENUM: + if (bt_list_empty(&node->u.field_class_declarator.pointers)) + goto errperm; + break; + default: + break; + } + } + if (node->u.field_class_declarator.type == TYPEDEC_ID && + node->u.field_class_declarator.u.id != NULL) + goto errperm; + break; /* OK */ + case NODE_TYPEDEF: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + break; /* OK */ + + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_CTF_EXPRESSION: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_POINTER: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_VARIANT: + case NODE_STRUCT: + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers, + siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + + switch (node->u.field_class_declarator.type) { + case TYPEDEC_ID: + break; + case TYPEDEC_NESTED: + { + if (node->u.field_class_declarator.u.nested.field_class_declarator) { + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.field_class_declarator.u.nested.field_class_declarator); + if (ret) + return ret; + } + if (!node->u.field_class_declarator.u.nested.abstract_array) { + bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length, + siblings) { + if (iter->type != NODE_UNARY_EXPRESSION) { + _BT_LOGE_LINENO(node->lineno, + "Expecting unary expression as length: node-type=%s", + node_type(iter)); + return -EINVAL; + } + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + } else { + if (node->parent->type == NODE_TYPEALIAS_TARGET) { + _BT_LOGE_LINENO(node->lineno, + "Abstract array declarator not permitted as target of field class alias."); + return -EINVAL; + } + } + if (node->u.field_class_declarator.bitfield_len) { + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.field_class_declarator.bitfield_len); + if (ret) + return ret; + } + break; + } + case TYPEDEC_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown field class declarator: type=%d", + node->u.field_class_declarator.type); + return -EINVAL; + } + depth--; + return 0; + +errinval: + _BT_LOGE_LINENO(node->lineno, + "Incoherent parent node's type: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EINVAL; /* Incoherent structure */ + +errperm: + _BT_LOGE_LINENO(node->lineno, + "Semantic error: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EPERM; /* Structure not allowed */ +} + +static +int _ctf_visitor_semantic_check(int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + if (node->visited) + return 0; + + switch (node->type) { + case NODE_ROOT: + bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_EVENT: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_STREAM: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENV: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_TRACE: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_CLOCK: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_CALLSITE: + switch (node->parent->type) { + case NODE_ROOT: + break; /* OK */ + default: + goto errinval; + } + + bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_CTF_EXPRESSION: + switch (node->parent->type) { + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_ENV: + case NODE_TRACE: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + break; /* OK */ + + case NODE_CTF_EXPRESSION: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_TYPEALIAS: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_VARIANT: + case NODE_STRUCT: + default: + goto errinval; + } + + depth++; + bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_UNARY_EXPRESSION: + return ctf_visitor_unary_expression(depth, node); + + case NODE_TYPEDEF: + switch (node->parent->type) { + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_TRACE: + case NODE_VARIANT: + case NODE_STRUCT: + break; /* OK */ + + case NODE_CTF_EXPRESSION: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_ENV: + default: + goto errinval; + } + + depth++; + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.field_class_def.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS_TARGET: + { + int nr_declarators; + + switch (node->parent->type) { + case NODE_TYPEALIAS: + break; /* OK */ + default: + goto errinval; + } + + depth++; + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.field_class_alias_target.field_class_specifier_list); + if (ret) + return ret; + nr_declarators = 0; + bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + nr_declarators++; + } + if (nr_declarators > 1) { + _BT_LOGE_LINENO(node->lineno, + "Too many declarators in field class alias's name (maximum is 1): count=%d", + nr_declarators); + return -EINVAL; + } + depth--; + break; + } + case NODE_TYPEALIAS_ALIAS: + { + int nr_declarators; + + switch (node->parent->type) { + case NODE_TYPEALIAS: + break; /* OK */ + default: + goto errinval; + } + + depth++; + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.field_class_alias_name.field_class_specifier_list); + if (ret) + return ret; + nr_declarators = 0; + bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + nr_declarators++; + } + if (nr_declarators > 1) { + _BT_LOGE_LINENO(node->lineno, + "Too many declarators in field class alias's name (maximum is 1): count=%d", + nr_declarators); + return -EINVAL; + } + depth--; + break; + } + case NODE_TYPEALIAS: + switch (node->parent->type) { + case NODE_ROOT: + case NODE_EVENT: + case NODE_STREAM: + case NODE_TRACE: + case NODE_VARIANT: + case NODE_STRUCT: + break; /* OK */ + + case NODE_CTF_EXPRESSION: + case NODE_UNARY_EXPRESSION: + case NODE_TYPEDEF: + case NODE_TYPEALIAS_TARGET: + case NODE_TYPEALIAS_ALIAS: + case NODE_TYPEALIAS: + case NODE_STRUCT_OR_VARIANT_DECLARATION: + case NODE_TYPE_SPECIFIER: + case NODE_TYPE_SPECIFIER_LIST: + case NODE_POINTER: + case NODE_TYPE_DECLARATOR: + case NODE_FLOATING_POINT: + case NODE_INTEGER: + case NODE_STRING: + case NODE_ENUMERATOR: + case NODE_ENUM: + case NODE_CLOCK: + case NODE_CALLSITE: + case NODE_ENV: + default: + goto errinval; + } + + ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.target); + if (ret) + return ret; + ret = _ctf_visitor_semantic_check(depth + 1, node->u.field_class_alias.alias); + if (ret) + return ret; + break; + + case NODE_TYPE_SPECIFIER_LIST: + ret = ctf_visitor_field_class_specifier_list(depth, node); + if (ret) + return ret; + break; + case NODE_TYPE_SPECIFIER: + ret = ctf_visitor_field_class_specifier(depth, node); + if (ret) + return ret; + break; + case NODE_POINTER: + switch (node->parent->type) { + case NODE_TYPE_DECLARATOR: + break; /* OK */ + default: + goto errinval; + } + break; + case NODE_TYPE_DECLARATOR: + ret = ctf_visitor_field_class_declarator(depth, node); + if (ret) + return ret; + break; + + case NODE_FLOATING_POINT: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + case NODE_UNARY_EXPRESSION: + goto errperm; + } + bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_INTEGER: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + } + + bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_STRING: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + case NODE_UNARY_EXPRESSION: + goto errperm; + } + + bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENUMERATOR: + switch (node->parent->type) { + case NODE_ENUM: + break; + default: + goto errinval; + } + /* + * Enumerators are only allows to contain: + * numeric unary expression + * or num. unary exp. ... num. unary exp + */ + { + int count = 0; + + bt_list_for_each_entry(iter, &node->u.enumerator.values, + siblings) { + switch (count++) { + case 0: if (iter->type != NODE_UNARY_EXPRESSION + || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT + && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) + || iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) { + _BT_LOGE_LINENO(iter->lineno, + "First unary expression of enumerator is unexpected."); + goto errperm; + } + break; + case 1: if (iter->type != NODE_UNARY_EXPRESSION + || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT + && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) + || iter->u.unary_expression.link != UNARY_DOTDOTDOT) { + _BT_LOGE_LINENO(iter->lineno, + "Second unary expression of enumerator is unexpected."); + goto errperm; + } + break; + default: + goto errperm; + } + } + } + + bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENUM: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + case NODE_UNARY_EXPRESSION: + goto errperm; + } + + depth++; + ret = _ctf_visitor_semantic_check(depth + 1, node->u._enum.container_field_class); + if (ret) + return ret; + + bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + switch (node->parent->type) { + case NODE_STRUCT: + case NODE_VARIANT: + break; + default: + goto errinval; + } + ret = _ctf_visitor_semantic_check(depth + 1, + node->u.struct_or_variant_declaration.field_class_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_VARIANT: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + case NODE_UNARY_EXPRESSION: + goto errperm; + } + bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_STRUCT: + switch (node->parent->type) { + case NODE_TYPE_SPECIFIER: + break; /* OK */ + default: + goto errinval; + + case NODE_UNARY_EXPRESSION: + goto errperm; + } + bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) { + ret = _ctf_visitor_semantic_check(depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_UNKNOWN: + default: + _BT_LOGE_LINENO(node->lineno, + "Unknown node type: type=%d", node->type); + return -EINVAL; + } + return ret; + +errinval: + _BT_LOGE_LINENO(node->lineno, + "Incoherent parent node's type: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EINVAL; /* Incoherent structure */ + +errperm: + _BT_LOGE_LINENO(node->lineno, + "Semantic error: node-type=%s, parent-node-type=%s", + node_type(node), node_type(node->parent)); + return -EPERM; /* Structure not allowed */ +} + +int ctf_visitor_semantic_check(int depth, struct ctf_node *node) +{ + int ret = 0; + + /* + * First make sure we create the parent links for all children. Let's + * take the safe route and recreate them at each validation, just in + * case the structure has changed. + */ + ret = ctf_visitor_parent_links(depth, node); + if (ret) { + _BT_LOGE_LINENO(node->lineno, + "Cannot create parent links in metadata's AST: " + "ret=%d", ret); + goto end; + } + + ret = _ctf_visitor_semantic_check(depth, node); + if (ret) { + _BT_LOGE_LINENO(node->lineno, + "Cannot check metadata's AST semantics: " + "ret=%d", ret); + goto end; + } + +end: + return ret; +} diff --git a/src/plugins/ctf/common/msg-iter/Makefile.am b/src/plugins/ctf/common/msg-iter/Makefile.am new file mode 100644 index 00000000..7c3c2087 --- /dev/null +++ b/src/plugins/ctf/common/msg-iter/Makefile.am @@ -0,0 +1,7 @@ +noinst_LTLIBRARIES = libctf-msg-iter.la + +libctf_msg_iter_la_SOURCES = \ + msg-iter.c \ + msg-iter.h \ + logging.c \ + logging.h diff --git a/src/plugins/ctf/common/msg-iter/logging.c b/src/plugins/ctf/common/msg-iter/logging.c new file mode 100644 index 00000000..662c4433 --- /dev/null +++ b/src/plugins/ctf/common/msg-iter/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL ctf_msg_iter_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(ctf_msg_iter_log_level, + "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL"); diff --git a/src/plugins/ctf/common/msg-iter/logging.h b/src/plugins/ctf/common/msg-iter/logging.h new file mode 100644 index 00000000..9788916a --- /dev/null +++ b/src/plugins/ctf/common/msg-iter/logging.h @@ -0,0 +1,31 @@ +#ifndef CTF_MSG_ITER_LOGGING_H +#define CTF_MSG_ITER_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL ctf_msg_iter_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_msg_iter_log_level); + +#endif /* CTF_MSG_ITER_LOGGING_H */ diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.c b/src/plugins/ctf/common/msg-iter/msg-iter.c new file mode 100644 index 00000000..2a1c4713 --- /dev/null +++ b/src/plugins/ctf/common/msg-iter/msg-iter.c @@ -0,0 +1,3046 @@ +/* + * Babeltrace - CTF message iterator + * + * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-MSG-ITER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include "common/assert.h" +#include +#include +#include "common/common.h" +#include +#include + +#include "msg-iter.h" +#include "../bfcr/bfcr.h" + +struct bt_msg_iter; + +/* A visit stack entry */ +struct stack_entry { + /* + * Current base field, one of: + * + * * string + * * structure + * * array + * * sequence + * * variant + * + * Field is borrowed. + */ + bt_field *base; + + /* Index of next field to set */ + size_t index; +}; + +/* Visit stack */ +struct stack { + /* Entries (struct stack_entry) */ + GArray *entries; + + /* Number of active entries */ + size_t size; +}; + +/* State */ +enum state { + STATE_INIT, + STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN, + STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, + STATE_AFTER_TRACE_PACKET_HEADER, + STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN, + STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, + STATE_AFTER_STREAM_PACKET_CONTEXT, + STATE_CHECK_EMIT_MSG_STREAM_BEGINNING, + STATE_EMIT_MSG_STREAM_BEGINNING, + STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING, + STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS, + STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS, + STATE_EMIT_MSG_DISCARDED_EVENTS, + STATE_EMIT_MSG_DISCARDED_PACKETS, + STATE_EMIT_MSG_PACKET_BEGINNING, + STATE_DSCOPE_EVENT_HEADER_BEGIN, + STATE_DSCOPE_EVENT_HEADER_CONTINUE, + STATE_AFTER_EVENT_HEADER, + STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN, + STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, + STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN, + STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, + STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, + STATE_EMIT_MSG_EVENT, + STATE_SKIP_PACKET_PADDING, + STATE_EMIT_MSG_PACKET_END_MULTI, + STATE_EMIT_MSG_PACKET_END_SINGLE, + STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END, + STATE_EMIT_MSG_STREAM_ACTIVITY_END, + STATE_EMIT_MSG_STREAM_END, + STATE_DONE, +}; + +struct end_of_packet_snapshots { + uint64_t discarded_events; + uint64_t packets; + uint64_t beginning_clock; + uint64_t end_clock; +}; + +/* CTF message iterator */ +struct bt_msg_iter { + /* Visit stack */ + struct stack *stack; + + /* Current message iterator to create messages (weak) */ + bt_self_message_iterator *msg_iter; + + /* + * True to emit stream beginning and stream activity beginning + * messages. + */ + bool emit_stream_begin_msg; + + /* True to emit stream end and stream activity end messages */ + bool emit_stream_end_msg; + + /* True to set the stream */ + bool set_stream; + + /* + * Current dynamic scope field pointer. + * + * This is set by read_dscope_begin_state() and contains the + * value of one of the pointers in `dscopes` below. + */ + bt_field *cur_dscope_field; + + /* + * True if we're done filling a string field from a text + * array/sequence payload. + */ + bool done_filling_string; + + /* Trace and classes */ + /* True to set IR fields */ + bool set_ir_fields; + + struct { + struct ctf_trace_class *tc; + struct ctf_stream_class *sc; + struct ctf_event_class *ec; + } meta; + + /* Current packet context field wrapper (NULL if not created yet) */ + bt_packet_context_field *packet_context_field; + + /* Current packet (NULL if not created yet) */ + bt_packet *packet; + + /* Current stream (NULL if not set yet) */ + bt_stream *stream; + + /* Current event (NULL if not created yet) */ + bt_event *event; + + /* Current event message (NULL if not created yet) */ + bt_message *event_msg; + + /* Database of current dynamic scopes */ + struct { + bt_field *stream_packet_context; + bt_field *event_common_context; + bt_field *event_spec_context; + bt_field *event_payload; + } dscopes; + + /* Current state */ + enum state state; + + /* Current medium buffer data */ + struct { + /* Last address provided by medium */ + const uint8_t *addr; + + /* Buffer size provided by medium (bytes) */ + size_t sz; + + /* Offset within whole packet of addr (bits) */ + size_t packet_offset; + + /* Current position from addr (bits) */ + size_t at; + + /* Position of the last event header from addr (bits) */ + size_t last_eh_at; + } buf; + + /* Binary type reader */ + struct bt_bfcr *bfcr; + + /* Current medium data */ + struct { + struct bt_msg_iter_medium_ops medops; + size_t max_request_sz; + void *data; + } medium; + + /* Current packet size (bits) (-1 if unknown) */ + int64_t cur_exp_packet_total_size; + + /* Current content size (bits) (-1 if unknown) */ + int64_t cur_exp_packet_content_size; + + /* Current stream class ID */ + int64_t cur_stream_class_id; + + /* Current event class ID */ + int64_t cur_event_class_id; + + /* Current data stream ID */ + int64_t cur_data_stream_id; + + /* + * Offset, in the underlying media, of the current packet's + * start (-1 if unknown). + */ + off_t cur_packet_offset; + + /* Default clock's current value */ + uint64_t default_clock_snapshot; + + /* End of current packet snapshots */ + struct end_of_packet_snapshots snapshots; + + /* End of previous packet snapshots */ + struct end_of_packet_snapshots prev_packet_snapshots; + + /* Stored values (for sequence lengths, variant tags) */ + GArray *stored_values; +}; + +static inline +const char *state_string(enum state state) +{ + switch (state) { + case STATE_INIT: + return "STATE_INIT"; + case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: + return "STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN"; + case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: + return "STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE"; + case STATE_AFTER_TRACE_PACKET_HEADER: + return "STATE_AFTER_TRACE_PACKET_HEADER"; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: + return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN"; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: + return "STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE"; + case STATE_AFTER_STREAM_PACKET_CONTEXT: + return "STATE_AFTER_STREAM_PACKET_CONTEXT"; + case STATE_EMIT_MSG_STREAM_BEGINNING: + return "STATE_EMIT_MSG_STREAM_BEGINNING"; + case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: + return "STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING"; + case STATE_EMIT_MSG_PACKET_BEGINNING: + return "STATE_EMIT_MSG_PACKET_BEGINNING"; + case STATE_EMIT_MSG_DISCARDED_EVENTS: + return "STATE_EMIT_MSG_DISCARDED_EVENTS"; + case STATE_EMIT_MSG_DISCARDED_PACKETS: + return "STATE_EMIT_MSG_DISCARDED_PACKETS"; + case STATE_DSCOPE_EVENT_HEADER_BEGIN: + return "STATE_DSCOPE_EVENT_HEADER_BEGIN"; + case STATE_DSCOPE_EVENT_HEADER_CONTINUE: + return "STATE_DSCOPE_EVENT_HEADER_CONTINUE"; + case STATE_AFTER_EVENT_HEADER: + return "STATE_AFTER_EVENT_HEADER"; + case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN: + return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN"; + case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE: + return "STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE"; + case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN: + return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN"; + case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE: + return "STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE"; + case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: + return "STATE_DSCOPE_EVENT_PAYLOAD_BEGIN"; + case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: + return "STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE"; + case STATE_EMIT_MSG_EVENT: + return "STATE_EMIT_MSG_EVENT"; + case STATE_SKIP_PACKET_PADDING: + return "STATE_SKIP_PACKET_PADDING"; + case STATE_EMIT_MSG_PACKET_END_MULTI: + return "STATE_EMIT_MSG_PACKET_END_MULTI"; + case STATE_EMIT_MSG_PACKET_END_SINGLE: + return "STATE_EMIT_MSG_PACKET_END_SINGLE"; + case STATE_EMIT_MSG_STREAM_ACTIVITY_END: + return "STATE_EMIT_MSG_STREAM_ACTIVITY_END"; + case STATE_EMIT_MSG_STREAM_END: + return "STATE_EMIT_MSG_STREAM_END"; + case STATE_DONE: + return "STATE_DONE"; + default: + return "(unknown)"; + } +} + +static +int bt_msg_iter_switch_packet(struct bt_msg_iter *notit); + +static +struct stack *stack_new(struct bt_msg_iter *notit) +{ + struct stack *stack = NULL; + + stack = g_new0(struct stack, 1); + if (!stack) { + BT_LOGE_STR("Failed to allocate one stack."); + goto error; + } + + stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry)); + if (!stack->entries) { + BT_LOGE_STR("Failed to allocate a GArray."); + goto error; + } + + BT_LOGD("Created stack: notit-addr=%p, stack-addr=%p", notit, stack); + goto end; + +error: + g_free(stack); + stack = NULL; + +end: + return stack; +} + +static +void stack_destroy(struct stack *stack) +{ + BT_ASSERT(stack); + BT_LOGD("Destroying stack: addr=%p", stack); + + if (stack->entries) { + g_array_free(stack->entries, TRUE); + } + + g_free(stack); +} + +static +void stack_push(struct stack *stack, bt_field *base) +{ + struct stack_entry *entry; + + BT_ASSERT(stack); + BT_ASSERT(base); + BT_LOGV("Pushing base field on stack: stack-addr=%p, " + "stack-size-before=%zu, stack-size-after=%zu", + stack, stack->size, stack->size + 1); + + if (stack->entries->len == stack->size) { + g_array_set_size(stack->entries, stack->size + 1); + } + + entry = &g_array_index(stack->entries, struct stack_entry, stack->size); + entry->base = base; + entry->index = 0; + stack->size++; +} + +static inline +unsigned int stack_size(struct stack *stack) +{ + BT_ASSERT(stack); + return stack->size; +} + +static +void stack_pop(struct stack *stack) +{ + BT_ASSERT(stack); + BT_ASSERT(stack_size(stack)); + BT_LOGV("Popping from stack: " + "stack-addr=%p, stack-size-before=%zu, stack-size-after=%zu", + stack, stack->size, stack->size - 1); + stack->size--; +} + +static inline +struct stack_entry *stack_top(struct stack *stack) +{ + BT_ASSERT(stack); + BT_ASSERT(stack_size(stack)); + return &g_array_index(stack->entries, struct stack_entry, + stack->size - 1); +} + +static inline +bool stack_empty(struct stack *stack) +{ + return stack_size(stack) == 0; +} + +static +void stack_clear(struct stack *stack) +{ + BT_ASSERT(stack); + stack->size = 0; +} + +static inline +enum bt_msg_iter_status msg_iter_status_from_m_status( + enum bt_msg_iter_medium_status m_status) +{ + /* They are the same */ + return (int) m_status; +} + +static inline +size_t buf_size_bits(struct bt_msg_iter *notit) +{ + return notit->buf.sz * 8; +} + +static inline +size_t buf_available_bits(struct bt_msg_iter *notit) +{ + return buf_size_bits(notit) - notit->buf.at; +} + +static inline +size_t packet_at(struct bt_msg_iter *notit) +{ + return notit->buf.packet_offset + notit->buf.at; +} + +static inline +void buf_consume_bits(struct bt_msg_iter *notit, size_t incr) +{ + BT_LOGV("Advancing cursor: notit-addr=%p, cur-before=%zu, cur-after=%zu", + notit, notit->buf.at, notit->buf.at + incr); + notit->buf.at += incr; +} + +static +enum bt_msg_iter_status request_medium_bytes( + struct bt_msg_iter *notit) +{ + uint8_t *buffer_addr = NULL; + size_t buffer_sz = 0; + enum bt_msg_iter_medium_status m_status; + + BT_LOGV("Calling user function (request bytes): notit-addr=%p, " + "request-size=%zu", notit, notit->medium.max_request_sz); + m_status = notit->medium.medops.request_bytes( + notit->medium.max_request_sz, &buffer_addr, + &buffer_sz, notit->medium.data); + BT_LOGV("User function returned: status=%s, buf-addr=%p, buf-size=%zu", + bt_msg_iter_medium_status_string(m_status), + buffer_addr, buffer_sz); + if (m_status == BT_MSG_ITER_MEDIUM_STATUS_OK) { + BT_ASSERT(buffer_sz != 0); + + /* New packet offset is old one + old size (in bits) */ + notit->buf.packet_offset += buf_size_bits(notit); + + /* Restart at the beginning of the new medium buffer */ + notit->buf.at = 0; + notit->buf.last_eh_at = SIZE_MAX; + + /* New medium buffer size */ + notit->buf.sz = buffer_sz; + + /* New medium buffer address */ + notit->buf.addr = buffer_addr; + + BT_LOGV("User function returned new bytes: " + "packet-offset=%zu, cur=%zu, size=%zu, addr=%p", + notit->buf.packet_offset, notit->buf.at, + notit->buf.sz, notit->buf.addr); + BT_LOGV_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:", + buffer_addr); + } else if (m_status == BT_MSG_ITER_MEDIUM_STATUS_EOF) { + /* + * User returned end of stream: validate that we're not + * in the middle of a packet header, packet context, or + * event. + */ + if (notit->cur_exp_packet_total_size >= 0) { + if (packet_at(notit) == + notit->cur_exp_packet_total_size) { + goto end; + } + } else { + if (packet_at(notit) == 0) { + goto end; + } + + if (notit->buf.last_eh_at != SIZE_MAX && + notit->buf.at == notit->buf.last_eh_at) { + goto end; + } + } + + /* All other states are invalid */ + BT_LOGW("User function returned %s, but message iterator is in an unexpected state: " + "state=%s, cur-packet-size=%" PRId64 ", cur=%zu, " + "packet-cur=%zu, last-eh-at=%zu", + bt_msg_iter_medium_status_string(m_status), + state_string(notit->state), + notit->cur_exp_packet_total_size, + notit->buf.at, packet_at(notit), + notit->buf.last_eh_at); + m_status = BT_MSG_ITER_MEDIUM_STATUS_ERROR; + } else if (m_status < 0) { + BT_LOGW("User function failed: status=%s", + bt_msg_iter_medium_status_string(m_status)); + } + +end: + return msg_iter_status_from_m_status(m_status); +} + +static inline +enum bt_msg_iter_status buf_ensure_available_bits( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + if (unlikely(buf_available_bits(notit) == 0)) { + /* + * This _cannot_ return BT_MSG_ITER_STATUS_OK + * _and_ no bits. + */ + status = request_medium_bytes(notit); + } + + return status; +} + +static +enum bt_msg_iter_status read_dscope_begin_state( + struct bt_msg_iter *notit, + struct ctf_field_class *dscope_fc, + enum state done_state, enum state continue_state, + bt_field *dscope_field) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + enum bt_bfcr_status bfcr_status; + size_t consumed_bits; + + notit->cur_dscope_field = dscope_field; + BT_LOGV("Starting BFCR: notit-addr=%p, bfcr-addr=%p, fc-addr=%p", + notit, notit->bfcr, dscope_fc); + consumed_bits = bt_bfcr_start(notit->bfcr, dscope_fc, + notit->buf.addr, notit->buf.at, packet_at(notit), + notit->buf.sz, &bfcr_status); + BT_LOGV("BFCR consumed bits: size=%zu", consumed_bits); + + switch (bfcr_status) { + case BT_BFCR_STATUS_OK: + /* Field class was read completely */ + BT_LOGV_STR("Field was completely decoded."); + notit->state = done_state; + break; + case BT_BFCR_STATUS_EOF: + BT_LOGV_STR("BFCR needs more data to decode field completely."); + notit->state = continue_state; + break; + default: + BT_LOGW("BFCR failed to start: notit-addr=%p, bfcr-addr=%p, " + "status=%s", notit, notit->bfcr, + bt_bfcr_status_string(bfcr_status)); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + /* Consume bits now since we know we're not in an error state */ + buf_consume_bits(notit, consumed_bits); + +end: + return status; +} + +static +enum bt_msg_iter_status read_dscope_continue_state( + struct bt_msg_iter *notit, enum state done_state) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + enum bt_bfcr_status bfcr_status; + size_t consumed_bits; + + BT_LOGV("Continuing BFCR: notit-addr=%p, bfcr-addr=%p", + notit, notit->bfcr); + + status = buf_ensure_available_bits(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + if (status < 0) { + BT_LOGW("Cannot ensure that buffer has at least one byte: " + "msg-addr=%p, status=%s", + notit, bt_msg_iter_status_string(status)); + } else { + BT_LOGV("Cannot ensure that buffer has at least one byte: " + "msg-addr=%p, status=%s", + notit, bt_msg_iter_status_string(status)); + } + + goto end; + } + + consumed_bits = bt_bfcr_continue(notit->bfcr, notit->buf.addr, + notit->buf.sz, &bfcr_status); + BT_LOGV("BFCR consumed bits: size=%zu", consumed_bits); + + switch (bfcr_status) { + case BT_BFCR_STATUS_OK: + /* Type was read completely. */ + BT_LOGV_STR("Field was completely decoded."); + notit->state = done_state; + break; + case BT_BFCR_STATUS_EOF: + /* Stay in this continue state. */ + BT_LOGV_STR("BFCR needs more data to decode field completely."); + break; + default: + BT_LOGW("BFCR failed to continue: notit-addr=%p, bfcr-addr=%p, " + "status=%s", notit, notit->bfcr, + bt_bfcr_status_string(bfcr_status)); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + /* Consume bits now since we know we're not in an error state. */ + buf_consume_bits(notit, consumed_bits); +end: + return status; +} + +static +void release_event_dscopes(struct bt_msg_iter *notit) +{ + notit->dscopes.event_common_context = NULL; + notit->dscopes.event_spec_context = NULL; + notit->dscopes.event_payload = NULL; +} + +static +void release_all_dscopes(struct bt_msg_iter *notit) +{ + notit->dscopes.stream_packet_context = NULL; + + if (notit->packet_context_field) { + bt_packet_context_field_release(notit->packet_context_field); + notit->packet_context_field = NULL; + } + + release_event_dscopes(notit); +} + +static +enum bt_msg_iter_status read_packet_header_begin_state( + struct bt_msg_iter *notit) +{ + struct ctf_field_class *packet_header_fc = NULL; + enum bt_msg_iter_status ret = BT_MSG_ITER_STATUS_OK; + + if (bt_msg_iter_switch_packet(notit)) { + BT_LOGW("Cannot switch packet: notit-addr=%p", notit); + ret = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + /* + * Make sure at least one bit is available for this packet. An + * empty packet is impossible. If we reach the end of the medium + * at this point, then it's considered the end of the stream. + */ + ret = buf_ensure_available_bits(notit); + switch (ret) { + case BT_MSG_ITER_STATUS_OK: + break; + case BT_MSG_ITER_STATUS_EOF: + ret = BT_MSG_ITER_STATUS_OK; + notit->state = STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END; + goto end; + default: + goto end; + } + + /* Packet header class is common to the whole trace class. */ + packet_header_fc = notit->meta.tc->packet_header_fc; + if (!packet_header_fc) { + notit->state = STATE_AFTER_TRACE_PACKET_HEADER; + goto end; + } + + notit->cur_stream_class_id = -1; + notit->cur_event_class_id = -1; + notit->cur_data_stream_id = -1; + BT_LOGV("Decoding packet header field:" + "notit-addr=%p, trace-class-addr=%p, fc-addr=%p", + notit, notit->meta.tc, packet_header_fc); + ret = read_dscope_begin_state(notit, packet_header_fc, + STATE_AFTER_TRACE_PACKET_HEADER, + STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL); + if (ret < 0) { + BT_LOGW("Cannot decode packet header field: " + "notit-addr=%p, trace-class-addr=%p, " + "fc-addr=%p", + notit, notit->meta.tc, packet_header_fc); + } + +end: + return ret; +} + +static +enum bt_msg_iter_status read_packet_header_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_TRACE_PACKET_HEADER); +} + +static inline +enum bt_msg_iter_status set_current_stream_class(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_stream_class *new_stream_class = NULL; + + if (notit->cur_stream_class_id == -1) { + /* + * No current stream class ID field, therefore only one + * stream class. + */ + if (notit->meta.tc->stream_classes->len != 1) { + BT_LOGW("Need exactly one stream class since there's " + "no stream class ID field: " + "notit-addr=%p", notit); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + new_stream_class = notit->meta.tc->stream_classes->pdata[0]; + notit->cur_stream_class_id = new_stream_class->id; + } + + new_stream_class = ctf_trace_class_borrow_stream_class_by_id( + notit->meta.tc, notit->cur_stream_class_id); + if (!new_stream_class) { + BT_LOGW("No stream class with ID of stream class ID to use in trace class: " + "notit-addr=%p, stream-class-id=%" PRIu64 ", " + "trace-class-addr=%p", + notit, notit->cur_stream_class_id, notit->meta.tc); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + if (notit->meta.sc) { + if (new_stream_class != notit->meta.sc) { + BT_LOGW("Two packets refer to two different stream classes within the same packet sequence: " + "notit-addr=%p, prev-stream-class-addr=%p, " + "prev-stream-class-id=%" PRId64 ", " + "next-stream-class-addr=%p, " + "next-stream-class-id=%" PRId64 ", " + "trace-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + new_stream_class, + new_stream_class->id, + notit->meta.tc); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + } else { + notit->meta.sc = new_stream_class; + } + + BT_LOGV("Set current stream class: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64, + notit, notit->meta.sc, notit->meta.sc->id); + +end: + return status; +} + +static inline +enum bt_msg_iter_status set_current_stream(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + bt_stream *stream = NULL; + + BT_LOGV("Calling user function (get stream): notit-addr=%p, " + "stream-class-addr=%p, stream-class-id=%" PRId64, + notit, notit->meta.sc, + notit->meta.sc->id); + stream = notit->medium.medops.borrow_stream( + notit->meta.sc->ir_sc, notit->cur_data_stream_id, + notit->medium.data); + bt_stream_get_ref(stream); + BT_LOGV("User function returned: stream-addr=%p", stream); + if (!stream) { + BT_LOGW_STR("User function failed to return a stream object " + "for the given stream class."); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + if (notit->stream && stream != notit->stream) { + BT_LOGW("User function returned a different stream than the " + "previous one for the same sequence of packets."); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + BT_STREAM_MOVE_REF(notit->stream, stream); + +end: + bt_stream_put_ref(stream); + return status; +} + +static inline +enum bt_msg_iter_status set_current_packet(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + bt_packet *packet = NULL; + + BT_LOGV("Creating packet for packet message: " + "notit-addr=%p", notit); + BT_LOGV("Creating packet from stream: " + "notit-addr=%p, stream-addr=%p, " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64, + notit, notit->stream, notit->meta.sc, + notit->meta.sc->id); + + /* Create packet */ + BT_ASSERT(notit->stream); + packet = bt_packet_create(notit->stream); + if (!packet) { + BT_LOGE("Cannot create packet from stream: " + "notit-addr=%p, stream-addr=%p, " + "stream-class-addr=%p, " + "stream-class-id=%" PRId64, + notit, notit->stream, notit->meta.sc, + notit->meta.sc->id); + goto error; + } + + goto end; + +error: + BT_PACKET_PUT_REF_AND_RESET(packet); + status = BT_MSG_ITER_STATUS_ERROR; + +end: + BT_PACKET_MOVE_REF(notit->packet, packet); + return status; +} + +static +enum bt_msg_iter_status after_packet_header_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status; + + status = set_current_stream_class(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + notit->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN; + +end: + return status; +} + +static +enum bt_msg_iter_status read_packet_context_begin_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_field_class *packet_context_fc; + + BT_ASSERT(notit->meta.sc); + packet_context_fc = notit->meta.sc->packet_context_fc; + if (!packet_context_fc) { + BT_LOGV("No packet packet context field class in stream class: continuing: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64, + notit, notit->meta.sc, + notit->meta.sc->id); + notit->state = STATE_AFTER_STREAM_PACKET_CONTEXT; + goto end; + } + + BT_ASSERT(!notit->packet_context_field); + + if (packet_context_fc->in_ir) { + /* + * Create free packet context field from stream class. + * This field is going to be moved to the packet once we + * create it. We cannot create the packet now because a + * packet is created from a stream, and this API must be + * able to return the packet context properties without + * creating a stream + * (bt_msg_iter_get_packet_properties()). + */ + notit->packet_context_field = + bt_packet_context_field_create( + notit->meta.sc->ir_sc); + if (!notit->packet_context_field) { + BT_LOGE_STR("Cannot create packet context field wrapper from stream class."); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + notit->dscopes.stream_packet_context = + bt_packet_context_field_borrow_field( + notit->packet_context_field); + BT_ASSERT(notit->dscopes.stream_packet_context); + } + + BT_LOGV("Decoding packet context field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, packet_context_fc); + status = read_dscope_begin_state(notit, packet_context_fc, + STATE_AFTER_STREAM_PACKET_CONTEXT, + STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, + notit->dscopes.stream_packet_context); + if (status < 0) { + BT_LOGW("Cannot decode packet context field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + packet_context_fc); + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_packet_context_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_STREAM_PACKET_CONTEXT); +} + +static +enum bt_msg_iter_status set_current_packet_content_sizes( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + if (notit->cur_exp_packet_total_size == -1) { + if (notit->cur_exp_packet_content_size != -1) { + notit->cur_exp_packet_total_size = + notit->cur_exp_packet_content_size; + } + } else { + if (notit->cur_exp_packet_content_size == -1) { + notit->cur_exp_packet_content_size = + notit->cur_exp_packet_total_size; + } + } + + BT_ASSERT((notit->cur_exp_packet_total_size >= 0 && + notit->cur_exp_packet_content_size >= 0) || + (notit->cur_exp_packet_total_size < 0 && + notit->cur_exp_packet_content_size < 0)); + + if (notit->cur_exp_packet_content_size > + notit->cur_exp_packet_total_size) { + BT_LOGW("Invalid packet or content size: " + "content size is greater than packet size: " + "notit-addr=%p, packet-context-field-addr=%p, " + "packet-size=%" PRId64 ", content-size=%" PRId64, + notit, notit->dscopes.stream_packet_context, + notit->cur_exp_packet_total_size, + notit->cur_exp_packet_content_size); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + BT_LOGV("Set current packet and content sizes: " + "notit-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64, + notit, notit->cur_exp_packet_total_size, + notit->cur_exp_packet_content_size); + +end: + return status; +} + +static +enum bt_msg_iter_status after_packet_context_state(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status; + + status = set_current_packet_content_sizes(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + if (notit->stream) { + /* + * Stream exists, which means we already emitted at + * least one packet beginning message, so the initial + * stream beginning message was also emitted. + */ + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; + } else { + notit->state = STATE_CHECK_EMIT_MSG_STREAM_BEGINNING; + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_header_begin_state(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_field_class *event_header_fc = NULL; + + /* Reset the position of the last event header */ + notit->buf.last_eh_at = notit->buf.at; + notit->cur_event_class_id = -1; + + /* Check if we have some content left */ + if (notit->cur_exp_packet_content_size >= 0) { + if (unlikely(packet_at(notit) == + notit->cur_exp_packet_content_size)) { + /* No more events! */ + BT_LOGV("Reached end of packet: notit-addr=%p, " + "cur=%zu", notit, packet_at(notit)); + notit->state = STATE_EMIT_MSG_PACKET_END_MULTI; + goto end; + } else if (unlikely(packet_at(notit) > + notit->cur_exp_packet_content_size)) { + /* That's not supposed to happen */ + BT_LOGV("Before decoding event header field: cursor is passed the packet's content: " + "notit-addr=%p, content-size=%" PRId64 ", " + "cur=%zu", notit, + notit->cur_exp_packet_content_size, + packet_at(notit)); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + } else { + /* + * "Infinite" content: we're done when the medium has + * nothing else for us. + */ + status = buf_ensure_available_bits(notit); + switch (status) { + case BT_MSG_ITER_STATUS_OK: + break; + case BT_MSG_ITER_STATUS_EOF: + status = BT_MSG_ITER_STATUS_OK; + notit->state = STATE_EMIT_MSG_PACKET_END_SINGLE; + goto end; + default: + goto end; + } + } + + release_event_dscopes(notit); + BT_ASSERT(notit->meta.sc); + event_header_fc = notit->meta.sc->event_header_fc; + if (!event_header_fc) { + notit->state = STATE_AFTER_EVENT_HEADER; + goto end; + } + + BT_LOGV("Decoding event header field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + event_header_fc); + status = read_dscope_begin_state(notit, event_header_fc, + STATE_AFTER_EVENT_HEADER, + STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL); + if (status < 0) { + BT_LOGW("Cannot decode event header field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + event_header_fc); + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_header_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_EVENT_HEADER); +} + +static inline +enum bt_msg_iter_status set_current_event_class(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + struct ctf_event_class *new_event_class = NULL; + + if (notit->cur_event_class_id == -1) { + /* + * No current event class ID field, therefore only one + * event class. + */ + if (notit->meta.sc->event_classes->len != 1) { + BT_LOGW("Need exactly one event class since there's " + "no event class ID field: " + "notit-addr=%p", notit); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + new_event_class = notit->meta.sc->event_classes->pdata[0]; + notit->cur_event_class_id = new_event_class->id; + } + + new_event_class = ctf_stream_class_borrow_event_class_by_id( + notit->meta.sc, notit->cur_event_class_id); + if (!new_event_class) { + BT_LOGW("No event class with ID of event class ID to use in stream class: " + "notit-addr=%p, stream-class-id=%" PRIu64 ", " + "event-class-id=%" PRIu64 ", " + "trace-class-addr=%p", + notit, notit->meta.sc->id, notit->cur_event_class_id, + notit->meta.tc); + status = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + + notit->meta.ec = new_event_class; + BT_LOGV("Set current event class: " + "notit-addr=%p, event-class-addr=%p, " + "event-class-id=%" PRId64 ", " + "event-class-name=\"%s\"", + notit, notit->meta.ec, notit->meta.ec->id, + notit->meta.ec->name->str); + +end: + return status; +} + +static inline +enum bt_msg_iter_status set_current_event_message( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + bt_message *msg = NULL; + + BT_ASSERT(notit->meta.ec); + BT_ASSERT(notit->packet); + BT_LOGV("Creating event message from event class and packet: " + "notit-addr=%p, ec-addr=%p, ec-name=\"%s\", packet-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->packet); + BT_ASSERT(notit->msg_iter); + BT_ASSERT(notit->meta.sc); + + if (bt_stream_class_borrow_default_clock_class(notit->meta.sc->ir_sc)) { + msg = bt_message_event_create_with_default_clock_snapshot( + notit->msg_iter, notit->meta.ec->ir_ec, + notit->packet, notit->default_clock_snapshot); + } else { + msg = bt_message_event_create(notit->msg_iter, + notit->meta.ec->ir_ec, notit->packet); + } + + if (!msg) { + BT_LOGE("Cannot create event message: " + "notit-addr=%p, ec-addr=%p, ec-name=\"%s\", " + "packet-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->packet); + goto error; + } + + goto end; + +error: + BT_MESSAGE_PUT_REF_AND_RESET(msg); + status = BT_MSG_ITER_STATUS_ERROR; + +end: + BT_MESSAGE_MOVE_REF(notit->event_msg, msg); + return status; +} + +static +enum bt_msg_iter_status after_event_header_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status; + + status = set_current_event_class(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + status = set_current_event_message(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + notit->event = bt_message_event_borrow_event( + notit->event_msg); + BT_ASSERT(notit->event); + notit->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN; + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_common_context_begin_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_field_class *event_common_context_fc; + + event_common_context_fc = notit->meta.sc->event_common_context_fc; + if (!event_common_context_fc) { + notit->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN; + goto end; + } + + if (event_common_context_fc->in_ir) { + BT_ASSERT(!notit->dscopes.event_common_context); + notit->dscopes.event_common_context = + bt_event_borrow_common_context_field( + notit->event); + BT_ASSERT(notit->dscopes.event_common_context); + } + + BT_LOGV("Decoding event common context field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", " + "fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + event_common_context_fc); + status = read_dscope_begin_state(notit, event_common_context_fc, + STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN, + STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE, + notit->dscopes.event_common_context); + if (status < 0) { + BT_LOGW("Cannot decode event common context field: " + "notit-addr=%p, stream-class-addr=%p, " + "stream-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.sc, + notit->meta.sc->id, + event_common_context_fc); + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_common_context_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN); +} + +static +enum bt_msg_iter_status read_event_spec_context_begin_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_field_class *event_spec_context_fc; + + event_spec_context_fc = notit->meta.ec->spec_context_fc; + if (!event_spec_context_fc) { + notit->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN; + goto end; + } + + if (event_spec_context_fc->in_ir) { + BT_ASSERT(!notit->dscopes.event_spec_context); + notit->dscopes.event_spec_context = + bt_event_borrow_specific_context_field( + notit->event); + BT_ASSERT(notit->dscopes.event_spec_context); + } + + BT_LOGV("Decoding event specific context field: " + "notit-addr=%p, event-class-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "fc-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->meta.ec->id, + event_spec_context_fc); + status = read_dscope_begin_state(notit, event_spec_context_fc, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, + STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE, + notit->dscopes.event_spec_context); + if (status < 0) { + BT_LOGW("Cannot decode event specific context field: " + "notit-addr=%p, event-class-addr=%p, " + "event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->meta.ec->id, + event_spec_context_fc); + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_spec_context_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN); +} + +static +enum bt_msg_iter_status read_event_payload_begin_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + struct ctf_field_class *event_payload_fc; + + event_payload_fc = notit->meta.ec->payload_fc; + if (!event_payload_fc) { + notit->state = STATE_EMIT_MSG_EVENT; + goto end; + } + + if (event_payload_fc->in_ir) { + BT_ASSERT(!notit->dscopes.event_payload); + notit->dscopes.event_payload = + bt_event_borrow_payload_field( + notit->event); + BT_ASSERT(notit->dscopes.event_payload); + } + + BT_LOGV("Decoding event payload field: " + "notit-addr=%p, event-class-addr=%p, " + "event-class-name=\"%s\", event-class-id=%" PRId64 ", " + "fc-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->meta.ec->id, + event_payload_fc); + status = read_dscope_begin_state(notit, event_payload_fc, + STATE_EMIT_MSG_EVENT, + STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, + notit->dscopes.event_payload); + if (status < 0) { + BT_LOGW("Cannot decode event payload field: " + "notit-addr=%p, event-class-addr=%p, " + "event-class-name=\"%s\", " + "event-class-id=%" PRId64 ", fc-addr=%p", + notit, notit->meta.ec, + notit->meta.ec->name->str, + notit->meta.ec->id, + event_payload_fc); + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_event_payload_continue_state( + struct bt_msg_iter *notit) +{ + return read_dscope_continue_state(notit, STATE_EMIT_MSG_EVENT); +} + +static +enum bt_msg_iter_status skip_packet_padding_state(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + size_t bits_to_skip; + + BT_ASSERT(notit->cur_exp_packet_total_size > 0); + bits_to_skip = notit->cur_exp_packet_total_size - packet_at(notit); + if (bits_to_skip == 0) { + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + goto end; + } else { + size_t bits_to_consume; + + BT_LOGV("Trying to skip %zu bits of padding: notit-addr=%p, size=%zu", + bits_to_skip, notit, bits_to_skip); + status = buf_ensure_available_bits(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + bits_to_consume = MIN(buf_available_bits(notit), bits_to_skip); + BT_LOGV("Skipping %zu bits of padding: notit-addr=%p, size=%zu", + bits_to_consume, notit, bits_to_consume); + buf_consume_bits(notit, bits_to_consume); + bits_to_skip = notit->cur_exp_packet_total_size - + packet_at(notit); + if (bits_to_skip == 0) { + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + goto end; + } + } + +end: + return status; +} + +static +enum bt_msg_iter_status check_emit_msg_stream_beginning_state( + struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + if (notit->set_stream) { + status = set_current_stream(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + } + + if (notit->emit_stream_begin_msg) { + notit->state = STATE_EMIT_MSG_STREAM_BEGINNING; + } else { + /* Stream's first packet */ + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; + } + +end: + return status; +} + +static +enum bt_msg_iter_status check_emit_msg_discarded_events( + struct bt_msg_iter *notit) +{ + notit->state = STATE_EMIT_MSG_DISCARDED_EVENTS; + + if (!notit->meta.sc->has_discarded_events) { + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; + goto end; + } + + if (notit->prev_packet_snapshots.discarded_events == UINT64_C(-1)) { + if (notit->snapshots.discarded_events == 0 || + notit->snapshots.discarded_events == UINT64_C(-1)) { + /* + * Stream's first packet with no discarded + * events or no information about discarded + * events: do not emit. + */ + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; + } + } else { + /* + * If the previous packet has a value for this counter, + * then this counter is defined for the whole stream. + */ + BT_ASSERT(notit->snapshots.discarded_events != UINT64_C(-1)); + + if (notit->snapshots.discarded_events - + notit->prev_packet_snapshots.discarded_events == 0) { + /* + * No discarded events since previous packet: do + * not emit. + */ + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; + } + } + +end: + return BT_MSG_ITER_STATUS_OK; +} + +static +enum bt_msg_iter_status check_emit_msg_discarded_packets( + struct bt_msg_iter *notit) +{ + notit->state = STATE_EMIT_MSG_DISCARDED_PACKETS; + + if (!notit->meta.sc->has_discarded_packets) { + notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; + goto end; + } + + if (notit->prev_packet_snapshots.packets == UINT64_C(-1)) { + /* + * Stream's first packet or no information about + * discarded packets: do not emit. In other words, if + * this is the first packet and its sequence number is + * not 0, do not consider that packets were previously + * lost: we might be reading a partial stream (LTTng + * snapshot for example). + */ + notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; + } else { + /* + * If the previous packet has a value for this counter, + * then this counter is defined for the whole stream. + */ + BT_ASSERT(notit->snapshots.packets != UINT64_C(-1)); + + if (notit->snapshots.packets - + notit->prev_packet_snapshots.packets <= 1) { + /* + * No discarded packets since previous packet: + * do not emit. + */ + notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; + } + } + +end: + return BT_MSG_ITER_STATUS_OK; +} + +static +enum bt_msg_iter_status check_emit_msg_stream_activity_end( + struct bt_msg_iter *notit) +{ + if (notit->emit_stream_end_msg) { + notit->state = STATE_EMIT_MSG_STREAM_ACTIVITY_END; + } else { + notit->state = STATE_DONE; + } + + return BT_MSG_ITER_STATUS_OK; +} + +static inline +enum bt_msg_iter_status handle_state(struct bt_msg_iter *notit) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + const enum state state = notit->state; + + BT_LOGV("Handling state: notit-addr=%p, state=%s", + notit, state_string(state)); + + // TODO: optimalize! + switch (state) { + case STATE_INIT: + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + break; + case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: + status = read_packet_header_begin_state(notit); + break; + case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: + status = read_packet_header_continue_state(notit); + break; + case STATE_AFTER_TRACE_PACKET_HEADER: + status = after_packet_header_state(notit); + break; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: + status = read_packet_context_begin_state(notit); + break; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: + status = read_packet_context_continue_state(notit); + break; + case STATE_AFTER_STREAM_PACKET_CONTEXT: + status = after_packet_context_state(notit); + break; + case STATE_CHECK_EMIT_MSG_STREAM_BEGINNING: + status = check_emit_msg_stream_beginning_state(notit); + break; + case STATE_EMIT_MSG_STREAM_BEGINNING: + notit->state = STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING; + break; + case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS; + break; + case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS: + status = check_emit_msg_discarded_events(notit); + break; + case STATE_EMIT_MSG_DISCARDED_EVENTS: + notit->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS; + break; + case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS: + status = check_emit_msg_discarded_packets(notit); + break; + case STATE_EMIT_MSG_DISCARDED_PACKETS: + notit->state = STATE_EMIT_MSG_PACKET_BEGINNING; + break; + case STATE_EMIT_MSG_PACKET_BEGINNING: + notit->state = STATE_DSCOPE_EVENT_HEADER_BEGIN; + break; + case STATE_DSCOPE_EVENT_HEADER_BEGIN: + status = read_event_header_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_HEADER_CONTINUE: + status = read_event_header_continue_state(notit); + break; + case STATE_AFTER_EVENT_HEADER: + status = after_event_header_state(notit); + break; + case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN: + status = read_event_common_context_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE: + status = read_event_common_context_continue_state(notit); + break; + case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN: + status = read_event_spec_context_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE: + status = read_event_spec_context_continue_state(notit); + break; + case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: + status = read_event_payload_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: + status = read_event_payload_continue_state(notit); + break; + case STATE_EMIT_MSG_EVENT: + notit->state = STATE_DSCOPE_EVENT_HEADER_BEGIN; + break; + case STATE_SKIP_PACKET_PADDING: + status = skip_packet_padding_state(notit); + break; + case STATE_EMIT_MSG_PACKET_END_MULTI: + notit->state = STATE_SKIP_PACKET_PADDING; + break; + case STATE_EMIT_MSG_PACKET_END_SINGLE: + notit->state = STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END; + break; + case STATE_CHECK_EMIT_MSG_STREAM_ACTIVITY_END: + status = check_emit_msg_stream_activity_end(notit); + break; + case STATE_EMIT_MSG_STREAM_ACTIVITY_END: + notit->state = STATE_EMIT_MSG_STREAM_END; + break; + case STATE_EMIT_MSG_STREAM_END: + notit->state = STATE_DONE; + break; + case STATE_DONE: + break; + default: + BT_LOGD("Unknown CTF plugin message iterator state: " + "notit-addr=%p, state=%d", notit, notit->state); + abort(); + } + + BT_LOGV("Handled state: notit-addr=%p, status=%s, " + "prev-state=%s, cur-state=%s", + notit, bt_msg_iter_status_string(status), + state_string(state), state_string(notit->state)); + return status; +} + +BT_HIDDEN +void bt_msg_iter_reset_for_next_stream_file(struct bt_msg_iter *notit) +{ + BT_ASSERT(notit); + BT_LOGD("Resetting message iterator: addr=%p", notit); + stack_clear(notit->stack); + notit->meta.sc = NULL; + notit->meta.ec = NULL; + BT_PACKET_PUT_REF_AND_RESET(notit->packet); + BT_STREAM_PUT_REF_AND_RESET(notit->stream); + BT_MESSAGE_PUT_REF_AND_RESET(notit->event_msg); + release_all_dscopes(notit); + notit->cur_dscope_field = NULL; + + if (notit->packet_context_field) { + bt_packet_context_field_release(notit->packet_context_field); + notit->packet_context_field = NULL; + } + + notit->buf.addr = NULL; + notit->buf.sz = 0; + notit->buf.at = 0; + notit->buf.last_eh_at = SIZE_MAX; + notit->buf.packet_offset = 0; + notit->state = STATE_INIT; + notit->cur_exp_packet_content_size = -1; + notit->cur_exp_packet_total_size = -1; + notit->cur_packet_offset = -1; + notit->cur_event_class_id = -1; + notit->snapshots.beginning_clock = UINT64_C(-1); + notit->snapshots.end_clock = UINT64_C(-1); +} + +/** + * Resets the internal state of a CTF message iterator. + */ +BT_HIDDEN +void bt_msg_iter_reset(struct bt_msg_iter *notit) +{ + bt_msg_iter_reset_for_next_stream_file(notit); + notit->cur_stream_class_id = -1; + notit->cur_data_stream_id = -1; + notit->emit_stream_begin_msg = true; + notit->emit_stream_end_msg = true; + notit->snapshots.discarded_events = UINT64_C(-1); + notit->snapshots.packets = UINT64_C(-1); + notit->prev_packet_snapshots.discarded_events = UINT64_C(-1); + notit->prev_packet_snapshots.packets = UINT64_C(-1); + notit->prev_packet_snapshots.beginning_clock = UINT64_C(-1); + notit->prev_packet_snapshots.end_clock = UINT64_C(-1); +} + +static +int bt_msg_iter_switch_packet(struct bt_msg_iter *notit) +{ + int ret = 0; + + /* + * We don't put the stream class here because we need to make + * sure that all the packets processed by the same message + * iterator refer to the same stream class (the first one). + */ + BT_ASSERT(notit); + + if (notit->cur_exp_packet_total_size != -1) { + notit->cur_packet_offset += notit->cur_exp_packet_total_size; + } + + BT_LOGV("Switching packet: notit-addr=%p, cur=%zu, " + "packet-offset=%" PRId64, notit, notit->buf.at, + notit->cur_packet_offset); + stack_clear(notit->stack); + notit->meta.ec = NULL; + BT_PACKET_PUT_REF_AND_RESET(notit->packet); + BT_MESSAGE_PUT_REF_AND_RESET(notit->event_msg); + release_all_dscopes(notit); + notit->cur_dscope_field = NULL; + + /* + * Adjust current buffer so that addr points to the beginning of the new + * packet. + */ + if (notit->buf.addr) { + size_t consumed_bytes = (size_t) (notit->buf.at / CHAR_BIT); + + /* Packets are assumed to start on a byte frontier. */ + if (notit->buf.at % CHAR_BIT) { + BT_LOGW("Cannot switch packet: current position is not a multiple of 8: " + "notit-addr=%p, cur=%zu", notit, notit->buf.at); + ret = -1; + goto end; + } + + notit->buf.addr += consumed_bytes; + notit->buf.sz -= consumed_bytes; + notit->buf.at = 0; + notit->buf.packet_offset = 0; + BT_LOGV("Adjusted buffer: addr=%p, size=%zu", + notit->buf.addr, notit->buf.sz); + } + + notit->cur_exp_packet_content_size = -1; + notit->cur_exp_packet_total_size = -1; + notit->cur_stream_class_id = -1; + notit->cur_event_class_id = -1; + notit->cur_data_stream_id = -1; + notit->prev_packet_snapshots = notit->snapshots; + notit->snapshots.discarded_events = UINT64_C(-1); + notit->snapshots.packets = UINT64_C(-1); + notit->snapshots.beginning_clock = UINT64_C(-1); + notit->snapshots.end_clock = UINT64_C(-1); + +end: + return ret; +} + +static +bt_field *borrow_next_field(struct bt_msg_iter *notit) +{ + bt_field *next_field = NULL; + bt_field *base_field; + const bt_field_class *base_fc; + size_t index; + + BT_ASSERT(!stack_empty(notit->stack)); + index = stack_top(notit->stack)->index; + base_field = stack_top(notit->stack)->base; + BT_ASSERT(base_field); + base_fc = bt_field_borrow_class_const(base_field); + BT_ASSERT(base_fc); + + switch (bt_field_class_get_type(base_fc)) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + { + BT_ASSERT(index < + bt_field_class_structure_get_member_count( + bt_field_borrow_class_const( + base_field))); + next_field = + bt_field_structure_borrow_member_field_by_index( + base_field, index); + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + BT_ASSERT(index < bt_field_array_get_length(base_field)); + next_field = bt_field_array_borrow_element_field_by_index( + base_field, index); + break; + case BT_FIELD_CLASS_TYPE_VARIANT: + BT_ASSERT(index == 0); + next_field = bt_field_variant_borrow_selected_option_field( + base_field); + break; + default: + abort(); + } + + BT_ASSERT(next_field); + return next_field; +} + +static +void update_default_clock(struct bt_msg_iter *notit, uint64_t new_val, + uint64_t new_val_size) +{ + uint64_t new_val_mask; + uint64_t cur_value_masked; + + BT_ASSERT(new_val_size > 0); + + /* + * Special case for a 64-bit new value, which is the limit + * of a clock value as of this version: overwrite the + * current value directly. + */ + if (new_val_size == 64) { + notit->default_clock_snapshot = new_val; + goto end; + } + + new_val_mask = (1ULL << new_val_size) - 1; + cur_value_masked = notit->default_clock_snapshot & new_val_mask; + + if (new_val < cur_value_masked) { + /* + * It looks like a wrap happened on the number of bits + * of the requested new value. Assume that the clock + * value wrapped only one time. + */ + notit->default_clock_snapshot += new_val_mask + 1; + } + + /* Clear the low bits of the current clock value. */ + notit->default_clock_snapshot &= ~new_val_mask; + + /* Set the low bits of the current clock value. */ + notit->default_clock_snapshot |= new_val; + +end: + BT_LOGV("Updated default clock's value from integer field's value: " + "value=%" PRIu64, notit->default_clock_snapshot); +} + +static +enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value, + struct ctf_field_class *fc, void *data) +{ + struct bt_msg_iter *notit = data; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + bt_field *field = NULL; + struct ctf_field_class_int *int_fc = (void *) fc; + + BT_LOGV("Unsigned integer function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d, value=%" PRIu64, + notit, notit->bfcr, fc, fc->type, fc->in_ir, value); + + if (likely(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) { + goto update_def_clock; + } + + switch (int_fc->meaning) { + case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID: + notit->cur_event_class_id = value; + break; + case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID: + notit->cur_data_stream_id = value; + break; + case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME: + notit->snapshots.beginning_clock = value; + break; + case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME: + notit->snapshots.end_clock = value; + break; + case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID: + notit->cur_stream_class_id = value; + break; + case CTF_FIELD_CLASS_MEANING_MAGIC: + if (value != 0xc1fc1fc1) { + BT_LOGW("Invalid CTF magic number: notit-addr=%p, " + "magic=%" PRIx64, notit, value); + status = BT_BFCR_STATUS_ERROR; + goto end; + } + + break; + case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT: + notit->snapshots.packets = value; + break; + case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT: + notit->snapshots.discarded_events = value; + break; + case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE: + notit->cur_exp_packet_total_size = value; + break; + case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE: + notit->cur_exp_packet_content_size = value; + break; + default: + abort(); + } + +update_def_clock: + if (unlikely(int_fc->mapped_clock_class)) { + update_default_clock(notit, value, int_fc->base.size); + } + + if (unlikely(int_fc->storing_index >= 0)) { + g_array_index(notit->stored_values, uint64_t, + (uint64_t) int_fc->storing_index) = value; + } + + if (unlikely(!fc->in_ir)) { + goto end; + } + + field = borrow_next_field(notit); + BT_ASSERT(field); + BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); + BT_ASSERT(bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || + bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION); + bt_field_unsigned_integer_set_value(field, value); + stack_top(notit->stack)->index++; + +end: + return status; +} + +static +enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value, + struct ctf_field_class *fc, void *data) +{ + int ret; + struct bt_msg_iter *notit = data; + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + bt_field *string_field = NULL; + struct ctf_field_class_int *int_fc = (void *) fc; + char str[2] = {'\0', '\0'}; + + BT_LOGV("Unsigned integer character function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d, value=%" PRIu64, + notit, notit->bfcr, fc, fc->type, fc->in_ir, value); + BT_ASSERT(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE); + BT_ASSERT(!int_fc->mapped_clock_class); + BT_ASSERT(int_fc->storing_index < 0); + + if (unlikely(!fc->in_ir)) { + goto end; + } + + if (notit->done_filling_string) { + goto end; + } + + if (value == 0) { + notit->done_filling_string = true; + goto end; + } + + string_field = stack_top(notit->stack)->base; + BT_ASSERT(bt_field_get_class_type(string_field) == + BT_FIELD_CLASS_TYPE_STRING); + + /* Append character */ + str[0] = (char) value; + ret = bt_field_string_append_with_length(string_field, str, 1); + if (ret) { + BT_LOGE("Cannot append character to string field's value: " + "notit-addr=%p, field-addr=%p, ret=%d", + notit, string_field, ret); + status = BT_BFCR_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static +enum bt_bfcr_status bfcr_signed_int_cb(int64_t value, + struct ctf_field_class *fc, void *data) +{ + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + bt_field *field = NULL; + struct bt_msg_iter *notit = data; + struct ctf_field_class_int *int_fc = (void *) fc; + + BT_LOGV("Signed integer function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d, value=%" PRId64, + notit, notit->bfcr, fc, fc->type, fc->in_ir, value); + BT_ASSERT(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE); + + if (unlikely(int_fc->storing_index >= 0)) { + g_array_index(notit->stored_values, uint64_t, + (uint64_t) int_fc->storing_index) = (uint64_t) value; + } + + if (unlikely(!fc->in_ir)) { + goto end; + } + + field = borrow_next_field(notit); + BT_ASSERT(field); + BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); + BT_ASSERT(bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || + bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); + bt_field_signed_integer_set_value(field, value); + stack_top(notit->stack)->index++; + +end: + return status; +} + +static +enum bt_bfcr_status bfcr_floating_point_cb(double value, + struct ctf_field_class *fc, void *data) +{ + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + bt_field *field = NULL; + struct bt_msg_iter *notit = data; + + BT_LOGV("Floating point number function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d, value=%f", + notit, notit->bfcr, fc, fc->type, fc->in_ir, value); + + if (unlikely(!fc->in_ir)) { + goto end; + } + + field = borrow_next_field(notit); + BT_ASSERT(field); + BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); + BT_ASSERT(bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_REAL); + bt_field_real_set_value(field, value); + stack_top(notit->stack)->index++; + +end: + return status; +} + +static +enum bt_bfcr_status bfcr_string_begin_cb( + struct ctf_field_class *fc, void *data) +{ + bt_field *field = NULL; + struct bt_msg_iter *notit = data; + int ret; + + BT_LOGV("String (beginning) function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d", + notit, notit->bfcr, fc, fc->type, fc->in_ir); + + if (unlikely(!fc->in_ir)) { + goto end; + } + + field = borrow_next_field(notit); + BT_ASSERT(field); + BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); + BT_ASSERT(bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_STRING); + ret = bt_field_string_clear(field); + BT_ASSERT(ret == 0); + + /* + * Push on stack. Not a compound class per se, but we know that + * only bfcr_string_cb() may be called between this call and a + * subsequent call to bfcr_string_end_cb(). + */ + stack_push(notit->stack, field); + +end: + return BT_BFCR_STATUS_OK; +} + +static +enum bt_bfcr_status bfcr_string_cb(const char *value, + size_t len, struct ctf_field_class *fc, void *data) +{ + enum bt_bfcr_status status = BT_BFCR_STATUS_OK; + bt_field *field = NULL; + struct bt_msg_iter *notit = data; + int ret; + + BT_LOGV("String (substring) function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d, string-length=%zu", + notit, notit->bfcr, fc, fc->type, fc->in_ir, + len); + + if (unlikely(!fc->in_ir)) { + goto end; + } + + field = stack_top(notit->stack)->base; + BT_ASSERT(field); + + /* Append current substring */ + ret = bt_field_string_append_with_length(field, value, len); + if (ret) { + BT_LOGE("Cannot append substring to string field's value: " + "notit-addr=%p, field-addr=%p, string-length=%zu, " + "ret=%d", notit, field, len, ret); + status = BT_BFCR_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static +enum bt_bfcr_status bfcr_string_end_cb( + struct ctf_field_class *fc, void *data) +{ + struct bt_msg_iter *notit = data; + + BT_LOGV("String (end) function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d", + notit, notit->bfcr, fc, fc->type, fc->in_ir); + + if (unlikely(!fc->in_ir)) { + goto end; + } + + /* Pop string field */ + stack_pop(notit->stack); + + /* Go to next field */ + stack_top(notit->stack)->index++; + +end: + return BT_BFCR_STATUS_OK; +} + +enum bt_bfcr_status bfcr_compound_begin_cb( + struct ctf_field_class *fc, void *data) +{ + struct bt_msg_iter *notit = data; + bt_field *field; + + BT_LOGV("Compound (beginning) function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d", + notit, notit->bfcr, fc, fc->type, fc->in_ir); + + if (!fc->in_ir) { + goto end; + } + + /* Borrow field */ + if (stack_empty(notit->stack)) { + /* Root: already set by read_dscope_begin_state() */ + field = notit->cur_dscope_field; + } else { + field = borrow_next_field(notit); + BT_ASSERT(field); + } + + /* Push field */ + BT_ASSERT(field); + BT_ASSERT(bt_field_borrow_class_const(field) == fc->ir_fc); + stack_push(notit->stack, field); + + /* + * Change BFCR "unsigned int" callback if it's a text + * array/sequence. + */ + if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || + fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + if (array_fc->is_text) { + int ret; + + BT_ASSERT(bt_field_get_class_type(field) == + BT_FIELD_CLASS_TYPE_STRING); + notit->done_filling_string = false; + ret = bt_field_string_clear(field); + BT_ASSERT(ret == 0); + bt_bfcr_set_unsigned_int_cb(notit->bfcr, + bfcr_unsigned_int_char_cb); + } + } + +end: + return BT_BFCR_STATUS_OK; +} + +enum bt_bfcr_status bfcr_compound_end_cb( + struct ctf_field_class *fc, void *data) +{ + struct bt_msg_iter *notit = data; + + BT_LOGV("Compound (end) function called from BFCR: " + "notit-addr=%p, bfcr-addr=%p, fc-addr=%p, " + "fc-type=%d, fc-in-ir=%d", + notit, notit->bfcr, fc, fc->type, fc->in_ir); + + if (!fc->in_ir) { + goto end; + } + + BT_ASSERT(!stack_empty(notit->stack)); + BT_ASSERT(bt_field_borrow_class_const(stack_top(notit->stack)->base) == + fc->ir_fc); + + /* + * Reset BFCR "unsigned int" callback if it's a text + * array/sequence. + */ + if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || + fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) { + struct ctf_field_class_array_base *array_fc = (void *) fc; + + if (array_fc->is_text) { + BT_ASSERT(bt_field_get_class_type( + stack_top(notit->stack)->base) == + BT_FIELD_CLASS_TYPE_STRING); + bt_bfcr_set_unsigned_int_cb(notit->bfcr, + bfcr_unsigned_int_cb); + } + } + + /* Pop stack */ + stack_pop(notit->stack); + + /* If the stack is not empty, increment the base's index */ + if (!stack_empty(notit->stack)) { + stack_top(notit->stack)->index++; + } + +end: + return BT_BFCR_STATUS_OK; +} + +static +int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data) +{ + bt_field *seq_field; + struct bt_msg_iter *notit = data; + struct ctf_field_class_sequence *seq_fc = (void *) fc; + int64_t length = -1; + int ret; + + length = (uint64_t) g_array_index(notit->stored_values, uint64_t, + seq_fc->stored_length_index); + seq_field = stack_top(notit->stack)->base; + BT_ASSERT(seq_field); + + /* + * bfcr_get_sequence_length_cb() also gets called back for a + * text sequence, but the destination field is a string field. + * Only set the field's sequence length if the destination field + * is a sequence field. + */ + if (!seq_fc->base.is_text) { + BT_ASSERT(bt_field_get_class_type(seq_field) == + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); + ret = bt_field_dynamic_array_set_length(seq_field, + (uint64_t) length); + if (ret) { + BT_LOGE("Cannot set dynamic array field's length field: " + "notit-addr=%p, field-addr=%p, " + "length=%" PRIu64, notit, seq_field, length); + } + } + + return length; +} + +static +struct ctf_field_class *bfcr_borrow_variant_selected_field_class_cb( + struct ctf_field_class *fc, void *data) +{ + int ret; + uint64_t i; + int64_t option_index = -1; + struct bt_msg_iter *notit = data; + struct ctf_field_class_variant *var_fc = (void *) fc; + struct ctf_named_field_class *selected_option = NULL; + struct ctf_field_class *ret_fc = NULL; + union { + uint64_t u; + int64_t i; + } tag; + + /* Get variant's tag */ + tag.u = g_array_index(notit->stored_values, uint64_t, + var_fc->stored_tag_index); + + /* + * Check each range to find the selected option's index. + */ + if (var_fc->tag_fc->base.is_signed) { + for (i = 0; i < var_fc->ranges->len; i++) { + struct ctf_field_class_variant_range *range = + ctf_field_class_variant_borrow_range_by_index( + var_fc, i); + + if (tag.i >= range->range.lower.i && + tag.i <= range->range.upper.i) { + option_index = (int64_t) range->option_index; + break; + } + } + } else { + for (i = 0; i < var_fc->ranges->len; i++) { + struct ctf_field_class_variant_range *range = + ctf_field_class_variant_borrow_range_by_index( + var_fc, i); + + if (tag.u >= range->range.lower.u && + tag.u <= range->range.upper.u) { + option_index = (int64_t) range->option_index; + break; + } + } + } + + if (option_index < 0) { + BT_LOGW("Cannot find variant field class's option: " + "notit-addr=%p, var-fc-addr=%p, u-tag=%" PRIu64 ", " + "i-tag=%" PRId64, notit, var_fc, tag.u, tag.i); + goto end; + } + + selected_option = ctf_field_class_variant_borrow_option_by_index( + var_fc, (uint64_t) option_index); + + if (selected_option->fc->in_ir) { + bt_field *var_field = stack_top(notit->stack)->base; + + ret = bt_field_variant_select_option_field( + var_field, option_index); + if (ret) { + BT_LOGW("Cannot select variant field's option field: " + "notit-addr=%p, var-field-addr=%p, " + "opt-index=%" PRId64, notit, var_field, + option_index); + goto end; + } + } + + ret_fc = selected_option->fc; + +end: + return ret_fc; +} + +static +void create_msg_stream_beginning(struct bt_msg_iter *notit, + bt_message **message) +{ + bt_message *ret = NULL; + + BT_ASSERT(notit->stream); + BT_ASSERT(notit->msg_iter); + ret = bt_message_stream_beginning_create(notit->msg_iter, + notit->stream); + if (!ret) { + BT_LOGE("Cannot create stream beginning message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + *message = ret; +} + +static +void create_msg_stream_activity_beginning(struct bt_msg_iter *notit, + bt_message **message) +{ + bt_message *ret = NULL; + + BT_ASSERT(notit->stream); + BT_ASSERT(notit->msg_iter); + ret = bt_message_stream_activity_beginning_create(notit->msg_iter, + notit->stream); + if (!ret) { + BT_LOGE("Cannot create stream activity beginning message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + *message = ret; +} + +static +void create_msg_stream_activity_end(struct bt_msg_iter *notit, + bt_message **message) +{ + bt_message *ret = NULL; + + if (!notit->stream) { + BT_LOGE("Cannot create stream for stream message: " + "notit-addr=%p", notit); + return; + } + + BT_ASSERT(notit->stream); + BT_ASSERT(notit->msg_iter); + ret = bt_message_stream_activity_end_create(notit->msg_iter, + notit->stream); + if (!ret) { + BT_LOGE("Cannot create stream activity end message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + *message = ret; +} + +static +void create_msg_stream_end(struct bt_msg_iter *notit, bt_message **message) +{ + bt_message *ret; + + if (!notit->stream) { + BT_LOGE("Cannot create stream for stream message: " + "notit-addr=%p", notit); + return; + } + + BT_ASSERT(notit->msg_iter); + ret = bt_message_stream_end_create(notit->msg_iter, + notit->stream); + if (!ret) { + BT_LOGE("Cannot create stream end message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + *message = ret; +} + +static +void create_msg_packet_beginning(struct bt_msg_iter *notit, + bt_message **message) +{ + int ret; + enum bt_msg_iter_status status; + bt_message *msg = NULL; + const bt_stream_class *sc; + + status = set_current_packet(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + BT_ASSERT(notit->packet); + sc = notit->meta.sc->ir_sc; + BT_ASSERT(sc); + + if (notit->packet_context_field) { + ret = bt_packet_move_context_field( + notit->packet, notit->packet_context_field); + if (ret) { + goto end; + } + + notit->packet_context_field = NULL; + + /* + * At this point notit->dscopes.stream_packet_context + * has the same value as the packet context field within + * notit->packet. + */ + BT_ASSERT(bt_packet_borrow_context_field( + notit->packet) == + notit->dscopes.stream_packet_context); + } + + BT_ASSERT(notit->msg_iter); + + if (notit->meta.sc->packets_have_ts_begin) { + BT_ASSERT(notit->snapshots.beginning_clock != UINT64_C(-1)); + msg = bt_message_packet_beginning_create_with_default_clock_snapshot( + notit->msg_iter, notit->packet, + notit->snapshots.beginning_clock); + } else { + msg = bt_message_packet_beginning_create(notit->msg_iter, + notit->packet); + } + + if (!msg) { + BT_LOGE("Cannot create packet beginning message: " + "notit-addr=%p, packet-addr=%p", + notit, notit->packet); + goto end; + } + + *message = msg; + +end: + return; +} + +static +void create_msg_packet_end(struct bt_msg_iter *notit, bt_message **message) +{ + bt_message *msg; + + if (!notit->packet) { + return; + } + + /* Update default clock from packet's end time */ + if (notit->snapshots.end_clock != UINT64_C(-1)) { + notit->default_clock_snapshot = notit->snapshots.end_clock; + } + + BT_ASSERT(notit->msg_iter); + + if (notit->meta.sc->packets_have_ts_end) { + BT_ASSERT(notit->snapshots.end_clock != UINT64_C(-1)); + msg = bt_message_packet_end_create_with_default_clock_snapshot( + notit->msg_iter, notit->packet, + notit->snapshots.end_clock); + } else { + msg = bt_message_packet_end_create(notit->msg_iter, + notit->packet); + } + + if (!msg) { + BT_LOGE("Cannot create packet end message: " + "notit-addr=%p, packet-addr=%p", + notit, notit->packet); + return; + + } + + BT_PACKET_PUT_REF_AND_RESET(notit->packet); + *message = msg; +} + +static +void create_msg_discarded_events(struct bt_msg_iter *notit, + bt_message **message) +{ + bt_message *msg; + uint64_t beginning_raw_value = UINT64_C(-1); + uint64_t end_raw_value = UINT64_C(-1); + + BT_ASSERT(notit->msg_iter); + BT_ASSERT(notit->stream); + BT_ASSERT(notit->meta.sc->has_discarded_events); + + if (notit->meta.sc->discarded_events_have_default_cs) { + if (notit->prev_packet_snapshots.discarded_events == UINT64_C(-1)) { + /* + * We discarded events, but before (and possibly + * including) the current packet: use this packet's time + * range, and do not have a specific count. + */ + beginning_raw_value = notit->snapshots.beginning_clock; + end_raw_value = notit->snapshots.end_clock; + } else { + beginning_raw_value = notit->prev_packet_snapshots.end_clock; + end_raw_value = notit->snapshots.end_clock; + } + + BT_ASSERT(beginning_raw_value != UINT64_C(-1)); + BT_ASSERT(end_raw_value != UINT64_C(-1)); + msg = bt_message_discarded_events_create_with_default_clock_snapshots( + notit->msg_iter, notit->stream, beginning_raw_value, + end_raw_value); + } else { + msg = bt_message_discarded_events_create(notit->msg_iter, + notit->stream); + } + + if (!msg) { + BT_LOGE("Cannot create discarded events message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + if (notit->prev_packet_snapshots.discarded_events != UINT64_C(-1)) { + bt_message_discarded_events_set_count(msg, + notit->snapshots.discarded_events - + notit->prev_packet_snapshots.discarded_events); + } + + *message = msg; +} + +static +void create_msg_discarded_packets(struct bt_msg_iter *notit, + bt_message **message) +{ + bt_message *msg; + + BT_ASSERT(notit->msg_iter); + BT_ASSERT(notit->stream); + BT_ASSERT(notit->meta.sc->has_discarded_packets); + BT_ASSERT(notit->prev_packet_snapshots.packets != + UINT64_C(-1)); + + if (notit->meta.sc->discarded_packets_have_default_cs) { + BT_ASSERT(notit->prev_packet_snapshots.end_clock != UINT64_C(-1)); + BT_ASSERT(notit->snapshots.beginning_clock != UINT64_C(-1)); + msg = bt_message_discarded_packets_create_with_default_clock_snapshots( + notit->msg_iter, notit->stream, + notit->prev_packet_snapshots.end_clock, + notit->snapshots.beginning_clock); + } else { + msg = bt_message_discarded_packets_create(notit->msg_iter, + notit->stream); + } + + if (!msg) { + BT_LOGE("Cannot create discarded packets message: " + "notit-addr=%p, stream-addr=%p", + notit, notit->stream); + return; + } + + bt_message_discarded_packets_set_count(msg, + notit->snapshots.packets - + notit->prev_packet_snapshots.packets - 1); + *message = msg; +} + +BT_HIDDEN +struct bt_msg_iter *bt_msg_iter_create(struct ctf_trace_class *tc, + size_t max_request_sz, + struct bt_msg_iter_medium_ops medops, void *data) +{ + struct bt_msg_iter *notit = NULL; + struct bt_bfcr_cbs cbs = { + .classes = { + .signed_int = bfcr_signed_int_cb, + .unsigned_int = bfcr_unsigned_int_cb, + .floating_point = bfcr_floating_point_cb, + .string_begin = bfcr_string_begin_cb, + .string = bfcr_string_cb, + .string_end = bfcr_string_end_cb, + .compound_begin = bfcr_compound_begin_cb, + .compound_end = bfcr_compound_end_cb, + }, + .query = { + .get_sequence_length = bfcr_get_sequence_length_cb, + .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb, + }, + }; + + BT_ASSERT(tc); + BT_ASSERT(medops.request_bytes); + BT_ASSERT(medops.borrow_stream); + BT_LOGD("Creating CTF plugin message iterator: " + "trace-addr=%p, max-request-size=%zu, " + "data=%p", tc, max_request_sz, data); + notit = g_new0(struct bt_msg_iter, 1); + if (!notit) { + BT_LOGE_STR("Failed to allocate one CTF plugin message iterator."); + goto end; + } + notit->meta.tc = tc; + notit->medium.medops = medops; + notit->medium.max_request_sz = max_request_sz; + notit->medium.data = data; + notit->stack = stack_new(notit); + notit->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t)); + g_array_set_size(notit->stored_values, tc->stored_value_count); + + if (!notit->stack) { + BT_LOGE_STR("Failed to create field stack."); + goto error; + } + + notit->bfcr = bt_bfcr_create(cbs, notit); + if (!notit->bfcr) { + BT_LOGE_STR("Failed to create binary class reader (BFCR)."); + goto error; + } + + bt_msg_iter_reset(notit); + BT_LOGD("Created CTF plugin message iterator: " + "trace-addr=%p, max-request-size=%zu, " + "data=%p, notit-addr=%p", + tc, max_request_sz, data, notit); + notit->cur_packet_offset = 0; + +end: + return notit; + +error: + bt_msg_iter_destroy(notit); + notit = NULL; + goto end; +} + +void bt_msg_iter_destroy(struct bt_msg_iter *notit) +{ + BT_PACKET_PUT_REF_AND_RESET(notit->packet); + BT_STREAM_PUT_REF_AND_RESET(notit->stream); + release_all_dscopes(notit); + + BT_LOGD("Destroying CTF plugin message iterator: addr=%p", notit); + + if (notit->stack) { + BT_LOGD_STR("Destroying field stack."); + stack_destroy(notit->stack); + } + + if (notit->bfcr) { + BT_LOGD("Destroying BFCR: bfcr-addr=%p", notit->bfcr); + bt_bfcr_destroy(notit->bfcr); + } + + if (notit->stored_values) { + g_array_free(notit->stored_values, TRUE); + } + + g_free(notit); +} + +enum bt_msg_iter_status bt_msg_iter_get_next_message( + struct bt_msg_iter *notit, + bt_self_message_iterator *msg_iter, bt_message **message) +{ + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + BT_ASSERT(notit); + BT_ASSERT(message); + notit->msg_iter = msg_iter; + notit->set_stream = true; + BT_LOGV("Getting next message: notit-addr=%p", notit); + + while (true) { + status = handle_state(notit); + if (unlikely(status == BT_MSG_ITER_STATUS_AGAIN)) { + BT_LOGV_STR("Medium returned BT_MSG_ITER_STATUS_AGAIN."); + goto end; + } else if (unlikely(status != BT_MSG_ITER_STATUS_OK)) { + BT_LOGW("Cannot handle state: notit-addr=%p, state=%s", + notit, state_string(notit->state)); + goto end; + } + + switch (notit->state) { + case STATE_EMIT_MSG_EVENT: + BT_ASSERT(notit->event_msg); + *message = notit->event_msg; + notit->event_msg = NULL; + goto end; + case STATE_EMIT_MSG_DISCARDED_EVENTS: + /* create_msg_discared_events() logs errors */ + create_msg_discarded_events(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_DISCARDED_PACKETS: + /* create_msg_discared_packets() logs errors */ + create_msg_discarded_packets(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_PACKET_BEGINNING: + /* create_msg_packet_beginning() logs errors */ + create_msg_packet_beginning(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_PACKET_END_SINGLE: + case STATE_EMIT_MSG_PACKET_END_MULTI: + /* create_msg_packet_end() logs errors */ + create_msg_packet_end(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: + /* create_msg_stream_activity_beginning() logs errors */ + create_msg_stream_activity_beginning(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_STREAM_ACTIVITY_END: + /* create_msg_stream_activity_end() logs errors */ + create_msg_stream_activity_end(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_STREAM_BEGINNING: + /* create_msg_stream_beginning() logs errors */ + create_msg_stream_beginning(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_EMIT_MSG_STREAM_END: + /* create_msg_stream_end() logs errors */ + create_msg_stream_end(notit, message); + + if (!*message) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + goto end; + case STATE_DONE: + status = BT_MSG_ITER_STATUS_EOF; + goto end; + default: + /* Non-emitting state: continue */ + break; + } + } + +end: + return status; +} + +static +enum bt_msg_iter_status read_packet_header_context_fields( + struct bt_msg_iter *notit) +{ + int ret; + enum bt_msg_iter_status status = BT_MSG_ITER_STATUS_OK; + + BT_ASSERT(notit); + notit->set_stream = false; + + if (notit->state == STATE_EMIT_MSG_PACKET_BEGINNING) { + /* We're already there */ + goto end; + } + + while (true) { + status = handle_state(notit); + if (unlikely(status == BT_MSG_ITER_STATUS_AGAIN)) { + BT_LOGV_STR("Medium returned BT_MSG_ITER_STATUS_AGAIN."); + goto end; + } else if (unlikely(status != BT_MSG_ITER_STATUS_OK)) { + BT_LOGW("Cannot handle state: notit-addr=%p, state=%s", + notit, state_string(notit->state)); + goto end; + } + + switch (notit->state) { + case STATE_EMIT_MSG_PACKET_BEGINNING: + /* + * Packet header and context fields are + * potentially decoded (or they don't exist). + */ + goto end; + case STATE_INIT: + case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: + case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: + case STATE_AFTER_TRACE_PACKET_HEADER: + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: + case STATE_AFTER_STREAM_PACKET_CONTEXT: + case STATE_CHECK_EMIT_MSG_STREAM_BEGINNING: + case STATE_EMIT_MSG_STREAM_BEGINNING: + case STATE_EMIT_MSG_STREAM_ACTIVITY_BEGINNING: + case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS: + case STATE_EMIT_MSG_DISCARDED_EVENTS: + case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS: + case STATE_EMIT_MSG_DISCARDED_PACKETS: + /* Non-emitting state: continue */ + break; + default: + /* + * We should never get past the + * STATE_EMIT_MSG_PACKET_BEGINNING state. + */ + BT_LOGF("Unexpected state: notit-addr=%p, state=%s", + notit, state_string(notit->state)); + abort(); + } + } + +end: + ret = set_current_packet_content_sizes(notit); + if (ret) { + status = BT_MSG_ITER_STATUS_ERROR; + } + + return status; +} + +BT_HIDDEN +void bt_msg_iter_set_medops_data(struct bt_msg_iter *notit, + void *medops_data) +{ + BT_ASSERT(notit); + notit->medium.data = medops_data; +} + +BT_HIDDEN +enum bt_msg_iter_status bt_msg_iter_seek(struct bt_msg_iter *notit, + off_t offset) +{ + enum bt_msg_iter_status ret = BT_MSG_ITER_STATUS_OK; + enum bt_msg_iter_medium_status medium_status; + + BT_ASSERT(notit); + if (offset < 0) { + BT_LOGE("Cannot seek to negative offset: offset=%jd", offset); + ret = BT_MSG_ITER_STATUS_INVAL; + goto end; + } + + if (!notit->medium.medops.seek) { + ret = BT_MSG_ITER_STATUS_UNSUPPORTED; + BT_LOGD("Aborting seek as the iterator's underlying media does not implement seek support."); + goto end; + } + + medium_status = notit->medium.medops.seek( + BT_MSG_ITER_SEEK_WHENCE_SET, offset, notit->medium.data); + if (medium_status != BT_MSG_ITER_MEDIUM_STATUS_OK) { + if (medium_status == BT_MSG_ITER_MEDIUM_STATUS_EOF) { + ret = BT_MSG_ITER_STATUS_EOF; + } else { + ret = BT_MSG_ITER_STATUS_ERROR; + goto end; + } + } + + bt_msg_iter_reset(notit); + notit->cur_packet_offset = offset; + +end: + return ret; +} + +BT_HIDDEN +enum bt_msg_iter_status bt_msg_iter_get_packet_properties( + struct bt_msg_iter *notit, + struct bt_msg_iter_packet_properties *props) +{ + enum bt_msg_iter_status status; + + BT_ASSERT(notit); + BT_ASSERT(props); + status = read_packet_header_context_fields(notit); + if (status != BT_MSG_ITER_STATUS_OK) { + goto end; + } + + props->exp_packet_total_size = notit->cur_exp_packet_total_size; + props->exp_packet_content_size = notit->cur_exp_packet_content_size; + props->stream_class_id = (uint64_t) notit->cur_stream_class_id; + props->data_stream_id = notit->cur_data_stream_id; + props->snapshots.discarded_events = notit->snapshots.discarded_events; + props->snapshots.packets = notit->snapshots.packets; + props->snapshots.beginning_clock = notit->snapshots.beginning_clock; + props->snapshots.end_clock = notit->snapshots.end_clock; + +end: + return status; +} + +BT_HIDDEN +void bt_msg_iter_set_emit_stream_beginning_message(struct bt_msg_iter *notit, + bool val) +{ + notit->emit_stream_begin_msg = val; +} + +BT_HIDDEN +void bt_msg_iter_set_emit_stream_end_message(struct bt_msg_iter *notit, + bool val) +{ + notit->emit_stream_end_msg = val; +} diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.h b/src/plugins/ctf/common/msg-iter/msg-iter.h new file mode 100644 index 00000000..65dd0ee1 --- /dev/null +++ b/src/plugins/ctf/common/msg-iter/msg-iter.h @@ -0,0 +1,382 @@ +#ifndef CTF_MSG_ITER_H +#define CTF_MSG_ITER_H + +/* + * Babeltrace - CTF message iterator + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/babeltrace.h" + +#include "../metadata/ctf-meta.h" + +/** + * @file ctf-msg-iter.h + * + * CTF message iterator + * + * This is a common internal API used by CTF source plugins. It allows + * one to get messages from a user-provided medium. + */ + +/** + * Medium operations status codes. + */ +enum bt_msg_iter_medium_status { + /** + * End of file. + * + * The medium function called by the message iterator + * function reached the end of the file. + */ + BT_MSG_ITER_MEDIUM_STATUS_EOF = 1, + + /** + * There is no data available right now, try again later. + */ + BT_MSG_ITER_MEDIUM_STATUS_AGAIN = 11, + + /** Unsupported operation. */ + BT_MSG_ITER_MEDIUM_STATUS_UNSUPPORTED = -3, + + /** Invalid argument. */ + BT_MSG_ITER_MEDIUM_STATUS_INVAL = -2, + + /** General error. */ + BT_MSG_ITER_MEDIUM_STATUS_ERROR = -1, + + /** Everything okay. */ + BT_MSG_ITER_MEDIUM_STATUS_OK = 0, +}; + +/** + * CTF message iterator API status code. + */ +enum bt_msg_iter_status { + /** + * End of file. + * + * The medium function called by the message iterator + * function reached the end of the file. + */ + BT_MSG_ITER_STATUS_EOF = BT_MSG_ITER_MEDIUM_STATUS_EOF, + + /** + * There is no data available right now, try again later. + * + * Some condition resulted in the + * bt_msg_iter_medium_ops::request_bytes() user function not + * having access to any data now. You should retry calling the + * last called message iterator function once the situation + * is resolved. + */ + BT_MSG_ITER_STATUS_AGAIN = BT_MSG_ITER_MEDIUM_STATUS_AGAIN, + + /** Invalid argument. */ + BT_MSG_ITER_STATUS_INVAL = BT_MSG_ITER_MEDIUM_STATUS_INVAL, + + /** Unsupported operation. */ + BT_MSG_ITER_STATUS_UNSUPPORTED = BT_MSG_ITER_MEDIUM_STATUS_UNSUPPORTED, + + /** General error. */ + BT_MSG_ITER_STATUS_ERROR = BT_MSG_ITER_MEDIUM_STATUS_ERROR, + + /** Everything okay. */ + BT_MSG_ITER_STATUS_OK = 0, +}; + +/** + * CTF message iterator seek operation directives. + */ +enum bt_msg_iter_seek_whence { + /** + * Set the iterator's position to an absolute offset in the underlying + * medium. + */ + BT_MSG_ITER_SEEK_WHENCE_SET, +}; + +/** + * Medium operations. + * + * Those user functions are called by the message iterator + * functions to request medium actions. + */ +struct bt_msg_iter_medium_ops { + /** + * Returns the next byte buffer to be used by the binary file + * reader to deserialize binary data. + * + * This function \em must be defined. + * + * The purpose of this function is to return a buffer of bytes + * to the message iterator, of a maximum of \p request_sz + * bytes. If this function cannot return a buffer of at least + * \p request_sz bytes, it may return a smaller buffer. In + * either cases, \p buffer_sz must be set to the returned buffer + * size (in bytes). + * + * The returned buffer's ownership remains the medium, in that + * it won't be freed by the message iterator functions. The + * returned buffer won't be modified by the message + * iterator functions either. + * + * When this function is called for the first time for a given + * file, the offset within the file is considered to be 0. The + * next times this function is called, the returned buffer's + * byte offset within the complete file must be the previous + * offset plus the last returned value of \p buffer_sz by this + * medium. + * + * This function must return one of the following statuses: + * + * - #BT_MSG_ITER_MEDIUM_STATUS_OK: Everything + * is okay, i.e. \p buffer_sz is set to a positive value + * reflecting the number of available bytes in the buffer + * starting at the address written in \p buffer_addr. + * - #BT_MSG_ITER_MEDIUM_STATUS_AGAIN: No data is + * available right now. In this case, the message + * iterator function called by the user returns + * #BT_MSG_ITER_STATUS_AGAIN, and it is the user's + * responsibility to make sure enough data becomes available + * before calling the \em same message iterator + * function again to continue the decoding process. + * - #BT_MSG_ITER_MEDIUM_STATUS_EOF: The end of + * the file was reached, and no more data will ever be + * available for this file. In this case, the message + * iterator function called by the user returns + * #BT_MSG_ITER_STATUS_EOF. This must \em not be + * returned when returning at least one byte of data to the + * caller, i.e. this must be returned when there's + * absolutely nothing left; should the request size be + * larger than what's left in the file, this function must + * return what's left, setting \p buffer_sz to the number of + * remaining bytes, and return + * #BT_MSG_ITER_MEDIUM_STATUS_EOF on the \em following + * call. + * - #BT_MSG_ITER_MEDIUM_STATUS_ERROR: A fatal + * error occured during this operation. In this case, the + * message iterator function called by the user returns + * #BT_MSG_ITER_STATUS_ERROR. + * + * If #BT_MSG_ITER_MEDIUM_STATUS_OK is not returned, the + * values of \p buffer_sz and \p buffer_addr are \em ignored by + * the caller. + * + * @param request_sz Requested buffer size (bytes) + * @param buffer_addr Returned buffer address + * @param buffer_sz Returned buffer's size (bytes) + * @param data User data + * @returns Status code (see description above) + */ + enum bt_msg_iter_medium_status (* request_bytes)(size_t request_sz, + uint8_t **buffer_addr, size_t *buffer_sz, void *data); + + /** + * Repositions the underlying stream's position. + * + * This *optional* method repositions the underlying stream + * to a given absolute or relative position, as indicated by + * the whence directive. + * + * @param whence One of #bt_msg_iter_seek_whence values + * @param offset Offset to use for the given directive + * @param data User data + * @returns One of #bt_msg_iter_medium_status values + */ + enum bt_msg_iter_medium_status (* seek)( + enum bt_msg_iter_seek_whence whence, + off_t offset, void *data); + + /** + * Returns a stream instance (weak reference) for the given + * stream class. + * + * This is called after a packet header is read, and the + * corresponding stream class is found by the message + * iterator. + * + * @param stream_class Stream class of the stream to get + * @param stream_id Stream (instance) ID of the stream + * to get (-1ULL if not available) + * @param data User data + * @returns Stream instance (weak reference) or + * \c NULL on error + */ + bt_stream * (* borrow_stream)(bt_stream_class *stream_class, + int64_t stream_id, void *data); +}; + +/** CTF message iterator. */ +struct bt_msg_iter; + +/** + * Creates a CTF message iterator. + * + * Upon successful completion, the reference count of \p trace is + * incremented. + * + * @param trace Trace to read + * @param max_request_sz Maximum buffer size, in bytes, to + * request to + * bt_msg_iter_medium_ops::request_bytes() + * at a time + * @param medops Medium operations + * @param medops_data User data (passed to medium operations) + * @returns New CTF message iterator on + * success, or \c NULL on error + */ +BT_HIDDEN +struct bt_msg_iter *bt_msg_iter_create(struct ctf_trace_class *tc, + size_t max_request_sz, struct bt_msg_iter_medium_ops medops, + void *medops_data); + +/** + * Destroys a CTF message iterator, freeing all internal resources. + * + * The registered trace's reference count is decremented. + * + * @param msg_iter CTF message iterator + */ +BT_HIDDEN +void bt_msg_iter_destroy(struct bt_msg_iter *msg_iter); + +/** + * Returns the next message from a CTF message iterator. + * + * Upon successful completion, #BT_MSG_ITER_STATUS_OK is + * returned, and the next message is written to \p msg. + * In this case, the caller is responsible for calling + * bt_message_put() on the returned message. + * + * If this function returns #BT_MSG_ITER_STATUS_AGAIN, the caller + * should make sure that data becomes available to its medium, and + * call this function again, until another status is returned. + * + * @param msg_iter CTF message iterator + * @param message Returned message if the function's + * return value is #BT_MSG_ITER_STATUS_OK + * @returns One of #bt_msg_iter_status values + */ +BT_HIDDEN +enum bt_msg_iter_status bt_msg_iter_get_next_message( + struct bt_msg_iter *notit, + bt_self_message_iterator *msg_iter, + bt_message **message); + +struct bt_msg_iter_packet_properties { + int64_t exp_packet_total_size; + int64_t exp_packet_content_size; + uint64_t stream_class_id; + int64_t data_stream_id; + + struct { + uint64_t discarded_events; + uint64_t packets; + uint64_t beginning_clock; + uint64_t end_clock; + } snapshots; +}; + +BT_HIDDEN +enum bt_msg_iter_status bt_msg_iter_get_packet_properties( + struct bt_msg_iter *notit, + struct bt_msg_iter_packet_properties *props); + +BT_HIDDEN +void bt_msg_iter_set_medops_data(struct bt_msg_iter *notit, + void *medops_data); + +BT_HIDDEN +enum bt_msg_iter_status bt_msg_iter_seek( + struct bt_msg_iter *notit, off_t offset); + +/* + * Resets the iterator so that the next requested medium bytes are + * assumed to be the first bytes of a new stream. Depending on + * bt_msg_iter_set_emit_stream_beginning_message(), the first message + * which this iterator emits after calling bt_msg_iter_reset() is of + * type `BT_MESSAGE_TYPE_STREAM_BEGINNING`. + */ +BT_HIDDEN +void bt_msg_iter_reset(struct bt_msg_iter *notit); + +/* + * Like bt_msg_iter_reset(), but preserves stream-dependent state. + */ +BT_HIDDEN +void bt_msg_iter_reset_for_next_stream_file(struct bt_msg_iter *notit); + +BT_HIDDEN +void bt_msg_iter_set_emit_stream_beginning_message(struct bt_msg_iter *notit, + bool val); + +BT_HIDDEN +void bt_msg_iter_set_emit_stream_end_message(struct bt_msg_iter *notit, + bool val); + +static inline +const char *bt_msg_iter_medium_status_string( + enum bt_msg_iter_medium_status status) +{ + switch (status) { + case BT_MSG_ITER_MEDIUM_STATUS_EOF: + return "BT_MSG_ITER_MEDIUM_STATUS_EOF"; + case BT_MSG_ITER_MEDIUM_STATUS_AGAIN: + return "BT_MSG_ITER_MEDIUM_STATUS_AGAIN"; + case BT_MSG_ITER_MEDIUM_STATUS_INVAL: + return "BT_MSG_ITER_MEDIUM_STATUS_INVAL"; + case BT_MSG_ITER_MEDIUM_STATUS_ERROR: + return "BT_MSG_ITER_MEDIUM_STATUS_ERROR"; + case BT_MSG_ITER_MEDIUM_STATUS_OK: + return "BT_MSG_ITER_MEDIUM_STATUS_OK"; + default: + return "(unknown)"; + } +} + +static inline +const char *bt_msg_iter_status_string( + enum bt_msg_iter_status status) +{ + switch (status) { + case BT_MSG_ITER_STATUS_EOF: + return "BT_MSG_ITER_STATUS_EOF"; + case BT_MSG_ITER_STATUS_AGAIN: + return "BT_MSG_ITER_STATUS_AGAIN"; + case BT_MSG_ITER_STATUS_INVAL: + return "BT_MSG_ITER_STATUS_INVAL"; + case BT_MSG_ITER_STATUS_ERROR: + return "BT_MSG_ITER_STATUS_ERROR"; + case BT_MSG_ITER_STATUS_OK: + return "BT_MSG_ITER_STATUS_OK"; + default: + return "(unknown)"; + } +} + +#endif /* CTF_MSG_ITER_H */ diff --git a/src/plugins/ctf/common/print.h b/src/plugins/ctf/common/print.h new file mode 100644 index 00000000..8825a9a7 --- /dev/null +++ b/src/plugins/ctf/common/print.h @@ -0,0 +1,58 @@ +#ifndef CTF_BTR_PRINT_H +#define CTF_BTR_PRINT_H + +/* + * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +#define PERR(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Error: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PWARN(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Warning: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PDBG(fmt, ...) \ + do { \ + if (babeltrace_debug) { \ + fprintf(stderr, \ + "Debug: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* CTF_BTR_PRINT_H */ diff --git a/src/plugins/ctf/common/utils/Makefile.am b/src/plugins/ctf/common/utils/Makefile.am new file mode 100644 index 00000000..c4b62ff5 --- /dev/null +++ b/src/plugins/ctf/common/utils/Makefile.am @@ -0,0 +1,6 @@ +noinst_LTLIBRARIES = libctf-utils.la +libctf_utils_la_SOURCES = \ + logging.c \ + logging.h \ + utils.c \ + utils.h diff --git a/src/plugins/ctf/common/utils/logging.c b/src/plugins/ctf/common/utils/logging.c new file mode 100644 index 00000000..bd773eaf --- /dev/null +++ b/src/plugins/ctf/common/utils/logging.c @@ -0,0 +1,26 @@ + /* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL utils_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(utils_log_level, "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL"); diff --git a/src/plugins/ctf/common/utils/logging.h b/src/plugins/ctf/common/utils/logging.h new file mode 100644 index 00000000..2eeace49 --- /dev/null +++ b/src/plugins/ctf/common/utils/logging.h @@ -0,0 +1,31 @@ +#ifndef CTF_UTILS_LOGGING_H +#define CTF_UTILS_LOGGING_H + +/* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL utils_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(utils_log_level); + +#endif /* CTF_UTILS_LOGGING_H */ diff --git a/src/plugins/ctf/common/utils/utils.c b/src/plugins/ctf/common/utils/utils.c new file mode 100644 index 00000000..dd65f941 --- /dev/null +++ b/src/plugins/ctf/common/utils/utils.c @@ -0,0 +1,28 @@ +/* + * Babeltrace - CTF Utils + * + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-UTILS" +#include "logging.h" + +#include "utils.h" diff --git a/src/plugins/ctf/common/utils/utils.h b/src/plugins/ctf/common/utils/utils.h new file mode 100644 index 00000000..28f056ca --- /dev/null +++ b/src/plugins/ctf/common/utils/utils.h @@ -0,0 +1,31 @@ +#ifndef CTF_UTILS_H +#define CTF_UTILS_H + +/* + * Babeltrace - CTF Utilities + * + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" + +#endif /* CTF_UTILS_H */ diff --git a/src/plugins/ctf/fs-sink/Makefile.am b/src/plugins/ctf/fs-sink/Makefile.am new file mode 100644 index 00000000..6035b1a1 --- /dev/null +++ b/src/plugins/ctf/fs-sink/Makefile.am @@ -0,0 +1,17 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-sink.la + +libbabeltrace2_plugin_ctf_fs_sink_la_LIBADD = +libbabeltrace2_plugin_ctf_fs_sink_la_SOURCES = \ + fs-sink.c \ + fs-sink.h \ + logging.c \ + logging.h \ + fs-sink-ctf-meta.h \ + translate-trace-ir-to-ctf-ir.c \ + translate-trace-ir-to-ctf-ir.h \ + translate-ctf-ir-to-tsdl.c \ + translate-ctf-ir-to-tsdl.h \ + fs-sink-stream.c \ + fs-sink-stream.h \ + fs-sink-trace.c \ + fs-sink-trace.h diff --git a/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h b/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h new file mode 100644 index 00000000..6ae88dfb --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h @@ -0,0 +1,926 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H + +/* + * Copyright 2018-2019 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include "common/common.h" +#include "common/assert.h" +#include "compat/uuid.h" +#include +#include +#include +#include +#include + +enum fs_sink_ctf_field_class_type { + FS_SINK_CTF_FIELD_CLASS_TYPE_INT, + FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT, + FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, + FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, + FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, + FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, + FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, +}; + +struct fs_sink_ctf_field_class { + enum fs_sink_ctf_field_class_type type; + + /* Weak */ + const bt_field_class *ir_fc; + + unsigned int alignment; + + /* Index of the field class within its own parent */ + uint64_t index_in_parent; +}; + +struct fs_sink_ctf_field_class_bit_array { + struct fs_sink_ctf_field_class base; + unsigned int size; +}; + +struct fs_sink_ctf_field_class_int { + struct fs_sink_ctf_field_class_bit_array base; + bool is_signed; +}; + +struct fs_sink_ctf_field_class_float { + struct fs_sink_ctf_field_class_bit_array base; +}; + +struct fs_sink_ctf_field_class_string { + struct fs_sink_ctf_field_class base; +}; + +struct fs_sink_ctf_named_field_class { + GString *name; + + /* Owned by this */ + struct fs_sink_ctf_field_class *fc; +}; + +struct fs_sink_ctf_field_class_struct { + struct fs_sink_ctf_field_class base; + + /* Array of `struct fs_sink_ctf_named_field_class` */ + GArray *members; +}; + +struct fs_sink_ctf_field_class_variant { + struct fs_sink_ctf_field_class base; + GString *tag_ref; + bool tag_is_before; + + /* Array of `struct fs_sink_ctf_named_field_class` */ + GArray *options; +}; + +struct fs_sink_ctf_field_class_array_base { + struct fs_sink_ctf_field_class base; + struct fs_sink_ctf_field_class *elem_fc; +}; + +struct fs_sink_ctf_field_class_array { + struct fs_sink_ctf_field_class_array_base base; + uint64_t length; +}; + +struct fs_sink_ctf_field_class_sequence { + struct fs_sink_ctf_field_class_array_base base; + GString *length_ref; + bool length_is_before; +}; + +struct fs_sink_ctf_stream_class; + +struct fs_sink_ctf_event_class { + /* Weak */ + const bt_event_class *ir_ec; + + /* Weak */ + struct fs_sink_ctf_stream_class *sc; + + /* Owned by this */ + struct fs_sink_ctf_field_class *spec_context_fc; + + /* Owned by this */ + struct fs_sink_ctf_field_class *payload_fc; +}; + +struct fs_sink_ctf_trace_class; + +struct fs_sink_ctf_stream_class { + /* Weak */ + struct fs_sink_ctf_trace_class *tc; + + /* Weak */ + const bt_stream_class *ir_sc; + + /* Weak */ + const bt_clock_class *default_clock_class; + + GString *default_clock_class_name; + bool packets_have_ts_begin; + bool packets_have_ts_end; + bool has_discarded_events; + bool discarded_events_has_ts; + bool discarded_packets_has_ts; + + /* Owned by this */ + struct fs_sink_ctf_field_class *packet_context_fc; + + /* Owned by this */ + struct fs_sink_ctf_field_class *event_common_context_fc; + + /* Array of `struct fs_sink_ctf_event_class *` (owned by this) */ + GPtrArray *event_classes; + + /* + * `const bt_event_class *` (weak) -> + * `struct fs_sink_ctf_event_class *` (weak) + */ + GHashTable *event_classes_from_ir; +}; + +struct fs_sink_ctf_trace_class { + /* Weak */ + const bt_trace_class *ir_tc; + + unsigned char uuid[BABELTRACE_UUID_LEN]; + + /* Array of `struct fs_sink_ctf_stream_class *` (owned by this) */ + GPtrArray *stream_classes; +}; + +static inline +void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc); + +static inline +void _fs_sink_ctf_field_class_init(struct fs_sink_ctf_field_class *fc, + enum fs_sink_ctf_field_class_type type, + const bt_field_class *ir_fc, unsigned int alignment, + uint64_t index_in_parent) +{ + BT_ASSERT(fc); + fc->type = type; + fc->ir_fc = ir_fc; + fc->alignment = alignment; + fc->index_in_parent = index_in_parent; +} + +static inline +void _fs_sink_ctf_field_class_bit_array_init( + struct fs_sink_ctf_field_class_bit_array *fc, + enum fs_sink_ctf_field_class_type type, + const bt_field_class *ir_fc, unsigned int size, + uint64_t index_in_parent) +{ + _fs_sink_ctf_field_class_init((void *) fc, type, ir_fc, + size % 8 == 0 ? 8 : 1, index_in_parent); + fc->size = size; +} + +static inline +void _fs_sink_ctf_field_class_int_init(struct fs_sink_ctf_field_class_int *fc, + enum fs_sink_ctf_field_class_type type, + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + bt_field_class_type ir_fc_type = bt_field_class_get_type(ir_fc); + + _fs_sink_ctf_field_class_bit_array_init((void *) fc, type, ir_fc, + (unsigned int) bt_field_class_integer_get_field_value_range( + ir_fc), + index_in_parent); + fc->is_signed = (ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || + ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION); +} + +static inline +void _fs_sink_ctf_named_field_class_init( + struct fs_sink_ctf_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + named_fc->name = g_string_new(NULL); + BT_ASSERT(named_fc->name); +} + +static inline +void _fs_sink_ctf_named_field_class_fini( + struct fs_sink_ctf_named_field_class *named_fc) +{ + BT_ASSERT(named_fc); + + if (named_fc->name) { + g_string_free(named_fc->name, TRUE); + named_fc->name = NULL; + } + + fs_sink_ctf_field_class_destroy(named_fc->fc); + named_fc->fc = NULL; +} + +static inline +struct fs_sink_ctf_field_class_int *fs_sink_ctf_field_class_int_create( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_int *fc = + g_new0(struct fs_sink_ctf_field_class_int, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_int_init(fc, FS_SINK_CTF_FIELD_CLASS_TYPE_INT, + ir_fc, index_in_parent); + return fc; +} + +static inline +struct fs_sink_ctf_field_class_float *fs_sink_ctf_field_class_float_create( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_float *fc = + g_new0(struct fs_sink_ctf_field_class_float, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_bit_array_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT, + ir_fc, bt_field_class_real_is_single_precision(ir_fc) ? 32 : 64, + index_in_parent); + return fc; +} + +static inline +struct fs_sink_ctf_field_class_string *fs_sink_ctf_field_class_string_create( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_string *fc = + g_new0(struct fs_sink_ctf_field_class_string, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, ir_fc, + 8, index_in_parent); + return fc; +} + +static inline +struct fs_sink_ctf_field_class_struct *fs_sink_ctf_field_class_struct_create_empty( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_struct *fc = + g_new0(struct fs_sink_ctf_field_class_struct, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, ir_fc, 1, index_in_parent); + fc->members = g_array_new(FALSE, TRUE, + sizeof(struct fs_sink_ctf_named_field_class)); + BT_ASSERT(fc->members); + return fc; +} + +static inline +struct fs_sink_ctf_field_class_variant *fs_sink_ctf_field_class_variant_create_empty( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_variant *fc = + g_new0(struct fs_sink_ctf_field_class_variant, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, ir_fc, + 1, index_in_parent); + fc->options = g_array_new(FALSE, TRUE, + sizeof(struct fs_sink_ctf_named_field_class)); + BT_ASSERT(fc->options); + fc->tag_ref = g_string_new(NULL); + BT_ASSERT(fc->tag_ref); + fc->tag_is_before = + bt_field_class_variant_borrow_selector_field_path_const(ir_fc) == + NULL; + return fc; +} + +static inline +struct fs_sink_ctf_field_class_array *fs_sink_ctf_field_class_array_create_empty( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_array *fc = + g_new0(struct fs_sink_ctf_field_class_array, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc, + 1, index_in_parent); + fc->length = bt_field_class_static_array_get_length(ir_fc); + return fc; +} + +static inline +struct fs_sink_ctf_field_class_sequence *fs_sink_ctf_field_class_sequence_create_empty( + const bt_field_class *ir_fc, uint64_t index_in_parent) +{ + struct fs_sink_ctf_field_class_sequence *fc = + g_new0(struct fs_sink_ctf_field_class_sequence, 1); + + BT_ASSERT(fc); + _fs_sink_ctf_field_class_init((void *) fc, + FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE, + ir_fc, 1, index_in_parent); + fc->length_ref = g_string_new(NULL); + BT_ASSERT(fc->length_ref); + fc->length_is_before = + bt_field_class_dynamic_array_borrow_length_field_path_const(ir_fc) == + NULL; + return fc; +} + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_struct_borrow_member_by_index( + struct fs_sink_ctf_field_class_struct *fc, uint64_t index); + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_variant_borrow_option_by_index( + struct fs_sink_ctf_field_class_variant *fc, uint64_t index); + +static inline +void _fs_sink_ctf_field_class_fini(struct fs_sink_ctf_field_class *fc) +{ + BT_ASSERT(fc); +} + +static inline +void _fs_sink_ctf_field_class_int_destroy( + struct fs_sink_ctf_field_class_int *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_float_destroy( + struct fs_sink_ctf_field_class_float *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_string_destroy( + struct fs_sink_ctf_field_class_string *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_struct_destroy( + struct fs_sink_ctf_field_class_struct *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + + if (fc->members) { + uint64_t i; + + for (i = 0; i < fc->members->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_struct_borrow_member_by_index( + fc, i); + + _fs_sink_ctf_named_field_class_fini(named_fc); + } + + g_array_free(fc->members, TRUE); + fc->members = NULL; + } + + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_array_base_fini( + struct fs_sink_ctf_field_class_array_base *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + fs_sink_ctf_field_class_destroy(fc->elem_fc); + fc->elem_fc = NULL; +} + +static inline +void _fs_sink_ctf_field_class_array_destroy( + struct fs_sink_ctf_field_class_array *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_array_base_fini((void *) fc); + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_sequence_destroy( + struct fs_sink_ctf_field_class_sequence *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_array_base_fini((void *) fc); + + if (fc->length_ref) { + g_string_free(fc->length_ref, TRUE); + fc->length_ref = NULL; + } + + g_free(fc); +} + +static inline +void _fs_sink_ctf_field_class_variant_destroy( + struct fs_sink_ctf_field_class_variant *fc) +{ + BT_ASSERT(fc); + _fs_sink_ctf_field_class_fini((void *) fc); + + if (fc->options) { + uint64_t i; + + for (i = 0; i < fc->options->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_variant_borrow_option_by_index( + fc, i); + + _fs_sink_ctf_named_field_class_fini(named_fc); + } + + g_array_free(fc->options, TRUE); + fc->options = NULL; + } + + if (fc->tag_ref) { + g_string_free(fc->tag_ref, TRUE); + fc->tag_ref = NULL; + } + + g_free(fc); +} + +static inline +void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc) +{ + if (!fc) { + return; + } + + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: + _fs_sink_ctf_field_class_int_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: + _fs_sink_ctf_field_class_float_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: + _fs_sink_ctf_field_class_string_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + _fs_sink_ctf_field_class_struct_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + _fs_sink_ctf_field_class_array_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + _fs_sink_ctf_field_class_sequence_destroy((void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + _fs_sink_ctf_field_class_variant_destroy((void *) fc); + break; + default: + abort(); + } +} + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_struct_borrow_member_by_index( + struct fs_sink_ctf_field_class_struct *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->members->len); + return &g_array_index(fc->members, struct fs_sink_ctf_named_field_class, + index); +} + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_struct_borrow_member_by_name( + struct fs_sink_ctf_field_class_struct *fc, const char *name) +{ + uint64_t i; + struct fs_sink_ctf_named_field_class *ret_named_fc = NULL; + + BT_ASSERT(fc); + BT_ASSERT(name); + + for (i = 0; i < fc->members->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_struct_borrow_member_by_index( + fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_named_fc = named_fc; + goto end; + } + } + +end: + return ret_named_fc; +} + +static inline +struct fs_sink_ctf_field_class * +fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name( + struct fs_sink_ctf_field_class_struct *struct_fc, const char *name) +{ + struct fs_sink_ctf_named_field_class *named_fc = NULL; + struct fs_sink_ctf_field_class *fc = NULL; + + if (!struct_fc) { + goto end; + } + + named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_name( + struct_fc, name); + if (!named_fc) { + goto end; + } + + fc = named_fc->fc; + +end: + return fc; +} + +static inline +struct fs_sink_ctf_field_class_int * +fs_sink_ctf_field_class_struct_borrow_member_int_field_class_by_name( + struct fs_sink_ctf_field_class_struct *struct_fc, + const char *name) +{ + struct fs_sink_ctf_field_class_int *int_fc = NULL; + + int_fc = (void *) + fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name( + struct_fc, name); + if (!int_fc) { + goto end; + } + + if (int_fc->base.base.type != FS_SINK_CTF_FIELD_CLASS_TYPE_INT) { + int_fc = NULL; + goto end; + } + +end: + return int_fc; +} + +static inline +void fs_sink_ctf_field_class_struct_align_at_least( + struct fs_sink_ctf_field_class_struct *fc, + unsigned int alignment) +{ + if (alignment > fc->base.alignment) { + fc->base.alignment = alignment; + } +} + +static inline +void fs_sink_ctf_field_class_struct_append_member( + struct fs_sink_ctf_field_class_struct *fc, + const char *name, struct fs_sink_ctf_field_class *member_fc) +{ + struct fs_sink_ctf_named_field_class *named_fc; + + BT_ASSERT(fc); + BT_ASSERT(name); + g_array_set_size(fc->members, fc->members->len + 1); + + named_fc = &g_array_index(fc->members, + struct fs_sink_ctf_named_field_class, fc->members->len - 1); + _fs_sink_ctf_named_field_class_init(named_fc); + g_string_assign(named_fc->name, name); + named_fc->fc = member_fc; + fs_sink_ctf_field_class_struct_align_at_least(fc, member_fc->alignment); +} + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_variant_borrow_option_by_index( + struct fs_sink_ctf_field_class_variant *fc, uint64_t index) +{ + BT_ASSERT(fc); + BT_ASSERT(index < fc->options->len); + return &g_array_index(fc->options, struct fs_sink_ctf_named_field_class, + index); +} + +static inline +struct fs_sink_ctf_named_field_class * +fs_sink_ctf_field_class_variant_borrow_option_by_name( + struct fs_sink_ctf_field_class_variant *fc, const char *name) +{ + uint64_t i; + struct fs_sink_ctf_named_field_class *ret_named_fc = NULL; + + BT_ASSERT(fc); + BT_ASSERT(name); + + for (i = 0; i < fc->options->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_variant_borrow_option_by_index( + fc, i); + + if (strcmp(name, named_fc->name->str) == 0) { + ret_named_fc = named_fc; + goto end; + } + } + +end: + return ret_named_fc; +} + +static inline +void fs_sink_ctf_field_class_variant_append_option( + struct fs_sink_ctf_field_class_variant *fc, + const char *name, struct fs_sink_ctf_field_class *option_fc) +{ + struct fs_sink_ctf_named_field_class *named_fc; + + BT_ASSERT(fc); + BT_ASSERT(name); + g_array_set_size(fc->options, fc->options->len + 1); + + named_fc = &g_array_index(fc->options, + struct fs_sink_ctf_named_field_class, fc->options->len - 1); + _fs_sink_ctf_named_field_class_init(named_fc); + g_string_assign(named_fc->name, name); + named_fc->fc = option_fc; +} + +static inline +struct fs_sink_ctf_event_class *fs_sink_ctf_event_class_create( + struct fs_sink_ctf_stream_class *sc, + const bt_event_class *ir_ec) +{ + struct fs_sink_ctf_event_class *ec = + g_new0(struct fs_sink_ctf_event_class, 1); + + BT_ASSERT(sc); + BT_ASSERT(ir_ec); + BT_ASSERT(ec); + ec->ir_ec = ir_ec; + ec->sc = sc; + g_ptr_array_add(sc->event_classes, ec); + g_hash_table_insert(sc->event_classes_from_ir, (gpointer) ir_ec, ec); + return ec; +} + +static inline +void fs_sink_ctf_event_class_destroy(struct fs_sink_ctf_event_class *ec) +{ + if (!ec) { + return; + } + + fs_sink_ctf_field_class_destroy(ec->spec_context_fc); + ec->spec_context_fc = NULL; + fs_sink_ctf_field_class_destroy(ec->payload_fc); + ec->payload_fc = NULL; + g_free(ec); +} + +static inline +struct fs_sink_ctf_stream_class *fs_sink_ctf_stream_class_create( + struct fs_sink_ctf_trace_class *tc, + const bt_stream_class *ir_sc) +{ + struct fs_sink_ctf_stream_class *sc = + g_new0(struct fs_sink_ctf_stream_class, 1); + + BT_ASSERT(tc); + BT_ASSERT(ir_sc); + BT_ASSERT(sc); + sc->tc = tc; + sc->ir_sc = ir_sc; + sc->default_clock_class = + bt_stream_class_borrow_default_clock_class_const(ir_sc); + sc->default_clock_class_name = g_string_new(NULL); + BT_ASSERT(sc->default_clock_class_name); + sc->event_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) fs_sink_ctf_event_class_destroy); + BT_ASSERT(sc->event_classes); + sc->event_classes_from_ir = g_hash_table_new(g_direct_hash, + g_direct_equal); + BT_ASSERT(sc->event_classes_from_ir); + sc->packets_have_ts_begin = + bt_stream_class_packets_have_beginning_default_clock_snapshot( + ir_sc); + sc->packets_have_ts_end = + bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc); + sc->has_discarded_events = + bt_stream_class_supports_discarded_events(ir_sc); + + if (sc->has_discarded_events) { + sc->discarded_events_has_ts = + bt_stream_class_discarded_events_have_default_clock_snapshots( + ir_sc); + } + + if (bt_stream_class_supports_discarded_packets(ir_sc)) { + sc->discarded_packets_has_ts = + bt_stream_class_discarded_packets_have_default_clock_snapshots( + ir_sc); + } + + g_ptr_array_add(tc->stream_classes, sc); + return sc; +} + +static inline +void fs_sink_ctf_stream_class_destroy(struct fs_sink_ctf_stream_class *sc) +{ + if (!sc) { + return; + } + + if (sc->default_clock_class_name) { + g_string_free(sc->default_clock_class_name, TRUE); + sc->default_clock_class_name = NULL; + } + + if (sc->event_classes) { + g_ptr_array_free(sc->event_classes, TRUE); + sc->event_classes = NULL; + } + + if (sc->event_classes_from_ir) { + g_hash_table_destroy(sc->event_classes_from_ir); + sc->event_classes_from_ir = NULL; + } + + fs_sink_ctf_field_class_destroy(sc->packet_context_fc); + sc->packet_context_fc = NULL; + fs_sink_ctf_field_class_destroy(sc->event_common_context_fc); + sc->event_common_context_fc = NULL; + g_free(sc); +} + +static inline +void fs_sink_ctf_stream_class_append_event_class( + struct fs_sink_ctf_stream_class *sc, + struct fs_sink_ctf_event_class *ec) +{ + g_ptr_array_add(sc->event_classes, ec); +} + +static inline +void fs_sink_ctf_trace_class_destroy(struct fs_sink_ctf_trace_class *tc) +{ + if (!tc) { + return; + } + + if (tc->stream_classes) { + g_ptr_array_free(tc->stream_classes, TRUE); + tc->stream_classes = NULL; + } + + g_free(tc); +} + +static inline +struct fs_sink_ctf_trace_class *fs_sink_ctf_trace_class_create( + const bt_trace_class *ir_tc) +{ + struct fs_sink_ctf_trace_class *tc = + g_new0(struct fs_sink_ctf_trace_class, 1); + + BT_ASSERT(tc); + + if (bt_uuid_generate(tc->uuid)) { + fs_sink_ctf_trace_class_destroy(tc); + tc = NULL; + goto end; + } + + tc->ir_tc = ir_tc; + tc->stream_classes = g_ptr_array_new_with_free_func( + (GDestroyNotify) fs_sink_ctf_stream_class_destroy); + BT_ASSERT(tc->stream_classes); + +end: + return tc; +} + +static inline +bool fs_sink_ctf_ist_valid_identifier(const char *name) +{ + const char *at; + uint64_t i; + bool ist_valid = true; + static const char *reserved_keywords[] = { + "align", + "callsite", + "const", + "char", + "clock", + "double", + "enum", + "env", + "event", + "floating_point", + "float", + "integer", + "int", + "long", + "short", + "signed", + "stream", + "string", + "struct", + "trace", + "typealias", + "typedef", + "unsigned", + "variant", + "void", + "_Bool", + "_Complex", + "_Imaginary", + }; + + /* Make sure the name is not a reserved keyword */ + for (i = 0; i < sizeof(reserved_keywords) / sizeof(*reserved_keywords); + i++) { + if (strcmp(name, reserved_keywords[i]) == 0) { + ist_valid = false; + goto end; + } + } + + /* Make sure the name is not an empty string */ + if (strlen(name) == 0) { + ist_valid = false; + goto end; + } + + /* Make sure the name starts with a letter or `_` */ + if (!isalpha(name[0]) && name[0] != '_') { + ist_valid = false; + goto end; + } + + /* Make sure the name only contains letters, digits, and `_` */ + for (at = name; *at != '\0'; at++) { + if (!isalnum(*at) && *at != '_') { + ist_valid = false; + goto end; + } + } + +end: + return ist_valid; +} + +static inline +int fs_sink_ctf_protect_name(GString *name) +{ + int ret = 0; + + if (!fs_sink_ctf_ist_valid_identifier(name->str)) { + ret = -1; + goto end; + } + + /* Prepend `_` to protect it */ + g_string_prepend_c(name, '_'); + +end: + return ret; +} + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H */ diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.c b/src/plugins/ctf/fs-sink/fs-sink-stream.c new file mode 100644 index 00000000..026251a6 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink-stream.c @@ -0,0 +1,660 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-STREAM" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include "ctfser/ctfser.h" +#include "compat/endian.h" + +#include "fs-sink-trace.h" +#include "fs-sink-stream.h" +#include "translate-trace-ir-to-ctf-ir.h" + +BT_HIDDEN +void fs_sink_stream_destroy(struct fs_sink_stream *stream) +{ + if (!stream) { + goto end; + } + + bt_ctfser_fini(&stream->ctfser); + + if (stream->file_name) { + g_string_free(stream->file_name, TRUE); + stream->file_name = NULL; + } + + bt_packet_put_ref(stream->packet_state.packet); + g_free(stream); + +end: + return; +} + +static +bool stream_file_name_exists(struct fs_sink_trace *trace, const char *name) +{ + bool exists = false; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, trace->streams); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct fs_sink_stream *stream = value; + + if (strcmp(name, stream->file_name->str) == 0) { + exists = true; + goto end; + } + } + +end: + return exists; +} + +static +GString *sanitize_stream_file_name(const char *file_name) +{ + GString *san_file_name = g_string_new(NULL); + const char *ch; + gchar *basename; + + BT_ASSERT(san_file_name); + BT_ASSERT(file_name); + basename = g_path_get_basename(file_name); + + for (ch = basename; *ch != '\0'; ch++) { + if (*ch == '/') { + g_string_append_c(san_file_name, '_'); + } else { + g_string_append_c(san_file_name, *ch); + } + } + + /* Do not allow `.` and `..` either */ + if (strcmp(san_file_name->str, ".") == 0 || + strcmp(san_file_name->str, "..") == 0) { + g_string_assign(san_file_name, "stream"); + } + + g_free(basename); + return san_file_name; +} + +static +GString *make_unique_stream_file_name(struct fs_sink_trace *trace, + const char *base) +{ + GString *san_base = sanitize_stream_file_name(base); + GString *name = g_string_new(san_base->str); + unsigned int suffix = 0; + + BT_ASSERT(name); + + while (stream_file_name_exists(trace, name->str) && + strcmp(name->str, "metadata") == 0) { + g_string_printf(name, "%s-%u", san_base->str, suffix); + suffix++; + } + + g_string_free(san_base, TRUE); + return name; +} + +static +void set_stream_file_name(struct fs_sink_stream *stream) +{ + const char *base_name = bt_stream_get_name(stream->ir_stream); + + if (!base_name) { + base_name = "stream"; + } + + BT_ASSERT(!stream->file_name); + stream->file_name = make_unique_stream_file_name(stream->trace, + base_name); +} + +BT_HIDDEN +struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace, + const bt_stream *ir_stream) +{ + struct fs_sink_stream *stream = g_new0(struct fs_sink_stream, 1); + int ret; + GString *path = g_string_new(trace->path->str); + + if (!stream) { + goto end; + } + + stream->trace = trace; + stream->ir_stream = ir_stream; + stream->packet_state.beginning_cs = UINT64_C(-1); + stream->packet_state.end_cs = UINT64_C(-1); + stream->prev_packet_state.end_cs = UINT64_C(-1); + stream->prev_packet_state.discarded_events_counter = UINT64_C(-1); + stream->prev_packet_state.seq_num = UINT64_C(-1); + ret = try_translate_stream_class_trace_ir_to_ctf_ir(trace->tc, + bt_stream_borrow_class_const(ir_stream), &stream->sc); + if (ret) { + goto error; + } + + set_stream_file_name(stream); + g_string_append_printf(path, "/%s", stream->file_name->str); + ret = bt_ctfser_init(&stream->ctfser, path->str); + if (ret) { + goto error; + } + + g_hash_table_insert(trace->streams, (gpointer) ir_stream, stream); + goto end; + +error: + fs_sink_stream_destroy(stream); + stream = NULL; + +end: + if (path) { + g_string_free(path, TRUE); + } + + return stream; +} + +static +int write_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class *fc, const bt_field *field); + +static inline +int write_int_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_int *fc, const bt_field *field) +{ + int ret; + + if (fc->is_signed) { + ret = bt_ctfser_write_signed_int(&stream->ctfser, + bt_field_signed_integer_get_value(field), + fc->base.base.alignment, fc->base.size, BYTE_ORDER); + } else { + ret = bt_ctfser_write_unsigned_int(&stream->ctfser, + bt_field_unsigned_integer_get_value(field), + fc->base.base.alignment, fc->base.size, BYTE_ORDER); + } + + return ret; +} + +static inline +int write_float_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_float *fc, const bt_field *field) +{ + int ret; + double val = bt_field_real_get_value(field); + + if (fc->base.size == 32) { + ret = bt_ctfser_write_float32(&stream->ctfser, val, + fc->base.base.alignment, BYTE_ORDER); + } else { + ret = bt_ctfser_write_float64(&stream->ctfser, val, + fc->base.base.alignment, BYTE_ORDER); + } + + return ret; +} + +static inline +int write_string_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_string *fc, const bt_field *field) +{ + return bt_ctfser_write_string(&stream->ctfser, + bt_field_string_get_value(field)); +} + +static inline +int write_array_field_elements(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_array_base *fc, + const bt_field *field) +{ + uint64_t i; + uint64_t len = bt_field_array_get_length(field); + int ret = 0; + + for (i = 0; i < len; i++) { + const bt_field *elem_field = + bt_field_array_borrow_element_field_by_index_const( + field, i); + ret = write_field(stream, fc->elem_fc, elem_field); + if (unlikely(ret)) { + goto end; + } + } + +end: + return ret; +} + +static inline +int write_sequence_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_sequence *fc, + const bt_field *field) +{ + int ret; + + if (fc->length_is_before) { + ret = bt_ctfser_write_unsigned_int(&stream->ctfser, + bt_field_array_get_length(field), 8, 32, BYTE_ORDER); + if (unlikely(ret)) { + goto end; + } + } + + ret = write_array_field_elements(stream, (void *) fc, field); + +end: + return ret; +} + +static inline +int write_struct_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_struct *fc, + const bt_field *field, bool align_struct) +{ + int ret = 0; + uint64_t i; + + if (likely(align_struct)) { + ret = bt_ctfser_align_offset_in_current_packet(&stream->ctfser, + fc->base.alignment); + if (unlikely(ret)) { + goto end; + } + } + + for (i = 0; i < fc->members->len; i++) { + const bt_field *memb_field = + bt_field_structure_borrow_member_field_by_index_const( + field, i); + struct fs_sink_ctf_field_class *member_fc = + fs_sink_ctf_field_class_struct_borrow_member_by_index( + fc, i)->fc; + + ret = write_field(stream, member_fc, memb_field); + if (unlikely(ret)) { + goto end; + } + } + +end: + return ret; +} + +static inline +int write_variant_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class_variant *fc, + const bt_field *field) +{ + uint64_t opt_index = + bt_field_variant_get_selected_option_field_index(field); + int ret; + + if (fc->tag_is_before) { + ret = bt_ctfser_write_unsigned_int(&stream->ctfser, + opt_index, 8, 16, BYTE_ORDER); + if (unlikely(ret)) { + goto end; + } + } + + ret = write_field(stream, + fs_sink_ctf_field_class_variant_borrow_option_by_index(fc, + opt_index)->fc, + bt_field_variant_borrow_selected_option_field_const(field)); + +end: + return ret; +} + +static +int write_field(struct fs_sink_stream *stream, + struct fs_sink_ctf_field_class *fc, const bt_field *field) +{ + int ret; + + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: + ret = write_int_field(stream, (void *) fc, field); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: + ret = write_float_field(stream, (void *) fc, field); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: + ret = write_string_field(stream, (void *) fc, field); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + ret = write_struct_field(stream, (void *) fc, field, true); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + ret = write_array_field_elements(stream, (void *) fc, field); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + ret = write_sequence_field(stream, (void *) fc, field); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + ret = write_variant_field(stream, (void *) fc, field); + break; + default: + abort(); + } + + return ret; +} + +static inline +int write_event_header(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs, struct fs_sink_ctf_event_class *ec) +{ + int ret; + + /* Event class ID */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + bt_event_class_get_id(ec->ir_ec), 8, 64, BYTE_ORDER); + if (unlikely(ret)) { + goto end; + } + + /* Time */ + if (stream->sc->default_clock_class) { + BT_ASSERT(cs); + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + bt_clock_snapshot_get_value(cs), 8, 64, BYTE_ORDER); + if (unlikely(ret)) { + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +int fs_sink_stream_write_event(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs, const bt_event *event, + struct fs_sink_ctf_event_class *ec) +{ + int ret; + const bt_field *field; + + /* Header */ + ret = write_event_header(stream, cs, ec); + if (unlikely(ret)) { + goto end; + } + + /* Common context */ + if (stream->sc->event_common_context_fc) { + field = bt_event_borrow_common_context_field_const(event); + BT_ASSERT(field); + ret = write_struct_field(stream, + (void *) stream->sc->event_common_context_fc, + field, true); + if (unlikely(ret)) { + goto end; + } + } + + /* Specific context */ + if (ec->spec_context_fc) { + field = bt_event_borrow_specific_context_field_const(event); + BT_ASSERT(field); + ret = write_struct_field(stream, (void *) ec->spec_context_fc, + field, true); + if (unlikely(ret)) { + goto end; + } + } + + /* Specific context */ + if (ec->payload_fc) { + field = bt_event_borrow_payload_field_const(event); + BT_ASSERT(field); + ret = write_struct_field(stream, (void *) ec->payload_fc, + field, true); + if (unlikely(ret)) { + goto end; + } + } + +end: + return ret; +} + +static +int write_packet_context(struct fs_sink_stream *stream) +{ + int ret; + + /* Packet total size */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.total_size, 8, 64, BYTE_ORDER); + if (ret) { + goto end; + } + + /* Packet content size */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.content_size, 8, 64, BYTE_ORDER); + if (ret) { + goto end; + } + + if (stream->sc->packets_have_ts_begin) { + /* Beginning time */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.beginning_cs, 8, 64, BYTE_ORDER); + if (ret) { + goto end; + } + } + + if (stream->sc->packets_have_ts_end) { + /* End time */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.end_cs, 8, 64, BYTE_ORDER); + if (ret) { + goto end; + } + } + + if (stream->sc->has_discarded_events) { + /* Discarded event counter */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.discarded_events_counter, 8, 64, + BYTE_ORDER); + if (ret) { + goto end; + } + } + + /* Sequence number */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + stream->packet_state.seq_num, 8, 64, BYTE_ORDER); + if (ret) { + goto end; + } + + /* Other members */ + if (stream->sc->packet_context_fc) { + const bt_field *packet_context_field = + bt_packet_borrow_context_field_const( + stream->packet_state.packet); + + BT_ASSERT(packet_context_field); + ret = write_struct_field(stream, + (void *) stream->sc->packet_context_fc, + packet_context_field, false); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +int fs_sink_stream_open_packet(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs, const bt_packet *packet) +{ + int ret; + uint64_t i; + + BT_ASSERT(!stream->packet_state.is_open); + bt_packet_put_ref(stream->packet_state.packet); + stream->packet_state.packet = packet; + bt_packet_get_ref(stream->packet_state.packet); + if (cs) { + stream->packet_state.beginning_cs = + bt_clock_snapshot_get_value(cs); + } + + /* Open packet */ + ret = bt_ctfser_open_packet(&stream->ctfser); + if (ret) { + /* bt_ctfser_open_packet() logs errors */ + goto end; + } + + /* Packet header: magic */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + UINT64_C(0xc1fc1fc1), 8, 32, BYTE_ORDER); + if (ret) { + BT_LOGE("Error writing packet header magic: stream-file-name=%s", + stream->file_name->str); + goto end; + } + + /* Packet header: UUID */ + for (i = 0; i < BABELTRACE_UUID_LEN; i++) { + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + (uint64_t) stream->sc->tc->uuid[i], 8, 8, BYTE_ORDER); + if (ret) { + BT_LOGE("Error writing packet header UUID: stream-file-name=%s", + stream->file_name->str); + goto end; + } + } + + /* Packet header: stream class ID */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER); + if (ret) { + BT_LOGE("Error writing packet header stream class id: " + "stream-file-name=%s, stream-class-id=%"PRIu64, + stream->file_name->str, + bt_stream_class_get_id(stream->sc->ir_sc)); + goto end; + } + + /* Packet header: stream ID */ + ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser, + bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER); + if (ret) { + BT_LOGE("Error writing packet header stream id: " + "stream-file-name=%s, stream-id=%"PRIu64, + stream->file_name->str, + bt_stream_get_id(stream->ir_stream)); + goto end; + } + + /* Save packet context's offset to rewrite it later */ + stream->packet_state.context_offset_bits = + bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser); + + /* Write packet context just to advance to content (first event) */ + ret = write_packet_context(stream); + if (ret) { + goto end; + } + + stream->packet_state.is_open = true; + +end: + return ret; +} + +BT_HIDDEN +int fs_sink_stream_close_packet(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs) +{ + int ret; + + BT_ASSERT(stream->packet_state.is_open); + + if (cs) { + stream->packet_state.end_cs = bt_clock_snapshot_get_value(cs); + } + + stream->packet_state.content_size = + bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser); + stream->packet_state.total_size = + (stream->packet_state.content_size + 7) & ~UINT64_C(7); + + /* Rewrite packet context */ + bt_ctfser_set_offset_in_current_packet_bits(&stream->ctfser, + stream->packet_state.context_offset_bits); + ret = write_packet_context(stream); + if (ret) { + goto end; + } + + /* Close packet */ + bt_ctfser_close_current_packet(&stream->ctfser, + stream->packet_state.total_size / 8); + + /* Partially copy current packet state to previous packet state */ + stream->prev_packet_state.end_cs = stream->packet_state.end_cs; + stream->prev_packet_state.discarded_events_counter = + stream->packet_state.discarded_events_counter; + stream->prev_packet_state.seq_num = + stream->packet_state.seq_num; + + /* Reset current packet state */ + stream->packet_state.beginning_cs = UINT64_C(-1); + stream->packet_state.end_cs = UINT64_C(-1); + stream->packet_state.content_size = 0; + stream->packet_state.total_size = 0; + stream->packet_state.seq_num += 1; + stream->packet_state.context_offset_bits = 0; + stream->packet_state.is_open = false; + BT_PACKET_PUT_REF_AND_RESET(stream->packet_state.packet); + +end: + return ret; +} diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.h b/src/plugins/ctf/fs-sink/fs-sink-stream.h new file mode 100644 index 00000000..c3efc9a0 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink-stream.h @@ -0,0 +1,191 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include "ctfser/ctfser.h" +#include +#include +#include + +#include "fs-sink-ctf-meta.h" + +struct fs_sink_trace; + +struct fs_sink_stream { + struct fs_sink_trace *trace; + struct bt_ctfser ctfser; + + /* Stream's file name */ + GString *file_name; + + /* Weak */ + const bt_stream *ir_stream; + + struct fs_sink_ctf_stream_class *sc; + + /* Current packet's state */ + struct { + /* + * True if we're, for this stream, within an opened + * packet (got a packet beginning message, but no + * packet end message yet). + */ + bool is_open; + + /* + * Current beginning default clock snapshot for the + * current packet (`UINT64_C(-1)` if not set). + */ + uint64_t beginning_cs; + + /* + * Current end default clock snapshot for the current + * packet (`UINT64_C(-1)` if not set). + */ + uint64_t end_cs; + + /* + * Current packet's content size (bits) for the current + * packet. + */ + uint64_t content_size; + + /* + * Current packet's total size (bits) for the current + * packet. + */ + uint64_t total_size; + + /* + * Discarded events (free running) counter for the + * current packet. + */ + uint64_t discarded_events_counter; + + /* Sequence number (free running) of the current packet */ + uint64_t seq_num; + + /* + * Offset of the packet context structure within the + * current packet (bits). + */ + uint64_t context_offset_bits; + + /* Owned by this */ + const bt_packet *packet; + } packet_state; + + /* Previous packet's state */ + struct { + /* End default clock snapshot (`UINT64_C(-1)` if not set) */ + uint64_t end_cs; + + /* Discarded events (free running) counter */ + uint64_t discarded_events_counter; + + /* Sequence number (free running) */ + uint64_t seq_num; + } prev_packet_state; + + /* State to handle discarded events */ + struct { + /* + * True if we're in the time range given by a previously + * received discarded events message. In this case, + * `beginning_cs` and `end_cs` below contain the + * beginning and end clock snapshots for this range. + * + * This is used to validate that, when receiving a + * packet end message, the current discarded events time + * range matches what's expected for CTF 1.8, that is: + * + * * Its beginning time is the previous packet's end + * time (or the current packet's beginning time if + * this is the first packet). + * + * * Its end time is the current packet's end time. + */ + bool in_range; + + /* + * Beginning and end times of the time range given by a + * previously received discarded events message. + */ + uint64_t beginning_cs; + uint64_t end_cs; + } discarded_events_state; + + /* State to handle discarded packets */ + struct { + /* + * True if we're in the time range given by a previously + * received discarded packets message. In this case, + * `beginning_cs` and `end_cs` below contain the + * beginning and end clock snapshots for this range. + * + * This is used to validate that, when receiving a + * packet beginning message, the current discarded + * packets time range matches what's expected for CTF + * 1.8, that is: + * + * * Its beginning time is the previous packet's end + * time. + * + * * Its end time is the current packet's beginning + * time. + */ + bool in_range; + + /* + * Beginning and end times of the time range given by a + * previously received discarded packets message. + */ + uint64_t beginning_cs; + uint64_t end_cs; + } discarded_packets_state; +}; + +BT_HIDDEN +struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace, + const bt_stream *ir_stream); + +BT_HIDDEN +void fs_sink_stream_destroy(struct fs_sink_stream *stream); + +BT_HIDDEN +int fs_sink_stream_write_event(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs, const bt_event *event, + struct fs_sink_ctf_event_class *ec); + +BT_HIDDEN +int fs_sink_stream_open_packet(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs, const bt_packet *packet); + +BT_HIDDEN +int fs_sink_stream_close_packet(struct fs_sink_stream *stream, + const bt_clock_snapshot *cs); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H */ diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.c b/src/plugins/ctf/fs-sink/fs-sink-trace.c new file mode 100644 index 00000000..7f5c83a7 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink-trace.c @@ -0,0 +1,610 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include "ctfser/ctfser.h" + +#include "translate-trace-ir-to-ctf-ir.h" +#include "translate-ctf-ir-to-tsdl.h" +#include "fs-sink.h" +#include "fs-sink-trace.h" +#include "fs-sink-stream.h" + +/* + * Sanitizes `path` so as to: + * + * * Replace `.` subdirectories with `_`. + * * Replace `..` subdirectories with `__`. + * * Remove trailing slashes. + */ +static +GString *sanitize_trace_path(const char *path) +{ + GString *san_path = g_string_new(NULL); + const char *ch = path; + bool dir_start = true; + + BT_ASSERT(san_path); + BT_ASSERT(path); + + while (*ch != '\0') { + switch (*ch) { + case '/': + /* Start of directory */ + dir_start = true; + g_string_append_c(san_path, *ch); + ch++; + continue; + case '.': + if (dir_start) { + switch (ch[1]) { + case '\0': + case '/': + /* `.` -> `_` */ + g_string_append_c(san_path, '_'); + ch++; + continue; + case '.': + switch (ch[2]) { + case '\0': + case '/': + /* `..` -> `__` */ + g_string_append(san_path, "__"); + ch += 2; + continue; + default: + break; + } + default: + break; + } + } + default: + break; + } + + /* Not a special character */ + g_string_append_c(san_path, *ch); + ch++; + dir_start = false; + } + + /* Remove trailing slashes */ + while (san_path->len > 0 && + san_path->str[san_path->len - 1] == '/') { + /* Remove trailing slash */ + g_string_set_size(san_path, san_path->len - 1); + } + + if (san_path->len == 0) { + /* Looks like there's nothing left: just use `trace` */ + g_string_assign(san_path, "trace"); + } + + return san_path; +} + +/* + * Find a path based on `path` that doesn't exist yet. First, try `path` + * itself, then try with incrementing suffixes. + */ + +static +GString *make_unique_trace_path(const char *path) +{ + GString *unique_path; + unsigned int suffix = 0; + + unique_path = g_string_new(path); + + while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) { + g_string_printf(unique_path, "%s-%u", path, suffix); + suffix++; + } + + return unique_path; +} + +/* + * Validate that the input string `datetime` is an ISO8601-compliant string (the + * format used by LTTng in the metadata). + */ + +static +int lttng_validate_datetime(const char *datetime) +{ + GTimeVal tv; + int ret = -1; + + /* + * We are using g_time_val_from_iso8601, as the safer/more modern + * alternative, g_date_time_new_from_iso8601, is only available in + * glib >= 2.56, and this is sufficient for our use case of validating + * the format. + */ + if (!g_time_val_from_iso8601(datetime, &tv)) { + BT_LOGD("Couldn't parse datetime as iso8601: date=\"%s\"", datetime); + goto end; + } + + ret = 0; + +end: + return ret; +} + +static +int append_lttng_trace_path_ust_uid(GString *path, const bt_trace_class *tc) +{ + const bt_value *v; + int ret; + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id"); + if (!v || !bt_value_is_signed_integer(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_id\""); + goto error; + } + + g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, + bt_value_signed_integer_get(v)); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "isa_length"); + if (!v || !bt_value_is_signed_integer(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"isa_length\""); + goto error; + } + + g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit", + bt_value_signed_integer_get(v)); + + ret = 0; + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int append_lttng_trace_path_ust_pid(GString *path, const bt_trace_class *tc) +{ + const bt_value *v; + const char *datetime; + int ret; + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "procname"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"procname\""); + goto error; + } + + g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v)); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid"); + if (!v || !bt_value_is_signed_integer(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"vpid\""); + goto error; + } + + g_string_append_printf(path, "-%" PRId64, bt_value_signed_integer_get(v)); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"vpid_datetime\""); + goto error; + } + + datetime = bt_value_string_get(v); + + if (lttng_validate_datetime(datetime)) { + goto error; + } + + g_string_append_printf(path, "-%s", datetime); + + ret = 0; + goto end; + +error: + ret = -1; + +end: + return ret; +} + +/* + * Try to build a trace path based on environment values put in the trace + * environment by the LTTng tracer, starting with version 2.11. + */ +static +GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace) +{ + const bt_trace_class *tc; + const bt_value *v; + const char *tracer_name, *domain, *datetime; + int64_t tracer_major, tracer_minor; + GString *path; + + path = g_string_new(NULL); + if (!path) { + goto error; + } + + tc = bt_trace_borrow_class_const(trace->ir_trace); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_name"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_name\""); + goto error; + } + + tracer_name = bt_value_string_get(v); + + if (!g_str_equal(tracer_name, "lttng-ust") + && !g_str_equal(tracer_name, "lttng-modules")) { + BT_LOGD("Unrecognized tracer name: name=\"%s\"", tracer_name); + goto error; + } + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_major"); + if (!v || !bt_value_is_signed_integer(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_major\""); + goto error; + } + + tracer_major = bt_value_signed_integer_get(v); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_minor"); + if (!v || !bt_value_is_signed_integer(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_minor\""); + goto error; + } + + tracer_minor = bt_value_signed_integer_get(v); + + if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) { + BT_LOGD("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64, + tracer_major, tracer_minor); + goto error; + } + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "hostname"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_hostname\""); + goto error; + } + + g_string_assign(path, bt_value_string_get(v)); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_name"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"trace_name\""); + goto error; + } + + g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v)); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_creation_datetime"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"trace_creation_datetime\""); + goto error; + } + + datetime = bt_value_string_get(v); + + if (lttng_validate_datetime(datetime)) { + goto error; + } + + g_string_append_printf(path, "-%s", datetime); + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "domain"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"domain\""); + goto error; + } + + domain = bt_value_string_get(v); + g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain); + + if (g_str_equal(domain, "ust")) { + const char *tracer_buffering_scheme; + + v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_scheme"); + if (!v || !bt_value_is_string(v)) { + BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\""); + goto error; + } + + tracer_buffering_scheme = bt_value_string_get(v); + g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme); + + if (g_str_equal(tracer_buffering_scheme, "uid")) { + if (append_lttng_trace_path_ust_uid(path, tc)) { + goto error; + } + } else if (g_str_equal(tracer_buffering_scheme, "pid")){ + if (append_lttng_trace_path_ust_pid(path, tc)) { + goto error; + } + } else { + /* Unknown buffering scheme. */ + BT_LOGD("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme); + goto error; + } + } else if (!g_str_equal(domain, "kernel")) { + /* Unknown domain. */ + BT_LOGD("Unknown domain: domain=\"%s\"", domain); + goto error; + } + + goto end; + +error: + if (path) { + g_string_free(path, TRUE); + path = NULL; + } + +end: + return path; +} + +/* Build the relative output path for `trace`. */ + +static +GString *make_trace_path_rel(const struct fs_sink_trace *trace) +{ + GString *path = NULL; + + if (trace->fs_sink->assume_single_trace) { + /* Use output directory directly */ + path = g_string_new(""); + goto end; + } + + /* First, try to build a path using environment fields written by LTTng. */ + path = make_lttng_trace_path_rel(trace); + if (path) { + goto end; + } + + /* Otherwise, use the trace name, if available. */ + const char *trace_name = bt_trace_get_name(trace->ir_trace); + if (trace_name) { + path = g_string_new(trace_name); + goto end; + } + + /* Otherwise, use "trace". */ + path = g_string_new("trace"); + +end: + return path; +} + +/* + * Compute the trace output path for `trace`, rooted at `output_base_directory`. + */ + +static +GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory) +{ + GString *rel_path = NULL; + GString *rel_path_san = NULL; + GString *full_path = NULL; + GString *unique_full_path = NULL; + + rel_path = make_trace_path_rel(trace); + if (!rel_path) { + goto end; + } + + rel_path_san = sanitize_trace_path(rel_path->str); + if (!rel_path_san) { + goto end; + } + + full_path = g_string_new(NULL); + if (!full_path) { + goto end; + } + + g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s", + output_base_directory, rel_path_san->str); + + unique_full_path = make_unique_trace_path(full_path->str); + +end: + if (rel_path) { + g_string_free(rel_path, TRUE); + } + + if (rel_path_san) { + g_string_free(rel_path_san, TRUE); + } + + if (full_path) { + g_string_free(full_path, TRUE); + } + + return unique_full_path; +} + +BT_HIDDEN +void fs_sink_trace_destroy(struct fs_sink_trace *trace) +{ + GString *tsdl = NULL; + FILE *fh = NULL; + size_t len; + + if (!trace) { + goto end; + } + + if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) { + /* + * Remove the destruction listener, otherwise it could + * be called in the future, and its private data is this + * CTF FS sink trace object which won't exist anymore. + */ + (void) bt_trace_remove_destruction_listener(trace->ir_trace, + trace->ir_trace_destruction_listener_id); + trace->ir_trace_destruction_listener_id = UINT64_C(-1); + } + + if (trace->streams) { + g_hash_table_destroy(trace->streams); + trace->streams = NULL; + } + + tsdl = g_string_new(NULL); + BT_ASSERT(tsdl); + translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl); + + BT_ASSERT(trace->metadata_path); + fh = fopen(trace->metadata_path->str, "wb"); + if (!fh) { + BT_LOGF_ERRNO("In trace destruction listener: " + "cannot open metadata file for writing: ", + ": path=\"%s\"", trace->metadata_path->str); + abort(); + } + + len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh); + if (len != tsdl->len) { + BT_LOGF_ERRNO("In trace destruction listener: " + "cannot write metadata file: ", + ": path=\"%s\"", trace->metadata_path->str); + abort(); + } + + if (!trace->fs_sink->quiet) { + printf("Created CTF trace `%s`.\n", trace->path->str); + } + + if (trace->path) { + g_string_free(trace->path, TRUE); + trace->path = NULL; + } + + g_string_free(trace->metadata_path, TRUE); + trace->metadata_path = NULL; + + fs_sink_ctf_trace_class_destroy(trace->tc); + trace->tc = NULL; + g_free(trace); + +end: + if (fh) { + int ret = fclose(fh); + + if (ret != 0) { + BT_LOGW_ERRNO("In trace destruction listener: " + "cannot close metadata file: ", + ": path=\"%s\"", trace->metadata_path->str); + } + } + + if (tsdl) { + g_string_free(tsdl, TRUE); + } + + return; +} + +static +void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data) +{ + struct fs_sink_trace *trace = data; + + /* + * Prevent bt_trace_remove_destruction_listener() from being + * called in fs_sink_trace_destroy(), which is called by + * g_hash_table_remove() below. + */ + trace->ir_trace_destruction_listener_id = UINT64_C(-1); + g_hash_table_remove(trace->fs_sink->traces, ir_trace); +} + +BT_HIDDEN +struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, + const bt_trace *ir_trace) +{ + int ret; + struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1); + bt_trace_status trace_status; + + if (!trace) { + goto end; + } + + trace->fs_sink = fs_sink; + trace->ir_trace = ir_trace; + trace->ir_trace_destruction_listener_id = UINT64_C(-1); + trace->tc = translate_trace_class_trace_ir_to_ctf_ir( + bt_trace_borrow_class_const(ir_trace)); + if (!trace->tc) { + goto error; + } + + trace->path = make_trace_path(trace, fs_sink->output_dir_path->str); + BT_ASSERT(trace->path); + ret = g_mkdir_with_parents(trace->path->str, 0755); + if (ret) { + BT_LOGE_ERRNO("Cannot create directories for trace directory", + ": path=\"%s\"", trace->path->str); + goto error; + } + + trace->metadata_path = g_string_new(trace->path->str); + BT_ASSERT(trace->metadata_path); + g_string_append(trace->metadata_path, "/metadata"); + trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) fs_sink_stream_destroy); + BT_ASSERT(trace->streams); + trace_status = bt_trace_add_destruction_listener(ir_trace, + ir_trace_destruction_listener, trace, + &trace->ir_trace_destruction_listener_id); + if (trace_status) { + goto error; + } + + g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace); + goto end; + +error: + fs_sink_trace_destroy(trace); + trace = NULL; + +end: + return trace; +} diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.h b/src/plugins/ctf/fs-sink/fs-sink-trace.h new file mode 100644 index 00000000..9a8ac525 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink-trace.h @@ -0,0 +1,79 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include "ctfser/ctfser.h" +#include +#include +#include + +#include "fs-sink-ctf-meta.h" + +struct fs_sink_comp; + +struct fs_sink_trace { + struct fs_sink_comp *fs_sink; + + /* Owned by this */ + struct fs_sink_ctf_trace_class *tc; + + /* + * Weak reference: this object does not own it, and `tc` above + * does not own its trace IR trace class either. Instead, we add + * a "trace destruction" listener (in create_trace()) so that + * this object gets destroyed when the trace object is + * destroyed. + * + * Otherwise (with a strong reference), we would keep this trace + * object alive until the upstream message iterator ends. This + * could "leak" resources (memory, file descriptors) associated + * to traces and streams which otherwise would not exist. + */ + const bt_trace *ir_trace; + + uint64_t ir_trace_destruction_listener_id; + + /* Trace's directory */ + GString *path; + + /* `metadata` file path */ + GString *metadata_path; + + /* + * Hash table of `const bt_stream *` (weak) to + * `struct fs_sink_stream *` (owned by hash table). + */ + GHashTable *streams; +}; + +BT_HIDDEN +struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, + const bt_trace *ir_trace); + +BT_HIDDEN +void fs_sink_trace_destroy(struct fs_sink_trace *trace); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H */ diff --git a/src/plugins/ctf/fs-sink/fs-sink.c b/src/plugins/ctf/fs-sink/fs-sink.c new file mode 100644 index 00000000..42d822e1 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink.c @@ -0,0 +1,1031 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include "ctfser/ctfser.h" + +#include "fs-sink.h" +#include "fs-sink-trace.h" +#include "fs-sink-stream.h" +#include "fs-sink-ctf-meta.h" +#include "translate-trace-ir-to-ctf-ir.h" +#include "translate-ctf-ir-to-tsdl.h" + +static +const char * const in_port_name = "in"; + +static +bt_self_component_status ensure_output_dir_exists( + struct fs_sink_comp *fs_sink) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + int ret; + + ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755); + if (ret) { + BT_LOGE_ERRNO("Cannot create directories for output directory", + ": output-dir-path=\"%s\"", + fs_sink->output_dir_path->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static +bt_self_component_status configure_component(struct fs_sink_comp *fs_sink, + const bt_value *params) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_value *value; + + value = bt_value_map_borrow_entry_value_const(params, "path"); + if (!value) { + BT_LOGE_STR("Missing mandatory `path` parameter."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (!bt_value_is_string(value)) { + BT_LOGE_STR("`path` parameter: expecting a string."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + g_string_assign(fs_sink->output_dir_path, + bt_value_string_get(value)); + value = bt_value_map_borrow_entry_value_const(params, + "assume-single-trace"); + if (value) { + if (!bt_value_is_bool(value)) { + BT_LOGE_STR("`assume-single-trace` parameter: expecting a boolean."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + fs_sink->assume_single_trace = (bool) bt_value_bool_get(value); + } + + value = bt_value_map_borrow_entry_value_const(params, + "ignore-discarded-events"); + if (value) { + if (!bt_value_is_bool(value)) { + BT_LOGE_STR("`ignore-discarded-events` parameter: expecting a boolean."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + fs_sink->ignore_discarded_events = + (bool) bt_value_bool_get(value); + } + + value = bt_value_map_borrow_entry_value_const(params, + "ignore-discarded-packets"); + if (value) { + if (!bt_value_is_bool(value)) { + BT_LOGE_STR("`ignore-discarded-packets` parameter: expecting a boolean."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + fs_sink->ignore_discarded_packets = + (bool) bt_value_bool_get(value); + } + + value = bt_value_map_borrow_entry_value_const(params, + "quiet"); + if (value) { + if (!bt_value_is_bool(value)) { + BT_LOGE_STR("`quiet` parameter: expecting a boolean."); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + fs_sink->quiet = (bool) bt_value_bool_get(value); + } + +end: + return status; +} + +static +void destroy_fs_sink_comp(struct fs_sink_comp *fs_sink) +{ + if (!fs_sink) { + goto end; + } + + if (fs_sink->output_dir_path) { + g_string_free(fs_sink->output_dir_path, TRUE); + fs_sink->output_dir_path = NULL; + } + + if (fs_sink->traces) { + g_hash_table_destroy(fs_sink->traces); + fs_sink->traces = NULL; + } + + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_PUT_REF_AND_RESET( + fs_sink->upstream_iter); + g_free(fs_sink); + +end: + return; +} + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_init( + bt_self_component_sink *self_comp, const bt_value *params, + void *init_method_data) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct fs_sink_comp *fs_sink = NULL; + + fs_sink = g_new0(struct fs_sink_comp, 1); + if (!fs_sink) { + BT_LOGE_STR("Failed to allocate one CTF FS sink structure."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + fs_sink->output_dir_path = g_string_new(NULL); + fs_sink->self_comp = self_comp; + status = configure_component(fs_sink, params); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + /* configure_component() logs errors */ + goto end; + } + + if (fs_sink->assume_single_trace && + g_file_test(fs_sink->output_dir_path->str, + G_FILE_TEST_EXISTS)) { + BT_LOGE("Single trace mode, but output path exists: " + "output-path=\"%s\"", fs_sink->output_dir_path->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + status = ensure_output_dir_exists(fs_sink); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + /* ensure_output_dir_exists() logs errors */ + goto end; + } + + fs_sink->traces = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) fs_sink_trace_destroy); + if (!fs_sink->traces) { + BT_LOGE_STR("Failed to allocate one GHashTable."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + status = bt_self_component_sink_add_input_port(self_comp, in_port_name, + NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + bt_self_component_set_data( + bt_self_component_sink_as_self_component(self_comp), fs_sink); + +end: + if (status != BT_SELF_COMPONENT_STATUS_OK) { + destroy_fs_sink_comp(fs_sink); + } + + return status; +} + +static inline +struct fs_sink_stream *borrow_stream(struct fs_sink_comp *fs_sink, + const bt_stream *ir_stream) +{ + const bt_trace *ir_trace = bt_stream_borrow_trace_const(ir_stream); + struct fs_sink_trace *trace; + struct fs_sink_stream *stream = NULL; + + trace = g_hash_table_lookup(fs_sink->traces, ir_trace); + if (unlikely(!trace)) { + if (fs_sink->assume_single_trace && + g_hash_table_size(fs_sink->traces) > 0) { + BT_LOGE("Single trace mode, but getting more than one trace: " + "stream-name=\"%s\"", + bt_stream_get_name(ir_stream)); + goto end; + } + + trace = fs_sink_trace_create(fs_sink, ir_trace); + if (!trace) { + goto end; + } + } + + stream = g_hash_table_lookup(trace->streams, ir_stream); + if (unlikely(!stream)) { + stream = fs_sink_stream_create(trace, ir_stream); + if (!stream) { + goto end; + } + } + +end: + return stream; +} + +static inline +bt_self_component_status handle_event_msg(struct fs_sink_comp *fs_sink, + const bt_message *msg) +{ + int ret; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_event *ir_event = bt_message_event_borrow_event_const(msg); + const bt_stream *ir_stream = bt_event_borrow_stream_const(ir_event); + struct fs_sink_stream *stream; + struct fs_sink_ctf_event_class *ec = NULL; + const bt_clock_snapshot *cs = NULL; + + stream = borrow_stream(fs_sink, ir_stream); + if (unlikely(!stream)) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + ret = try_translate_event_class_trace_ir_to_ctf_ir(stream->sc, + bt_event_borrow_class_const(ir_event), &ec); + if (ret) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + BT_ASSERT(ec); + + if (stream->sc->default_clock_class) { + cs = bt_message_event_borrow_default_clock_snapshot_const( + msg); + } + + ret = fs_sink_stream_write_event(stream, cs, ir_event, ec); + if (unlikely(ret)) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static inline +bt_self_component_status handle_packet_beginning_msg( + struct fs_sink_comp *fs_sink, const bt_message *msg) +{ + int ret; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_packet *ir_packet = + bt_message_packet_beginning_borrow_packet_const(msg); + const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet); + struct fs_sink_stream *stream; + const bt_clock_snapshot *cs = NULL; + + stream = borrow_stream(fs_sink, ir_stream); + if (unlikely(!stream)) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (stream->sc->packets_have_ts_begin) { + cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + } + + /* + * If we previously received a discarded events message with + * a time range, make sure that its beginning time matches what's + * expected for CTF 1.8, that is: + * + * * Its beginning time is the previous packet's end + * time (or the current packet's beginning time if + * this is the first packet). + * + * We check this here instead of in handle_packet_end_msg() + * because we want to catch any incompatible message as early as + * possible to report the error. + * + * Validation of the discarded events message's end time is + * performed in handle_packet_end_msg(). + */ + if (stream->discarded_events_state.in_range) { + uint64_t expected_cs; + + /* + * `stream->discarded_events_state.in_range` is only set + * when the stream class's discarded events have a time + * range. + * + * It is required that the packet beginning and end + * messages for this stream class have times when + * discarded events have a time range. + */ + BT_ASSERT(stream->sc->discarded_events_has_ts); + BT_ASSERT(stream->sc->packets_have_ts_begin); + BT_ASSERT(stream->sc->packets_have_ts_end); + + if (stream->prev_packet_state.end_cs == UINT64_C(-1)) { + /* We're opening the first packet */ + expected_cs = bt_clock_snapshot_get_value(cs); + } else { + expected_cs = stream->prev_packet_state.end_cs; + } + + if (stream->discarded_events_state.beginning_cs != + expected_cs) { + BT_LOGE("Incompatible discarded events message: " + "unexpected beginning time: " + "beginning-cs-val=%" PRIu64 ", " + "expected-beginning-cs-val=%" PRIu64 ", " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + stream->discarded_events_state.beginning_cs, + expected_cs, + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + } + + /* + * If we previously received a discarded packets message with a + * time range, make sure that its beginning and end times match + * what's expected for CTF 1.8, that is: + * + * * Its beginning time is the previous packet's end time. + * + * * Its end time is the current packet's beginning time. + */ + if (stream->discarded_packets_state.in_range) { + uint64_t expected_end_cs; + + /* + * `stream->discarded_packets_state.in_range` is only + * set when the stream class's discarded packets have a + * time range. + * + * It is required that the packet beginning and end + * messages for this stream class have times when + * discarded packets have a time range. + */ + BT_ASSERT(stream->sc->discarded_packets_has_ts); + BT_ASSERT(stream->sc->packets_have_ts_begin); + BT_ASSERT(stream->sc->packets_have_ts_end); + + /* + * It is not supported to have a discarded packets + * message _before_ the first packet: we cannot validate + * that its beginning time is compatible with CTF 1.8 in + * this case. + */ + if (stream->prev_packet_state.end_cs == UINT64_C(-1)) { + BT_LOGE("Incompatible discarded packets message " + "occuring before the stream's first packet: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (stream->discarded_packets_state.beginning_cs != + stream->prev_packet_state.end_cs) { + BT_LOGE("Incompatible discarded packets message: " + "unexpected beginning time: " + "beginning-cs-val=%" PRIu64 ", " + "expected-beginning-cs-val=%" PRIu64 ", " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + stream->discarded_packets_state.beginning_cs, + stream->prev_packet_state.end_cs, + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + expected_end_cs = bt_clock_snapshot_get_value(cs); + + if (stream->discarded_packets_state.end_cs != + expected_end_cs) { + BT_LOGE("Incompatible discarded packets message: " + "unexpected end time: " + "end-cs-val=%" PRIu64 ", " + "expected-end-cs-val=%" PRIu64 ", " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + stream->discarded_packets_state.end_cs, + expected_end_cs, + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + } + + /* + * We're not in a discarded packets time range anymore since we + * require that the discarded packets time ranges go from one + * packet's end time to the next packet's beginning time, and + * we're handling a packet beginning message here. + */ + stream->discarded_packets_state.in_range = false; + + ret = fs_sink_stream_open_packet(stream, cs, ir_packet); + if (ret) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static inline +bt_self_component_status handle_packet_end_msg( + struct fs_sink_comp *fs_sink, const bt_message *msg) +{ + int ret; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_packet *ir_packet = + bt_message_packet_end_borrow_packet_const(msg); + const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet); + struct fs_sink_stream *stream; + const bt_clock_snapshot *cs = NULL; + + stream = borrow_stream(fs_sink, ir_stream); + if (unlikely(!stream)) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (stream->sc->packets_have_ts_end) { + cs = bt_message_packet_end_borrow_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + } + + /* + * If we previously received a discarded events message with + * a time range, make sure that its end time matches what's + * expected for CTF 1.8, that is: + * + * * Its end time is the current packet's end time. + * + * Validation of the discarded events message's beginning time + * is performed in handle_packet_beginning_msg(). + */ + if (stream->discarded_events_state.in_range) { + uint64_t expected_cs; + + /* + * `stream->discarded_events_state.in_range` is only set + * when the stream class's discarded events have a time + * range. + * + * It is required that the packet beginning and end + * messages for this stream class have times when + * discarded events have a time range. + */ + BT_ASSERT(stream->sc->discarded_events_has_ts); + BT_ASSERT(stream->sc->packets_have_ts_begin); + BT_ASSERT(stream->sc->packets_have_ts_end); + + expected_cs = bt_clock_snapshot_get_value(cs); + + if (stream->discarded_events_state.end_cs != expected_cs) { + BT_LOGE("Incompatible discarded events message: " + "unexpected end time: " + "end-cs-val=%" PRIu64 ", " + "expected-end-cs-val=%" PRIu64 ", " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + stream->discarded_events_state.end_cs, + expected_cs, + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + } + + ret = fs_sink_stream_close_packet(stream, cs); + if (ret) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + /* + * We're not in a discarded events time range anymore since we + * require that the discarded events time ranges go from one + * packet's end time to the next packet's end time, and we're + * handling a packet end message here. + */ + stream->discarded_events_state.in_range = false; + +end: + return status; +} + +static inline +bt_self_component_status handle_stream_beginning_msg( + struct fs_sink_comp *fs_sink, const bt_message *msg) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_stream *ir_stream = + bt_message_stream_beginning_borrow_stream_const(msg); + const bt_stream_class *ir_sc = + bt_stream_borrow_class_const(ir_stream); + struct fs_sink_stream *stream; + bool packets_have_beginning_end_cs = + bt_stream_class_packets_have_beginning_default_clock_snapshot(ir_sc) && + bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc); + + /* + * Not supported: discarded events with default clock snapshots, + * but packet beginning/end without default clock snapshot. + */ + if (!fs_sink->ignore_discarded_events && + bt_stream_class_discarded_events_have_default_clock_snapshots(ir_sc) && + !packets_have_beginning_end_cs) { + BT_LOGE("Unsupported stream: discarded events have " + "default clock snapshots, but packets have no " + "beginning and/or end default clock snapshots: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + ir_stream, bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + /* + * Not supported: discarded packets with default clock + * snapshots, but packet beginning/end without default clock + * snapshot. + */ + if (!fs_sink->ignore_discarded_packets && + bt_stream_class_discarded_packets_have_default_clock_snapshots(ir_sc) && + !packets_have_beginning_end_cs) { + BT_LOGE("Unsupported stream: discarded packets have " + "default clock snapshots, but packets have no " + "beginning and/or end default clock snapshots: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + ir_stream, bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + stream = borrow_stream(fs_sink, ir_stream); + if (!stream) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + BT_LOGI("Created new, empty stream file: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream), + bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + +end: + return status; +} + +static inline +bt_self_component_status handle_stream_end_msg(struct fs_sink_comp *fs_sink, + const bt_message *msg) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_stream *ir_stream = + bt_message_stream_end_borrow_stream_const(msg); + struct fs_sink_stream *stream; + + stream = borrow_stream(fs_sink, ir_stream); + if (!stream) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + BT_LOGI("Closing stream file: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream), + bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + + /* + * This destroys the stream object and frees all its resources, + * closing the stream file. + */ + g_hash_table_remove(stream->trace->streams, ir_stream); + +end: + return status; +} + +static inline +bt_self_component_status handle_discarded_events_msg( + struct fs_sink_comp *fs_sink, const bt_message *msg) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_stream *ir_stream = + bt_message_discarded_events_borrow_stream_const(msg); + struct fs_sink_stream *stream; + const bt_clock_snapshot *cs = NULL; + bt_property_availability avail; + uint64_t count; + + stream = borrow_stream(fs_sink, ir_stream); + if (!stream) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (fs_sink->ignore_discarded_events) { + BT_LOGI("Ignoring discarded events message: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + goto end; + } + + if (stream->discarded_events_state.in_range) { + BT_LOGE("Unsupported contiguous discarded events message: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + /* + * If we're currently in an opened packet (got a packet + * beginning message, but no packet end message yet), we do not + * support having a discarded events message with a time range + * because we require that the discarded events message's time + * range go from a packet's end time to the next packet's end + * time. + */ + if (stream->packet_state.is_open && + stream->sc->discarded_events_has_ts) { + BT_LOGE("Unsupported discarded events message with " + "default clock snapshots occuring within a packet: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (stream->sc->discarded_events_has_ts) { + /* + * Make the stream's state be in the time range of a + * discarded events message since we have the message's + * time range (`stream->sc->discarded_events_has_ts`). + */ + stream->discarded_events_state.in_range = true; + + /* + * The clock snapshot values will be validated when + * handling the next packet beginning and end messages + * (next calls to handle_packet_beginning_msg() and + * handle_packet_end_msg()). + */ + cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + stream->discarded_events_state.beginning_cs = + bt_clock_snapshot_get_value(cs); + cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + stream->discarded_events_state.end_cs = bt_clock_snapshot_get_value(cs); + } + + avail = bt_message_discarded_events_get_count(msg, &count); + if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) { + /* + * There's no specific count of discarded events: set it + * to 1 so that we know that we at least discarded + * something. + */ + count = 1; + } + + stream->packet_state.discarded_events_counter += count; + +end: + return status; +} + +static inline +bt_self_component_status handle_discarded_packets_msg( + struct fs_sink_comp *fs_sink, const bt_message *msg) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + const bt_stream *ir_stream = + bt_message_discarded_packets_borrow_stream_const(msg); + struct fs_sink_stream *stream; + const bt_clock_snapshot *cs = NULL; + bt_property_availability avail; + uint64_t count; + + stream = borrow_stream(fs_sink, ir_stream); + if (!stream) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + if (fs_sink->ignore_discarded_packets) { + BT_LOGI("Ignoring discarded packets message: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + goto end; + } + + if (stream->discarded_packets_state.in_range) { + BT_LOGE("Unsupported contiguous discarded packets message: " + "stream-id=%" PRIu64 ", stream-name=\"%s\", " + "trace-name=\"%s\", path=\"%s/%s\"", + bt_stream_get_id(ir_stream), + bt_stream_get_name(ir_stream), + bt_trace_get_name( + bt_stream_borrow_trace_const(ir_stream)), + stream->trace->path->str, stream->file_name->str); + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + /* + * Discarded packets messages are guaranteed to occur between + * packets. + */ + BT_ASSERT(!stream->packet_state.is_open); + + if (stream->sc->discarded_packets_has_ts) { + /* + * Make the stream's state be in the time range of a + * discarded packets message since we have the message's + * time range (`stream->sc->discarded_packets_has_ts`). + */ + stream->discarded_packets_state.in_range = true; + + /* + * The clock snapshot values will be validated when + * handling the next packet beginning message (next call + * to handle_packet_beginning_msg()). + */ + cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + stream->discarded_packets_state.beginning_cs = + bt_clock_snapshot_get_value(cs); + cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + msg); + BT_ASSERT(cs); + stream->discarded_packets_state.end_cs = + bt_clock_snapshot_get_value(cs); + } + + avail = bt_message_discarded_packets_get_count(msg, &count); + if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) { + /* + * There's no specific count of discarded packets: set + * it to 1 so that we know that we at least discarded + * something. + */ + count = 1; + } + + stream->packet_state.seq_num += count; + +end: + return status; +} + +static inline +void put_messages(bt_message_array_const msgs, uint64_t count) +{ + uint64_t i; + + for (i = 0; i < count; i++) { + BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); + } +} + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_consume(bt_self_component_sink *self_comp) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct fs_sink_comp *fs_sink; + bt_message_iterator_status it_status; + uint64_t msg_count = 0; + bt_message_array_const msgs; + + fs_sink = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + BT_ASSERT(fs_sink); + BT_ASSERT(fs_sink->upstream_iter); + + /* Consume messages */ + it_status = bt_self_component_port_input_message_iterator_next( + fs_sink->upstream_iter, &msgs, &msg_count); + if (it_status < 0) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + switch (it_status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + { + uint64_t i; + + for (i = 0; i < msg_count; i++) { + const bt_message *msg = msgs[i]; + + BT_ASSERT(msg); + + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_EVENT: + status = handle_event_msg(fs_sink, msg); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + status = handle_packet_beginning_msg( + fs_sink, msg); + break; + case BT_MESSAGE_TYPE_PACKET_END: + status = handle_packet_end_msg( + fs_sink, msg); + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + /* Ignore */ + BT_LOGD_STR("Ignoring message iterator inactivity message."); + break; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + status = handle_stream_beginning_msg( + fs_sink, msg); + break; + case BT_MESSAGE_TYPE_STREAM_END: + status = handle_stream_end_msg( + fs_sink, msg); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + /* Not supported by CTF 1.8 */ + BT_LOGD_STR("Ignoring stream activity message."); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + status = handle_discarded_events_msg( + fs_sink, msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + status = handle_discarded_packets_msg( + fs_sink, msg); + break; + default: + abort(); + } + + BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); + + if (status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGE("Failed to handle message: " + "generated CTF traces could be incomplete: " + "output-dir-path=\"%s\"", + fs_sink->output_dir_path->str); + goto error; + } + } + + break; + } + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + status = BT_SELF_COMPONENT_STATUS_AGAIN; + break; + case BT_MESSAGE_ITERATOR_STATUS_END: + /* TODO: Finalize all traces (should already be done?) */ + status = BT_SELF_COMPONENT_STATUS_END; + break; + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + status = BT_SELF_COMPONENT_STATUS_NOMEM; + break; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + status = BT_SELF_COMPONENT_STATUS_NOMEM; + break; + default: + break; + } + + goto end; + +error: + BT_ASSERT(status != BT_SELF_COMPONENT_STATUS_OK); + put_messages(msgs, msg_count); + +end: + return status; +} + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_graph_is_configured( + bt_self_component_sink *self_comp) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct fs_sink_comp *fs_sink = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + fs_sink->upstream_iter = + bt_self_component_port_input_message_iterator_create( + bt_self_component_sink_borrow_input_port_by_name( + self_comp, in_port_name)); + if (!fs_sink->upstream_iter) { + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + +end: + return status; +} + +BT_HIDDEN +void ctf_fs_sink_finalize(bt_self_component_sink *self_comp) +{ + struct fs_sink_comp *fs_sink = bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_comp)); + + destroy_fs_sink_comp(fs_sink); +} diff --git a/src/plugins/ctf/fs-sink/fs-sink.h b/src/plugins/ctf/fs-sink/fs-sink.h new file mode 100644 index 00000000..7a740bc2 --- /dev/null +++ b/src/plugins/ctf/fs-sink/fs-sink.h @@ -0,0 +1,84 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include +#include +#include + +struct fs_sink_comp { + bt_self_component_sink *self_comp; + + /* Owned by this */ + bt_self_component_port_input_message_iterator *upstream_iter; + + /* Base output directory path */ + GString *output_dir_path; + + /* + * True if the component assumes that it will only write a + * single CTF trace (which can contain one or more data + * streams). This makes the component write the stream files + * directly in the output directory (`output_dir_path` above). + */ + bool assume_single_trace; + + /* True to completely ignore discarded events messages */ + bool ignore_discarded_events; + + /* True to completely ignore discarded packets messages */ + bool ignore_discarded_packets; + + /* + * True to make the component quiet (nothing printed to the + * standard output). + */ + bool quiet; + + /* + * Hash table of `const bt_trace *` (weak) to + * `struct fs_sink_trace *` (owned by hash table). + */ + GHashTable *traces; +}; + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_init( + bt_self_component_sink *component, + const bt_value *params, + void *init_method_data); + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_consume( + bt_self_component_sink *component); + +BT_HIDDEN +bt_self_component_status ctf_fs_sink_graph_is_configured( + bt_self_component_sink *component); + +BT_HIDDEN +void ctf_fs_sink_finalize(bt_self_component_sink *component); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H */ diff --git a/src/plugins/ctf/fs-sink/logging.c b/src/plugins/ctf/fs-sink/logging.c new file mode 100644 index 00000000..0469b7a7 --- /dev/null +++ b/src/plugins/ctf/fs-sink/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_fs_sink_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_fs_sink_log_level, + "BABELTRACE_SINK_CTF_FS_LOG_LEVEL"); diff --git a/src/plugins/ctf/fs-sink/logging.h b/src/plugins/ctf/fs-sink/logging.h new file mode 100644 index 00000000..a6c9dd4d --- /dev/null +++ b/src/plugins/ctf/fs-sink/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_FS_SINK_LOGGING_H +#define PLUGINS_FS_SINK_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_fs_sink_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_fs_sink_log_level); + +#endif /* PLUGINS_FS_SINK_LOGGING_H */ diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c new file mode 100644 index 00000000..3b6d2bc9 --- /dev/null +++ b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c @@ -0,0 +1,907 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-CTF-IR-TO-TSDL" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include +#include +#include +#include +#include "common/assert.h" +#include "compat/endian.h" + +#include "fs-sink-ctf-meta.h" + +struct ctx { + unsigned int indent_level; + GString *tsdl; +}; + +static inline +void append_indent(struct ctx *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->indent_level; i++) { + g_string_append_c(ctx->tsdl, '\t'); + } +} + +static +void append_uuid(struct ctx *ctx, bt_uuid uuid) +{ + g_string_append_printf(ctx->tsdl, + "\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + (unsigned int) uuid[0], + (unsigned int) uuid[1], + (unsigned int) uuid[2], + (unsigned int) uuid[3], + (unsigned int) uuid[4], + (unsigned int) uuid[5], + (unsigned int) uuid[6], + (unsigned int) uuid[7], + (unsigned int) uuid[8], + (unsigned int) uuid[9], + (unsigned int) uuid[10], + (unsigned int) uuid[11], + (unsigned int) uuid[12], + (unsigned int) uuid[13], + (unsigned int) uuid[14], + (unsigned int) uuid[15]); +} + +static +void append_quoted_string_content(struct ctx *ctx, const char *str) +{ + const char *ch; + + for (ch = str; *ch != '\0'; ch++) { + unsigned char uch = (unsigned char) *ch; + + if (uch < 32 || uch >= 127) { + switch (*ch) { + case '\a': + g_string_append(ctx->tsdl, "\\a"); + break; + case '\b': + g_string_append(ctx->tsdl, "\\b"); + break; + case '\f': + g_string_append(ctx->tsdl, "\\f"); + break; + case '\n': + g_string_append(ctx->tsdl, "\\n"); + break; + case '\r': + g_string_append(ctx->tsdl, "\\r"); + break; + case '\t': + g_string_append(ctx->tsdl, "\\t"); + break; + case '\v': + g_string_append(ctx->tsdl, "\\v"); + break; + default: + g_string_append_printf(ctx->tsdl, "\\x%02x", + (unsigned int) uch); + break; + } + } else if (*ch == '"' || *ch == '\\') { + g_string_append_c(ctx->tsdl, '\\'); + g_string_append_c(ctx->tsdl, *ch); + } else { + g_string_append_c(ctx->tsdl, *ch); + } + } +} + +static +void append_quoted_string(struct ctx *ctx, const char *str) +{ + g_string_append_c(ctx->tsdl, '"'); + append_quoted_string_content(ctx, str); + g_string_append_c(ctx->tsdl, '"'); +} + +static +void append_integer_field_class_from_props(struct ctx *ctx, unsigned int size, + unsigned int alignment, bool is_signed, + bt_field_class_integer_preferred_display_base disp_base, + const char *mapped_clock_class_name, const char *field_name, + bool end) +{ + g_string_append_printf(ctx->tsdl, + "integer { size = %u; align = %u;", + size, alignment); + + if (is_signed) { + g_string_append(ctx->tsdl, " signed = true;"); + } + + if (disp_base != BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL) { + g_string_append(ctx->tsdl, " base = "); + + switch (disp_base) { + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: + g_string_append(ctx->tsdl, "b"); + break; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: + g_string_append(ctx->tsdl, "o"); + break; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: + g_string_append(ctx->tsdl, "x"); + break; + default: + abort(); + } + + g_string_append_c(ctx->tsdl, ';'); + } + + if (mapped_clock_class_name) { + g_string_append_printf(ctx->tsdl, " map = clock.%s.value;", + mapped_clock_class_name); + } + + g_string_append(ctx->tsdl, " }"); + + if (field_name) { + g_string_append_printf(ctx->tsdl, " %s", field_name); + } + + if (end) { + g_string_append(ctx->tsdl, ";\n"); + } +} + +static +void append_end_block(struct ctx *ctx) +{ + ctx->indent_level--; + append_indent(ctx); + g_string_append(ctx->tsdl, "}"); +} + +static +void append_end_block_semi_nl(struct ctx *ctx) +{ + ctx->indent_level--; + append_indent(ctx); + g_string_append(ctx->tsdl, "};\n"); +} + +static +void append_end_block_semi_nl_nl(struct ctx *ctx) +{ + append_end_block_semi_nl(ctx); + g_string_append_c(ctx->tsdl, '\n'); +} + +static +void append_integer_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class_int *fc) +{ + const bt_field_class *ir_fc = fc->base.base.ir_fc; + bt_field_class_type type = bt_field_class_get_type(ir_fc); + bool is_signed = type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION || + type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER; + + if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || + type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { + g_string_append(ctx->tsdl, "enum : "); + } + + append_integer_field_class_from_props(ctx, fc->base.size, + fc->base.base.alignment, is_signed, + bt_field_class_integer_get_preferred_display_base(ir_fc), + NULL, NULL, false); + + if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION || + type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { + uint64_t i; + + g_string_append(ctx->tsdl, " {\n"); + ctx->indent_level++; + + for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_fc); i++) { + const char *label; + const bt_field_class_enumeration_mapping *mapping; + const bt_field_class_unsigned_enumeration_mapping *u_mapping; + const bt_field_class_signed_enumeration_mapping *i_mapping; + uint64_t range_count; + uint64_t range_i; + + if (is_signed) { + i_mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const( + ir_fc, i); + mapping = bt_field_class_signed_enumeration_mapping_as_mapping_const( + i_mapping); + } else { + u_mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( + ir_fc, i); + mapping = bt_field_class_unsigned_enumeration_mapping_as_mapping_const( + u_mapping); + } + + label = bt_field_class_enumeration_mapping_get_label( + mapping); + range_count = + bt_field_class_enumeration_mapping_get_range_count( + mapping); + + for (range_i = 0; range_i < range_count; range_i++) { + append_indent(ctx); + + /* + * Systematically prepend `_` to the + * mapping's label as this could be used + * as the tag of a subsequent variant + * field class and variant FC option + * names are systematically protected + * with a leading `_`. + * + * FIXME: This is temporary as the + * library's API should change to + * decouple variant FC option names from + * selector FC labels. The current + * drawback is that an original label + * `HELLO` becomes `_HELLO` in the + * generated metadata, therefore tools + * expecting `HELLO` could fail. + */ + g_string_append(ctx->tsdl, "\"_"); + append_quoted_string_content(ctx, label); + g_string_append(ctx->tsdl, "\" = "); + + if (is_signed) { + int64_t lower, upper; + + bt_field_class_signed_enumeration_mapping_get_range_by_index( + i_mapping, range_i, + &lower, &upper); + + if (lower == upper) { + g_string_append_printf( + ctx->tsdl, "%" PRId64, + lower); + } else { + g_string_append_printf( + ctx->tsdl, "%" PRId64 " ... %" PRId64, + lower, upper); + } + } else { + uint64_t lower, upper; + + bt_field_class_unsigned_enumeration_mapping_get_range_by_index( + u_mapping, range_i, + &lower, &upper); + + if (lower == upper) { + g_string_append_printf( + ctx->tsdl, "%" PRIu64, + lower); + } else { + g_string_append_printf( + ctx->tsdl, "%" PRIu64 " ... %" PRIu64, + lower, upper); + } + } + + g_string_append(ctx->tsdl, ",\n"); + } + } + + append_end_block(ctx); + } +} + +static +void append_float_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class_float *fc) +{ + unsigned int mant_dig, exp_dig; + + if (bt_field_class_real_is_single_precision(fc->base.base.ir_fc)) { + mant_dig = 24; + exp_dig = 8; + } else { + mant_dig = 53; + exp_dig = 11; + } + + g_string_append_printf(ctx->tsdl, + "floating_point { mant_dig = %u; exp_dig = %u; align = %u; }", + mant_dig, exp_dig, fc->base.base.alignment); +} + +static +void append_string_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class_float *fc) +{ + g_string_append(ctx->tsdl, "string { encoding = UTF8; }"); +} + +static +void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc); + +static +void append_member(struct ctx *ctx, const char *name, + struct fs_sink_ctf_field_class *fc) +{ + GString *lengths = NULL; + const char *lengths_str = ""; + + while (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY || + fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { + if (!lengths) { + lengths = g_string_new(NULL); + BT_ASSERT(lengths); + } + + if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY) { + struct fs_sink_ctf_field_class_array *array_fc = + (void *) fc; + + g_string_append_printf(lengths, "[%" PRIu64 "]", + array_fc->length); + fc = array_fc->base.elem_fc; + } else { + struct fs_sink_ctf_field_class_sequence *seq_fc = + (void *) fc; + + g_string_append_printf(lengths, "[%s]", + seq_fc->length_ref->str); + fc = seq_fc->base.elem_fc; + } + } + + append_field_class(ctx, fc); + + if (lengths) { + lengths_str = lengths->str; + } + + g_string_append_printf(ctx->tsdl, " %s%s;\n", name, lengths_str); + + if (lengths) { + g_string_free(lengths, TRUE); + } +} + +static +void append_struct_field_class_members(struct ctx *ctx, + struct fs_sink_ctf_field_class_struct *struct_fc) +{ + uint64_t i; + + for (i = 0; i < struct_fc->members->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + struct fs_sink_ctf_field_class *fc = named_fc->fc; + + if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { + struct fs_sink_ctf_field_class_sequence *seq_fc = + (void *) fc; + + if (seq_fc->length_is_before) { + append_indent(ctx); + append_integer_field_class_from_props(ctx, + 32, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, seq_fc->length_ref->str, true); + } + } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT) { + struct fs_sink_ctf_field_class_variant *var_fc = + (void *) fc; + + if (var_fc->tag_is_before) { + append_indent(ctx); + g_string_append(ctx->tsdl, "enum : "); + append_integer_field_class_from_props(ctx, + 16, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, NULL, false); + g_string_append(ctx->tsdl, " {\n"); + ctx->indent_level++; + + for (i = 0; i < var_fc->options->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + append_indent(ctx); + g_string_append_printf(ctx->tsdl, + "\"%s\" = %" PRIu64 ",\n", + named_fc->name->str, i); + } + + append_end_block(ctx); + g_string_append_printf(ctx->tsdl, " %s;\n", + var_fc->tag_ref->str); + } + } + + append_indent(ctx); + append_member(ctx, named_fc->name->str, fc); + } +} + +static +void append_struct_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class_struct *fc) +{ + g_string_append(ctx->tsdl, "struct {\n"); + ctx->indent_level++; + append_struct_field_class_members(ctx, fc); + append_end_block(ctx); + g_string_append_printf(ctx->tsdl, " align(%u)", + fc->base.alignment); +} + +static +void append_variant_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class_variant *var_fc) +{ + uint64_t i; + + g_string_append_printf(ctx->tsdl, "variant <%s> {\n", + var_fc->tag_ref->str); + ctx->indent_level++; + + for (i = 0; i < var_fc->options->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + + append_indent(ctx); + append_member(ctx, named_fc->name->str, named_fc->fc); + } + + append_end_block(ctx); +} + +static +void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc) +{ + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_INT: + append_integer_field_class(ctx, (void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT: + append_float_field_class(ctx, (void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING: + append_string_field_class(ctx, (void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + append_struct_field_class(ctx, (void *) fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + append_variant_field_class(ctx, (void *) fc); + break; + default: + abort(); + } +} + +static +void append_event_class(struct ctx *ctx, struct fs_sink_ctf_event_class *ec) +{ + const char *str; + bt_event_class_log_level log_level; + + /* Event class */ + append_indent(ctx); + g_string_append(ctx->tsdl, "event {\n"); + ctx->indent_level++; + + /* Event class properties */ + append_indent(ctx); + g_string_append(ctx->tsdl, "name = "); + str = bt_event_class_get_name(ec->ir_ec); + if (!str) { + str = "unknown"; + } + + append_quoted_string(ctx, str); + g_string_append(ctx->tsdl, ";\n"); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "stream_id = %" PRIu64 ";\n", + bt_stream_class_get_id(ec->sc->ir_sc)); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n", + bt_event_class_get_id(ec->ir_ec)); + + str = bt_event_class_get_emf_uri(ec->ir_ec); + if (str) { + append_indent(ctx); + g_string_append(ctx->tsdl, "model.emf.uri = "); + append_quoted_string(ctx, str); + g_string_append(ctx->tsdl, ";\n"); + } + + if (bt_event_class_get_log_level(ec->ir_ec, &log_level) == + BT_PROPERTY_AVAILABILITY_AVAILABLE) { + unsigned int level; + + append_indent(ctx); + g_string_append(ctx->tsdl, "loglevel = "); + + switch (log_level) { + case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY: + level = 0; + break; + case BT_EVENT_CLASS_LOG_LEVEL_ALERT: + level = 1; + break; + case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL: + level = 2; + break; + case BT_EVENT_CLASS_LOG_LEVEL_ERROR: + level = 3; + break; + case BT_EVENT_CLASS_LOG_LEVEL_WARNING: + level = 4; + break; + case BT_EVENT_CLASS_LOG_LEVEL_NOTICE: + level = 5; + break; + case BT_EVENT_CLASS_LOG_LEVEL_INFO: + level = 6; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM: + level = 7; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM: + level = 8; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS: + level = 9; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE: + level = 10; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT: + level = 11; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION: + level = 12; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE: + level = 13; + break; + case BT_EVENT_CLASS_LOG_LEVEL_DEBUG: + level = 14; + break; + default: + abort(); + } + + g_string_append_printf(ctx->tsdl, "%u;\n", level); + } + + /* Event specific context field class */ + if (ec->spec_context_fc) { + append_indent(ctx); + g_string_append(ctx->tsdl, "context := "); + append_field_class(ctx, ec->spec_context_fc); + g_string_append(ctx->tsdl, ";\n"); + } + + /* Event payload field class */ + if (ec->payload_fc) { + append_indent(ctx); + g_string_append(ctx->tsdl, "fields := "); + append_field_class(ctx, ec->payload_fc); + g_string_append(ctx->tsdl, ";\n"); + } + + append_end_block_semi_nl_nl(ctx); +} + +static +void append_stream_class(struct ctx *ctx, + struct fs_sink_ctf_stream_class *sc) +{ + uint64_t i; + + /* Default clock class */ + if (sc->default_clock_class) { + const char *descr; + int64_t offset_seconds; + uint64_t offset_cycles; + bt_uuid uuid; + + append_indent(ctx); + g_string_append(ctx->tsdl, "clock {\n"); + ctx->indent_level++; + BT_ASSERT(sc->default_clock_class_name->len > 0); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "name = %s;\n", + sc->default_clock_class_name->str); + descr = bt_clock_class_get_description(sc->default_clock_class); + if (descr) { + append_indent(ctx); + g_string_append(ctx->tsdl, "description = "); + append_quoted_string(ctx, descr); + g_string_append(ctx->tsdl, ";\n"); + } + + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "freq = %" PRIu64 ";\n", + bt_clock_class_get_frequency(sc->default_clock_class)); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "precision = %" PRIu64 ";\n", + bt_clock_class_get_precision(sc->default_clock_class)); + bt_clock_class_get_offset(sc->default_clock_class, + &offset_seconds, &offset_cycles); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "offset_s = %" PRId64 ";\n", + offset_seconds); + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "offset = %" PRIu64 ";\n", + offset_cycles); + append_indent(ctx); + g_string_append(ctx->tsdl, "absolute = "); + + if (bt_clock_class_origin_is_unix_epoch( + sc->default_clock_class)) { + g_string_append(ctx->tsdl, "true"); + } else { + g_string_append(ctx->tsdl, "false"); + } + + g_string_append(ctx->tsdl, ";\n"); + uuid = bt_clock_class_get_uuid(sc->default_clock_class); + if (uuid) { + append_indent(ctx); + g_string_append(ctx->tsdl, "uuid = "); + append_uuid(ctx, uuid); + g_string_append(ctx->tsdl, ";\n"); + } + + /* End clock class */ + append_end_block_semi_nl_nl(ctx); + } + + /* Stream class */ + append_indent(ctx); + g_string_append(ctx->tsdl, "stream {\n"); + ctx->indent_level++; + + /* Stream class properties */ + append_indent(ctx); + g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n", + bt_stream_class_get_id(sc->ir_sc)); + + /* Packet context field class */ + append_indent(ctx); + g_string_append(ctx->tsdl, "packet.context := struct {\n"); + ctx->indent_level++; + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "packet_size", true); + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "content_size", true); + + if (sc->packets_have_ts_begin) { + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + sc->default_clock_class_name->str, + "timestamp_begin", true); + } + + if (sc->packets_have_ts_end) { + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + sc->default_clock_class_name->str, + "timestamp_end", true); + } + + if (sc->has_discarded_events) { + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "events_discarded", true); + } + + /* + * Unconditionnally write the packet sequence number as, even if + * there's no possible discarded packets message, it's still + * useful information to have. + */ + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "packet_seq_num", true); + + if (sc->packet_context_fc) { + append_struct_field_class_members(ctx, + (void *) sc->packet_context_fc); + fs_sink_ctf_field_class_struct_align_at_least( + (void *) sc->packet_context_fc, 8); + } + + /* End packet context field class */ + append_end_block(ctx); + g_string_append_printf(ctx->tsdl, " align(%u);\n\n", + sc->packet_context_fc ? sc->packet_context_fc->alignment : 8); + + /* Event header field class */ + append_indent(ctx); + g_string_append(ctx->tsdl, "event.header := struct {\n"); + ctx->indent_level++; + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "id", true); + + if (sc->default_clock_class) { + append_indent(ctx); + append_integer_field_class_from_props(ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + sc->default_clock_class_name->str, + "timestamp", true); + } + + /* End event header field class */ + append_end_block(ctx); + g_string_append(ctx->tsdl, " align(8);\n"); + + /* Event common context field class */ + if (sc->event_common_context_fc) { + append_indent(ctx); + g_string_append(ctx->tsdl, "event.context := "); + append_field_class(ctx, + (void *) sc->event_common_context_fc); + g_string_append(ctx->tsdl, ";\n"); + } + + /* End stream class */ + append_end_block_semi_nl_nl(ctx); + + /* Event classes */ + for (i = 0; i < sc->event_classes->len; i++) { + append_event_class(ctx, sc->event_classes->pdata[i]); + } +} + +BT_HIDDEN +void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc, + GString *tsdl) +{ + struct ctx ctx = { + .indent_level = 0, + .tsdl = tsdl, + }; + uint64_t i; + uint64_t count; + + g_string_assign(tsdl, "/* CTF 1.8 */\n\n"); + g_string_append(tsdl, "/* This was generated by a Babeltrace `sink.ctf.fs` component. */\n\n"); + + /* Trace class */ + append_indent(&ctx); + g_string_append(tsdl, "trace {\n"); + ctx.indent_level++; + + /* Trace class properties */ + append_indent(&ctx); + g_string_append(tsdl, "major = 1;\n"); + append_indent(&ctx); + g_string_append(tsdl, "minor = 8;\n"); + append_indent(&ctx); + g_string_append(tsdl, "uuid = "); + append_uuid(&ctx, tc->uuid); + g_string_append(tsdl, ";\n"); + append_indent(&ctx); + g_string_append(tsdl, "byte_order = "); + + if (BYTE_ORDER == LITTLE_ENDIAN) { + g_string_append(tsdl, "le"); + } else { + g_string_append(tsdl, "be"); + } + + g_string_append(tsdl, ";\n"); + + /* Packet header field class */ + append_indent(&ctx); + g_string_append(tsdl, "packet.header := struct {\n"); + ctx.indent_level++; + append_indent(&ctx); + append_integer_field_class_from_props(&ctx, 32, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL, + NULL, "magic", true); + append_indent(&ctx); + append_integer_field_class_from_props(&ctx, 8, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "uuid[16]", true); + append_indent(&ctx); + append_integer_field_class_from_props(&ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "stream_id", true); + append_indent(&ctx); + append_integer_field_class_from_props(&ctx, 64, 8, false, + BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL, + NULL, "stream_instance_id", true); + + /* End packet header field class */ + append_end_block(&ctx); + g_string_append(ctx.tsdl, " align(8);\n"); + + /* End trace class */ + append_end_block_semi_nl_nl(&ctx); + + /* Trace class environment */ + count = bt_trace_class_get_environment_entry_count(tc->ir_tc); + if (count > 0) { + append_indent(&ctx); + g_string_append(tsdl, "env {\n"); + ctx.indent_level++; + + for (i = 0; i < count; i++) { + const char *name; + const bt_value *val; + + bt_trace_class_borrow_environment_entry_by_index_const( + tc->ir_tc, i, &name, &val); + append_indent(&ctx); + g_string_append_printf(tsdl, "%s = ", name); + + switch (bt_value_get_type(val)) { + case BT_VALUE_TYPE_SIGNED_INTEGER: + g_string_append_printf(tsdl, "%" PRId64, + bt_value_signed_integer_get(val)); + break; + case BT_VALUE_TYPE_STRING: + append_quoted_string(&ctx, bt_value_string_get(val)); + break; + default: + /* + * This is checked in + * translate_trace_class_trace_ir_to_ctf_ir(). + */ + abort(); + } + + g_string_append(tsdl, ";\n"); + } + + /* End trace class environment */ + append_end_block_semi_nl_nl(&ctx); + } + + /* Stream classes and their event classes */ + for (i = 0; i < tc->stream_classes->len; i++) { + append_stream_class(&ctx, tc->stream_classes->pdata[i]); + } +} diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h new file mode 100644 index 00000000..4ed02625 --- /dev/null +++ b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h @@ -0,0 +1,34 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "fs-sink-ctf-meta.h" + +BT_HIDDEN +void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc, + GString *tsdl); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */ diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c new file mode 100644 index 00000000..64f4d5ea --- /dev/null +++ b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c @@ -0,0 +1,1290 @@ +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-TRACE-IR-TO-CTF-IR" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/common.h" +#include "common/assert.h" +#include +#include +#include +#include + +#include "fs-sink-ctf-meta.h" + +struct field_path_elem { + uint64_t index_in_parent; + GString *name; + + /* Weak */ + const bt_field_class *ir_fc; + + /* Weak */ + struct fs_sink_ctf_field_class *parent_fc; +}; + +struct ctx { + /* Weak */ + struct fs_sink_ctf_stream_class *cur_sc; + + /* Weak */ + struct fs_sink_ctf_event_class *cur_ec; + + bt_scope cur_scope; + + /* + * Array of `struct field_path_elem` */ + GArray *cur_path; +}; + +static inline +struct field_path_elem *cur_path_stack_at(struct ctx *ctx, uint64_t i) +{ + BT_ASSERT(i < ctx->cur_path->len); + return &g_array_index(ctx->cur_path, struct field_path_elem, i); +} + +static inline +struct field_path_elem *cur_path_stack_top(struct ctx *ctx) +{ + BT_ASSERT(ctx->cur_path->len > 0); + return cur_path_stack_at(ctx, ctx->cur_path->len - 1); +} + +static inline +bool is_reserved_member_name(const char *name, const char *reserved_name) +{ + bool is_reserved = false; + + if (strcmp(name, reserved_name) == 0) { + is_reserved = true; + goto end; + } + + if (name[0] == '_' && strcmp(&name[1], reserved_name) == 0) { + is_reserved = true; + goto end; + } + +end: + return is_reserved; +} + +static inline +int cur_path_stack_push(struct ctx *ctx, + uint64_t index_in_parent, const char *ir_name, + const bt_field_class *ir_fc, + struct fs_sink_ctf_field_class *parent_fc) +{ + int ret = 0; + struct field_path_elem *field_path_elem; + + g_array_set_size(ctx->cur_path, ctx->cur_path->len + 1); + field_path_elem = cur_path_stack_top(ctx); + field_path_elem->index_in_parent = index_in_parent; + field_path_elem->name = g_string_new(ir_name); + + if (ir_name) { + if (ctx->cur_scope == BT_SCOPE_PACKET_CONTEXT) { + if (is_reserved_member_name(ir_name, "packet_size") || + is_reserved_member_name(ir_name, "content_size") || + is_reserved_member_name(ir_name, "timestamp_begin") || + is_reserved_member_name(ir_name, "timestamp_end") || + is_reserved_member_name(ir_name, "events_discarded") || + is_reserved_member_name(ir_name, "packet_seq_num")) { + BT_LOGE("Unsupported reserved TSDL structure field class member " + "or variant field class option name: name=\"%s\"", + ir_name); + ret = -1; + goto end; + } + } + + ret = fs_sink_ctf_protect_name(field_path_elem->name); + if (ret) { + BT_LOGE("Unsupported non-TSDL structure field class member " + "or variant field class option name: name=\"%s\"", + ir_name); + goto end; + } + } + + field_path_elem->ir_fc = ir_fc; + field_path_elem->parent_fc = parent_fc; + +end: + return ret; +} + +static inline +void cur_path_stack_pop(struct ctx *ctx) +{ + struct field_path_elem *field_path_elem; + + BT_ASSERT(ctx->cur_path->len > 0); + field_path_elem = cur_path_stack_top(ctx); + + if (field_path_elem->name) { + g_string_free(field_path_elem->name, TRUE); + field_path_elem->name = NULL; + } + + g_array_set_size(ctx->cur_path, ctx->cur_path->len - 1); +} + +/* + * Creates a relative field ref (a single name) from IR field path + * `tgt_ir_field_path`. + * + * This function tries to locate the target field class recursively from + * the top to the bottom of the context's current path using only the + * target field class's own name. This is because many CTF reading tools + * do not support a relative field ref with more than one element, for + * example `prev_struct.len`. + * + * Returns a negative value if this resolving operation failed. + */ +static +int create_relative_field_ref(struct ctx *ctx, + const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref) +{ + int ret = 0; + struct fs_sink_ctf_field_class *tgt_fc = NULL; + uint64_t i; + int64_t si; + const char *tgt_fc_name = NULL; + struct field_path_elem *field_path_elem; + + /* Get target field class's name */ + switch (bt_field_path_get_root_scope(tgt_ir_field_path)) { + case BT_SCOPE_PACKET_CONTEXT: + BT_ASSERT(ctx->cur_sc); + tgt_fc = ctx->cur_sc->packet_context_fc; + break; + case BT_SCOPE_EVENT_COMMON_CONTEXT: + BT_ASSERT(ctx->cur_sc); + tgt_fc = ctx->cur_sc->event_common_context_fc; + break; + case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: + BT_ASSERT(ctx->cur_ec); + tgt_fc = ctx->cur_ec->spec_context_fc; + break; + case BT_SCOPE_EVENT_PAYLOAD: + BT_ASSERT(ctx->cur_ec); + tgt_fc = ctx->cur_ec->payload_fc; + break; + default: + abort(); + } + + i = 0; + + while (i < bt_field_path_get_item_count(tgt_ir_field_path)) { + const bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_const( + tgt_ir_field_path, i); + struct fs_sink_ctf_named_field_class *named_fc = NULL; + + BT_ASSERT(tgt_fc); + BT_ASSERT(fp_item); + + switch (tgt_fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( + (void *) tgt_fc, + bt_field_path_item_index_get_index(fp_item)); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( + (void *) tgt_fc, + bt_field_path_item_index_get_index(fp_item)); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct fs_sink_ctf_field_class_array_base *array_base_fc = + (void *) tgt_fc; + + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); + tgt_fc = array_base_fc->elem_fc; + break; + } + default: + abort(); + } + + if (named_fc) { + tgt_fc = named_fc->fc; + tgt_fc_name = named_fc->name->str; + i++; + } + } + + BT_ASSERT(tgt_fc); + BT_ASSERT(tgt_fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT); + BT_ASSERT(tgt_fc_name); + + /* Find target field class having this name in current context */ + for (si = ctx->cur_path->len - 1; si >= 0; si--) { + struct fs_sink_ctf_field_class *fc; + struct fs_sink_ctf_field_class_struct *struct_fc; + struct fs_sink_ctf_field_class_variant *var_fc; + struct fs_sink_ctf_named_field_class *named_fc; + uint64_t len; + + field_path_elem = cur_path_stack_at(ctx, (uint64_t) si); + fc = field_path_elem->parent_fc; + if (!fc) { + /* Reached stack's bottom */ + ret = -1; + goto end; + } + + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + continue; + default: + /* Not supported by TSDL 1.8 */ + ret = -1; + goto end; + } + + if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { + struct_fc = (void *) fc; + len = struct_fc->members->len; + } else { + var_fc = (void *) fc; + len = var_fc->options->len; + } + + for (i = 0; i < len; i++) { + if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { + named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + } else { + named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + } + + if (strcmp(named_fc->name->str, tgt_fc_name) == 0) { + if (named_fc->fc == tgt_fc) { + g_string_assign(tgt_field_ref, + tgt_fc_name); + } else { + /* + * Using only the target field + * class's name, we're not + * reaching the target field + * class. This is not supported + * by TSDL 1.8. + */ + ret = -1; + } + + goto end; + } + } + } + +end: + return ret; +} + +/* + * Creates an absolute field ref from IR field path `tgt_ir_field_path`. + * + * Returns a negative value if this resolving operation failed. + */ +static +int create_absolute_field_ref(struct ctx *ctx, + const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref) +{ + int ret = 0; + struct fs_sink_ctf_field_class *fc = NULL; + uint64_t i; + + switch (bt_field_path_get_root_scope(tgt_ir_field_path)) { + case BT_SCOPE_PACKET_CONTEXT: + BT_ASSERT(ctx->cur_sc); + fc = ctx->cur_sc->packet_context_fc; + g_string_assign(tgt_field_ref, "stream.packet.context"); + break; + case BT_SCOPE_EVENT_COMMON_CONTEXT: + BT_ASSERT(ctx->cur_sc); + fc = ctx->cur_sc->event_common_context_fc; + g_string_assign(tgt_field_ref, "stream.event.context"); + break; + case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: + BT_ASSERT(ctx->cur_ec); + fc = ctx->cur_ec->spec_context_fc; + g_string_assign(tgt_field_ref, "event.context"); + break; + case BT_SCOPE_EVENT_PAYLOAD: + BT_ASSERT(ctx->cur_ec); + fc = ctx->cur_ec->payload_fc; + g_string_assign(tgt_field_ref, "event.fields"); + break; + default: + abort(); + } + + BT_ASSERT(fc); + + for (i = 0; i < bt_field_path_get_item_count(tgt_ir_field_path); i++) { + const bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_const( + tgt_ir_field_path, i); + struct fs_sink_ctf_named_field_class *named_fc = NULL; + + if (bt_field_path_item_get_type(fp_item) != + BT_FIELD_PATH_ITEM_TYPE_INDEX) { + /* Not supported by TSDL 1.8 */ + ret = -1; + goto end; + } + + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( + (void *) fc, + bt_field_path_item_index_get_index(fp_item)); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( + (void *) fc, + bt_field_path_item_index_get_index(fp_item)); + break; + default: + abort(); + } + + BT_ASSERT(named_fc); + g_string_append_c(tgt_field_ref, '.'); + g_string_append(tgt_field_ref, named_fc->name->str); + fc = named_fc->fc; + } + +end: + return ret; +} + +/* + * Resolves a target field class located at `tgt_ir_field_path`, writing + * the resolved field ref to `tgt_field_ref` and setting + * `*create_before` according to whether or not the target field must be + * created immediately before (in which case `tgt_field_ref` is + * irrelevant). + */ +static +void resolve_field_class(struct ctx *ctx, + const bt_field_path *tgt_ir_field_path, + GString *tgt_field_ref, bool *create_before) +{ + int ret; + bt_scope tgt_scope; + + *create_before = false; + + if (!tgt_ir_field_path) { + *create_before = true; + goto end; + } + + tgt_scope = bt_field_path_get_root_scope(tgt_ir_field_path); + + if (tgt_scope == ctx->cur_scope) { + /* + * Try, in this order: + * + * 1. Use a relative path, using only the target field + * class's name. This is what is the most commonly + * supported by popular CTF reading tools. + * + * 2. Use an absolute path. This could fail if there's + * an array field class from the current root's field + * class to the target field class. + * + * 3. Create the target field class before the + * requesting field class (fallback). + */ + ret = create_relative_field_ref(ctx, tgt_ir_field_path, + tgt_field_ref); + if (ret) { + ret = create_absolute_field_ref(ctx, tgt_ir_field_path, + tgt_field_ref); + if (ret) { + *create_before = true; + ret = 0; + goto end; + } + } + } else { + ret = create_absolute_field_ref(ctx, tgt_ir_field_path, + tgt_field_ref); + + /* It must always work in previous scopes */ + BT_ASSERT(ret == 0); + } + +end: + return; +} + +static +int translate_field_class(struct ctx *ctx); + +static inline +void append_to_parent_field_class(struct ctx *ctx, + struct fs_sink_ctf_field_class *fc) +{ + struct fs_sink_ctf_field_class *parent_fc = + cur_path_stack_top(ctx)->parent_fc; + + switch (parent_fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + fs_sink_ctf_field_class_struct_append_member((void *) parent_fc, + cur_path_stack_top(ctx)->name->str, fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + fs_sink_ctf_field_class_variant_append_option((void *) parent_fc, + cur_path_stack_top(ctx)->name->str, fc); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct fs_sink_ctf_field_class_array_base *array_base_fc = + (void *) parent_fc; + + BT_ASSERT(!array_base_fc->elem_fc); + array_base_fc->elem_fc = fc; + array_base_fc->base.alignment = fc->alignment; + break; + } + default: + abort(); + } +} + +static inline +void update_parent_field_class_alignment(struct ctx *ctx, + unsigned int alignment) +{ + struct fs_sink_ctf_field_class *parent_fc = + cur_path_stack_top(ctx)->parent_fc; + + switch (parent_fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + fs_sink_ctf_field_class_struct_align_at_least( + (void *) parent_fc, alignment); + break; + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct fs_sink_ctf_field_class_array_base *array_base_fc = + (void *) parent_fc; + + array_base_fc->base.alignment = alignment; + break; + } + default: + break; + } +} + +static inline +int translate_structure_field_class_members(struct ctx *ctx, + struct fs_sink_ctf_field_class_struct *struct_fc, + const bt_field_class *ir_fc) +{ + int ret = 0; + uint64_t i; + + for (i = 0; i < bt_field_class_structure_get_member_count(ir_fc); i++) { + const bt_field_class_structure_member *member; + const char *name; + const bt_field_class *memb_ir_fc; + + member = + bt_field_class_structure_borrow_member_by_index_const( + ir_fc, i); + name = bt_field_class_structure_member_get_name(member); + memb_ir_fc = bt_field_class_structure_member_borrow_field_class_const( + member); + ret = cur_path_stack_push(ctx, i, name, memb_ir_fc, + (void *) struct_fc); + if (ret) { + BT_LOGE("Cannot translate structure field class member: " + "name=\"%s\"", name); + goto end; + } + + ret = translate_field_class(ctx); + if (ret) { + BT_LOGE("Cannot translate structure field class member: " + "name=\"%s\"", name); + goto end; + } + + cur_path_stack_pop(ctx); + } + +end: + return ret; +} + +static inline +int translate_structure_field_class(struct ctx *ctx) +{ + int ret; + struct fs_sink_ctf_field_class_struct *fc = + fs_sink_ctf_field_class_struct_create_empty( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + + BT_ASSERT(fc); + append_to_parent_field_class(ctx, (void *) fc); + ret = translate_structure_field_class_members(ctx, fc, fc->base.ir_fc); + if (ret) { + goto end; + } + + update_parent_field_class_alignment(ctx, fc->base.alignment); + +end: + return ret; +} + +static inline +int translate_variant_field_class(struct ctx *ctx) +{ + int ret = 0; + uint64_t i; + struct fs_sink_ctf_field_class_variant *fc = + fs_sink_ctf_field_class_variant_create_empty( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + + BT_ASSERT(fc); + + /* Resolve tag field class before appending to parent */ + resolve_field_class(ctx, + bt_field_class_variant_borrow_selector_field_path_const( + fc->base.ir_fc), fc->tag_ref, &fc->tag_is_before); + + append_to_parent_field_class(ctx, (void *) fc); + + for (i = 0; i < bt_field_class_variant_get_option_count(fc->base.ir_fc); + i++) { + const bt_field_class_variant_option *opt; + const char *name; + const bt_field_class *opt_ir_fc; + + opt = bt_field_class_variant_borrow_option_by_index_const( + fc->base.ir_fc, i); + name = bt_field_class_variant_option_get_name(opt); + opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const( + opt); + ret = cur_path_stack_push(ctx, i, name, opt_ir_fc, (void *) fc); + if (ret) { + BT_LOGE("Cannot translate variant field class option: " + "name=\"%s\"", name); + goto end; + } + + ret = translate_field_class(ctx); + if (ret) { + BT_LOGE("Cannot translate variant field class option: " + "name=\"%s\"", name); + goto end; + } + + cur_path_stack_pop(ctx); + } + +end: + return ret; +} + +static inline +int translate_static_array_field_class(struct ctx *ctx) +{ + struct fs_sink_ctf_field_class_array *fc = + fs_sink_ctf_field_class_array_create_empty( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + const bt_field_class *elem_ir_fc = + bt_field_class_array_borrow_element_field_class_const( + fc->base.base.ir_fc); + int ret; + + BT_ASSERT(fc); + append_to_parent_field_class(ctx, (void *) fc); + ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc, + (void *) fc); + if (ret) { + BT_LOGE_STR("Cannot translate static array field class element."); + goto end; + } + + ret = translate_field_class(ctx); + if (ret) { + BT_LOGE_STR("Cannot translate static array field class element."); + goto end; + } + + cur_path_stack_pop(ctx); + update_parent_field_class_alignment(ctx, fc->base.base.alignment); + +end: + return ret; +} + +static inline +int translate_dynamic_array_field_class(struct ctx *ctx) +{ + struct fs_sink_ctf_field_class_sequence *fc = + fs_sink_ctf_field_class_sequence_create_empty( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + const bt_field_class *elem_ir_fc = + bt_field_class_array_borrow_element_field_class_const( + fc->base.base.ir_fc); + int ret; + + BT_ASSERT(fc); + + /* Resolve length field class before appending to parent */ + resolve_field_class(ctx, + bt_field_class_dynamic_array_borrow_length_field_path_const( + fc->base.base.ir_fc), + fc->length_ref, &fc->length_is_before); + + append_to_parent_field_class(ctx, (void *) fc); + ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc, + (void *) fc); + if (ret) { + BT_LOGE_STR("Cannot translate dynamic array field class element."); + goto end; + } + + ret = translate_field_class(ctx); + if (ret) { + BT_LOGE_STR("Cannot translate dynamic array field class element."); + goto end; + } + + cur_path_stack_pop(ctx); + update_parent_field_class_alignment(ctx, fc->base.base.alignment); + +end: + return ret; +} + +static inline +int translate_integer_field_class(struct ctx *ctx) +{ + struct fs_sink_ctf_field_class_int *fc = + fs_sink_ctf_field_class_int_create( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + + BT_ASSERT(fc); + append_to_parent_field_class(ctx, (void *) fc); + return 0; +} + +static inline +int translate_real_field_class(struct ctx *ctx) +{ + struct fs_sink_ctf_field_class_float *fc = + fs_sink_ctf_field_class_float_create( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + + BT_ASSERT(fc); + append_to_parent_field_class(ctx, (void *) fc); + return 0; +} + +static inline +int translate_string_field_class(struct ctx *ctx) +{ + struct fs_sink_ctf_field_class_string *fc = + fs_sink_ctf_field_class_string_create( + cur_path_stack_top(ctx)->ir_fc, + cur_path_stack_top(ctx)->index_in_parent); + + BT_ASSERT(fc); + append_to_parent_field_class(ctx, (void *) fc); + return 0; +} + +/* + * Translates a field class, recursively. + * + * The field class's IR field class, parent field class, and index + * within its parent are in the context's current path's top element + * (cur_path_stack_top()). + */ +static +int translate_field_class(struct ctx *ctx) +{ + int ret; + + switch (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc)) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + ret = translate_integer_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_REAL: + ret = translate_real_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_STRING: + ret = translate_string_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_STRUCTURE: + ret = translate_structure_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + ret = translate_static_array_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + ret = translate_dynamic_array_field_class(ctx); + break; + case BT_FIELD_CLASS_TYPE_VARIANT: + ret = translate_variant_field_class(ctx); + break; + default: + abort(); + } + + return ret; +} + +static +int set_field_ref(struct fs_sink_ctf_field_class *fc, const char *fc_name, + struct fs_sink_ctf_field_class *parent_fc) +{ + int ret = 0; + GString *field_ref = NULL; + bool is_before; + const char *tgt_type; + struct fs_sink_ctf_field_class_struct *parent_struct_fc = + (void *) parent_fc; + uint64_t i; + unsigned int suffix = 0; + + if (!fc_name || !parent_fc || + parent_fc->type != FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { + /* Not supported */ + ret = -1; + goto end; + } + + switch (fc->type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct fs_sink_ctf_field_class_sequence *seq_fc = (void *) fc; + + field_ref = seq_fc->length_ref; + is_before = seq_fc->length_is_before; + tgt_type = "len"; + break; + } + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + { + struct fs_sink_ctf_field_class_variant *var_fc = (void *) fc; + + field_ref = var_fc->tag_ref; + is_before = var_fc->tag_is_before; + tgt_type = "tag"; + break; + } + default: + abort(); + } + + BT_ASSERT(field_ref); + + if (!is_before) { + goto end; + } + + /* Initial field ref */ + g_string_printf(field_ref, "__%s_%s", fc_name, tgt_type); + + /* + * Make sure field ref does not clash with an existing field + * class name within the same parent structure field class. + */ + while (true) { + bool name_ok = true; + + for (i = 0; i < parent_struct_fc->members->len; i++) { + struct fs_sink_ctf_named_field_class *named_fc = + fs_sink_ctf_field_class_struct_borrow_member_by_index( + parent_struct_fc, i); + + if (strcmp(field_ref->str, named_fc->name->str) == 0) { + /* Name clash */ + name_ok = false; + break; + } + } + + if (name_ok) { + /* No clash: we're done */ + break; + } + + /* Append suffix and try again */ + g_string_printf(field_ref, "__%s_%s_%u", fc_name, tgt_type, + suffix); + suffix++; + } + +end: + return ret; +} + +/* + * This function recursively sets field refs of sequence and variant + * field classes when they are immediately before, avoiding name clashes + * with existing field class names. + * + * It can fail at this point if, for example, a sequence field class of + * which to set the length's field ref has something else than a + * structure field class as its parent: in this case, there's no + * location to place the length field class immediately before the + * sequence field class. + */ +static +int set_field_refs(struct fs_sink_ctf_field_class * const fc, + const char *fc_name, struct fs_sink_ctf_field_class *parent_fc) +{ + int ret = 0; + enum fs_sink_ctf_field_class_type fc_type; + BT_ASSERT(fc); + + fc_type = fc->type; + + switch (fc_type) { + case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT: + case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT: + { + uint64_t i; + uint64_t len; + struct fs_sink_ctf_field_class_struct *struct_fc; + struct fs_sink_ctf_field_class_variant *var_fc = NULL; + struct fs_sink_ctf_named_field_class *named_fc; + + if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { + struct_fc = (void *) fc; + len = struct_fc->members->len; + } else { + var_fc = (void *) fc; + len = var_fc->options->len; + ret = set_field_ref(fc, fc_name, parent_fc); + if (ret) { + goto end; + } + } + + for (i = 0; i < len; i++) { + if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) { + named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index( + struct_fc, i); + } else { + named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index( + var_fc, i); + } + + ret = set_field_refs(named_fc->fc, named_fc->name->str, + fc); + if (ret) { + goto end; + } + } + + break; + } + case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY: + case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE: + { + struct fs_sink_ctf_field_class_array_base *array_base_fc = + (void *) fc; + + if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) { + ret = set_field_ref(fc, fc_name, parent_fc); + if (ret) { + goto end; + } + } + + ret = set_field_refs(array_base_fc->elem_fc, NULL, fc); + if (ret) { + goto end; + } + + break; + } + default: + break; + } + +end: + return ret; +} + +/* + * This function translates a root scope trace IR field class to + * a CTF IR field class. + * + * The resulting CTF IR field class is written to `*fc` so that it + * exists as the parent object's (stream class or event class) true root + * field class during the recursive translation for resolving purposes. + * This is also why this function creates the empty structure field + * class and then calls translate_structure_field_class_members() to + * fill it. + */ +static +int translate_scope_field_class(struct ctx *ctx, bt_scope scope, + struct fs_sink_ctf_field_class **fc, + const bt_field_class *ir_fc) +{ + int ret = 0; + + if (!ir_fc) { + goto end; + } + + BT_ASSERT(bt_field_class_get_type(ir_fc) == + BT_FIELD_CLASS_TYPE_STRUCTURE); + BT_ASSERT(fc); + *fc = (void *) fs_sink_ctf_field_class_struct_create_empty( + ir_fc, UINT64_C(-1)); + BT_ASSERT(*fc); + ctx->cur_scope = scope; + BT_ASSERT(ctx->cur_path->len == 0); + ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, ir_fc, NULL); + if (ret) { + BT_LOGE("Cannot translate scope structure field class: " + "scope=%d", scope); + goto end; + } + + ret = translate_structure_field_class_members(ctx, (void *) *fc, ir_fc); + if (ret) { + BT_LOGE("Cannot translate scope structure field class: " + "scope=%d", scope); + goto end; + } + + cur_path_stack_pop(ctx); + + /* Set field refs for preceding targets */ + ret = set_field_refs(*fc, NULL, NULL); + +end: + return ret; +} + +static inline +void ctx_init(struct ctx *ctx) +{ + memset(ctx, 0, sizeof(struct ctx)); + ctx->cur_path = g_array_new(FALSE, TRUE, + sizeof(struct field_path_elem)); + BT_ASSERT(ctx->cur_path); +} + +static inline +void ctx_fini(struct ctx *ctx) +{ + if (ctx->cur_path) { + g_array_free(ctx->cur_path, TRUE); + ctx->cur_path = NULL; + } +} + +static +int translate_event_class(struct fs_sink_ctf_stream_class *sc, + const bt_event_class *ir_ec, + struct fs_sink_ctf_event_class **out_ec) +{ + int ret = 0; + struct ctx ctx; + struct fs_sink_ctf_event_class *ec; + + BT_ASSERT(sc); + BT_ASSERT(ir_ec); + + ctx_init(&ctx); + ec = fs_sink_ctf_event_class_create(sc, ir_ec); + BT_ASSERT(ec); + ctx.cur_sc = sc; + ctx.cur_ec = ec; + ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_SPECIFIC_CONTEXT, + &ec->spec_context_fc, + bt_event_class_borrow_specific_context_field_class_const( + ir_ec)); + if (ret) { + goto end; + } + + ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_PAYLOAD, + &ec->payload_fc, + bt_event_class_borrow_payload_field_class_const(ir_ec)); + if (ret) { + goto end; + } + +end: + ctx_fini(&ctx); + *out_ec = ec; + return ret; +} + +BT_HIDDEN +int try_translate_event_class_trace_ir_to_ctf_ir( + struct fs_sink_ctf_stream_class *sc, + const bt_event_class *ir_ec, + struct fs_sink_ctf_event_class **out_ec) +{ + int ret = 0; + + BT_ASSERT(sc); + BT_ASSERT(ir_ec); + + /* Check in hash table first */ + *out_ec = g_hash_table_lookup(sc->event_classes_from_ir, ir_ec); + if (likely(*out_ec)) { + goto end; + } + + ret = translate_event_class(sc, ir_ec, out_ec); + +end: + return ret; +} + +bool default_clock_class_name_exists(struct fs_sink_ctf_trace_class *tc, + const char *name) +{ + bool exists = false; + uint64_t i; + + for (i = 0; i < tc->stream_classes->len; i++) { + struct fs_sink_ctf_stream_class *sc = + tc->stream_classes->pdata[i]; + + if (sc->default_clock_class_name->len == 0) { + /* No default clock class */ + continue; + } + + if (strcmp(sc->default_clock_class_name->str, name) == 0) { + exists = true; + goto end; + } + } + +end: + return exists; +} + +static +void make_unique_default_clock_class_name(struct fs_sink_ctf_stream_class *sc) +{ + unsigned int suffix = 0; + char buf[16]; + + g_string_assign(sc->default_clock_class_name, ""); + sprintf(buf, "default"); + + while (default_clock_class_name_exists(sc->tc, buf)) { + sprintf(buf, "default%u", suffix); + suffix++; + } + + g_string_assign(sc->default_clock_class_name, buf); +} + +static +int translate_stream_class(struct fs_sink_ctf_trace_class *tc, + const bt_stream_class *ir_sc, + struct fs_sink_ctf_stream_class **out_sc) +{ + int ret = 0; + struct ctx ctx; + + BT_ASSERT(tc); + BT_ASSERT(ir_sc); + ctx_init(&ctx); + *out_sc = fs_sink_ctf_stream_class_create(tc, ir_sc); + BT_ASSERT(*out_sc); + + /* Set default clock class's protected name, if any */ + if ((*out_sc)->default_clock_class) { + const char *name = bt_clock_class_get_name( + (*out_sc)->default_clock_class); + + if (name) { + /* Try original name, protected */ + g_string_assign((*out_sc)->default_clock_class_name, + name); + ret = fs_sink_ctf_protect_name( + (*out_sc)->default_clock_class_name); + if (ret) { + /* Invalid: create a new name */ + make_unique_default_clock_class_name(*out_sc); + ret = 0; + } + } else { + /* No name: create a name */ + make_unique_default_clock_class_name(*out_sc); + } + } + + ctx.cur_sc = *out_sc; + ret = translate_scope_field_class(&ctx, BT_SCOPE_PACKET_CONTEXT, + &(*out_sc)->packet_context_fc, + bt_stream_class_borrow_packet_context_field_class_const(ir_sc)); + if (ret) { + goto error; + } + + if ((*out_sc)->packet_context_fc) { + /* + * Make sure the structure field class's alignment is + * enough: 8 is what we use for our own special members + * in the packet context. + */ + fs_sink_ctf_field_class_struct_align_at_least( + (void *) (*out_sc)->packet_context_fc, 8); + } + + ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_COMMON_CONTEXT, + &(*out_sc)->event_common_context_fc, + bt_stream_class_borrow_event_common_context_field_class_const( + ir_sc)); + if (ret) { + goto error; + } + + goto end; + +error: + fs_sink_ctf_stream_class_destroy(*out_sc); + *out_sc = NULL; + +end: + ctx_fini(&ctx); + return ret; +} + +BT_HIDDEN +int try_translate_stream_class_trace_ir_to_ctf_ir( + struct fs_sink_ctf_trace_class *tc, + const bt_stream_class *ir_sc, + struct fs_sink_ctf_stream_class **out_sc) +{ + int ret = 0; + uint64_t i; + + BT_ASSERT(tc); + BT_ASSERT(ir_sc); + + for (i = 0; i < tc->stream_classes->len; i++) { + *out_sc = tc->stream_classes->pdata[i]; + + if ((*out_sc)->ir_sc == ir_sc) { + goto end; + } + } + + ret = translate_stream_class(tc, ir_sc, out_sc); + +end: + return ret; +} + +BT_HIDDEN +struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir( + const bt_trace_class *ir_tc) +{ + uint64_t count; + uint64_t i; + struct fs_sink_ctf_trace_class *tc = NULL; + + /* Check that trace class's environment is TSDL-compatible */ + count = bt_trace_class_get_environment_entry_count(ir_tc); + for (i = 0; i < count; i++) { + const char *name; + const bt_value *val; + + bt_trace_class_borrow_environment_entry_by_index_const( + ir_tc, i, &name, &val); + + if (!fs_sink_ctf_ist_valid_identifier(name)) { + BT_LOGE("Unsupported trace class's environment entry name: " + "name=\"%s\"", name); + goto end; + } + + switch (bt_value_get_type(val)) { + case BT_VALUE_TYPE_SIGNED_INTEGER: + case BT_VALUE_TYPE_STRING: + break; + default: + BT_LOGE("Unsupported trace class's environment entry value type: " + "type=%s", + bt_common_value_type_string( + bt_value_get_type(val))); + goto end; + } + } + + tc = fs_sink_ctf_trace_class_create(ir_tc); + BT_ASSERT(tc); + +end: + return tc; +} diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h new file mode 100644 index 00000000..3a1a31e1 --- /dev/null +++ b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h @@ -0,0 +1,47 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H +#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H + +/* + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include + +#include "fs-sink-ctf-meta.h" + +BT_HIDDEN +int try_translate_event_class_trace_ir_to_ctf_ir( + struct fs_sink_ctf_stream_class *sc, + const bt_event_class *ir_ec, + struct fs_sink_ctf_event_class **out_ec); + +BT_HIDDEN +int try_translate_stream_class_trace_ir_to_ctf_ir( + struct fs_sink_ctf_trace_class *tc, + const bt_stream_class *ir_sc, + struct fs_sink_ctf_stream_class **out_sc); + +BT_HIDDEN +struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir( + const bt_trace_class *ir_tc); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H */ diff --git a/src/plugins/ctf/fs-src/Makefile.am b/src/plugins/ctf/fs-src/Makefile.am new file mode 100644 index 00000000..37ad7ed6 --- /dev/null +++ b/src/plugins/ctf/fs-src/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-src.la + +libbabeltrace2_plugin_ctf_fs_src_la_SOURCES = \ + data-stream-file.c \ + data-stream-file.h \ + file.c \ + file.h \ + fs.c \ + fs.h \ + lttng-index.h \ + metadata.c \ + metadata.h \ + query.h \ + query.c \ + logging.h \ + logging.c diff --git a/src/plugins/ctf/fs-src/data-stream-file.c b/src/plugins/ctf/fs-src/data-stream-file.c new file mode 100644 index 00000000..5b0086af --- /dev/null +++ b/src/plugins/ctf/fs-src/data-stream-file.c @@ -0,0 +1,757 @@ +/* + * Copyright 2016-2017 - Philippe Proulx + * Copyright 2016 - Jérémie Galarneau + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "compat/mman.h" +#include "compat/endian.h" +#include +#include "common/common.h" +#include "file.h" +#include "metadata.h" +#include "../common/msg-iter/msg-iter.h" +#include "common/assert.h" +#include "data-stream-file.h" +#include + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SRC-DS" +#include "logging.h" + +static inline +size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file) +{ + return ds_file->mmap_len - ds_file->request_offset; +} + +static +int ds_file_munmap(struct ctf_fs_ds_file *ds_file) +{ + int ret = 0; + + if (!ds_file || !ds_file->mmap_addr) { + goto end; + } + + if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) { + BT_LOGE_ERRNO("Cannot memory-unmap file", + ": address=%p, size=%zu, file_path=\"%s\", file=%p", + ds_file->mmap_addr, ds_file->mmap_len, + ds_file->file ? ds_file->file->path->str : "NULL", + ds_file->file ? ds_file->file->fp : NULL); + ret = -1; + goto end; + } + + ds_file->mmap_addr = NULL; + +end: + return ret; +} + +static +enum bt_msg_iter_medium_status ds_file_mmap_next( + struct ctf_fs_ds_file *ds_file) +{ + enum bt_msg_iter_medium_status ret = + BT_MSG_ITER_MEDIUM_STATUS_OK; + + /* Unmap old region */ + if (ds_file->mmap_addr) { + if (ds_file_munmap(ds_file)) { + goto error; + } + + /* + * mmap_len is guaranteed to be page-aligned except on the + * last mapping where it may not be possible (since the file's + * size itself may not be a page multiple). + */ + ds_file->mmap_offset += ds_file->mmap_len; + ds_file->request_offset = 0; + } + + ds_file->mmap_len = MIN(ds_file->file->size - ds_file->mmap_offset, + ds_file->mmap_max_len); + if (ds_file->mmap_len == 0) { + ret = BT_MSG_ITER_MEDIUM_STATUS_EOF; + goto end; + } + /* Map new region */ + BT_ASSERT(ds_file->mmap_len); + ds_file->mmap_addr = bt_mmap((void *) 0, ds_file->mmap_len, + PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp), + ds_file->mmap_offset); + if (ds_file->mmap_addr == MAP_FAILED) { + BT_LOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s", + ds_file->mmap_len, ds_file->file->path->str, + ds_file->file->fp, (intmax_t) ds_file->mmap_offset, + strerror(errno)); + goto error; + } + + goto end; +error: + ds_file_munmap(ds_file); + ret = BT_MSG_ITER_MEDIUM_STATUS_ERROR; +end: + return ret; +} + +static +enum bt_msg_iter_medium_status medop_request_bytes( + size_t request_sz, uint8_t **buffer_addr, + size_t *buffer_sz, void *data) +{ + enum bt_msg_iter_medium_status status = + BT_MSG_ITER_MEDIUM_STATUS_OK; + struct ctf_fs_ds_file *ds_file = data; + + if (request_sz == 0) { + goto end; + } + + /* Check if we have at least one memory-mapped byte left */ + if (remaining_mmap_bytes(ds_file) == 0) { + /* Are we at the end of the file? */ + if (ds_file->mmap_offset >= ds_file->file->size) { + BT_LOGD("Reached end of file \"%s\" (%p)", + ds_file->file->path->str, ds_file->file->fp); + status = BT_MSG_ITER_MEDIUM_STATUS_EOF; + goto end; + } + + status = ds_file_mmap_next(ds_file); + switch (status) { + case BT_MSG_ITER_MEDIUM_STATUS_OK: + break; + case BT_MSG_ITER_MEDIUM_STATUS_EOF: + goto end; + default: + BT_LOGE("Cannot memory-map next region of file \"%s\" (%p)", + ds_file->file->path->str, + ds_file->file->fp); + goto error; + } + } + + *buffer_sz = MIN(remaining_mmap_bytes(ds_file), request_sz); + *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset; + ds_file->request_offset += *buffer_sz; + goto end; + +error: + status = BT_MSG_ITER_MEDIUM_STATUS_ERROR; + +end: + return status; +} + +static +bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id, + void *data) +{ + struct ctf_fs_ds_file *ds_file = data; + bt_stream_class *ds_file_stream_class; + bt_stream *stream = NULL; + + ds_file_stream_class = bt_stream_borrow_class( + ds_file->stream); + + if (stream_class != ds_file_stream_class) { + /* + * Not supported: two packets described by two different + * stream classes within the same data stream file. + */ + goto end; + } + + stream = ds_file->stream; + +end: + return stream; +} + +static +enum bt_msg_iter_medium_status medop_seek(enum bt_msg_iter_seek_whence whence, + off_t offset, void *data) +{ + enum bt_msg_iter_medium_status ret = + BT_MSG_ITER_MEDIUM_STATUS_OK; + struct ctf_fs_ds_file *ds_file = data; + off_t file_size = ds_file->file->size; + + if (whence != BT_MSG_ITER_SEEK_WHENCE_SET || + offset < 0 || offset > file_size) { + BT_LOGE("Invalid medium seek request: whence=%d, offset=%jd, " + "file-size=%jd", (int) whence, offset, + file_size); + ret = BT_MSG_ITER_MEDIUM_STATUS_INVAL; + goto end; + } + + /* + * Determine whether or not the destination is contained within the + * current mapping. + */ + if (ds_file->mmap_addr && (offset < ds_file->mmap_offset || + offset >= ds_file->mmap_offset + ds_file->mmap_len)) { + int unmap_ret; + off_t offset_in_mapping = offset % bt_common_get_page_size(); + + BT_LOGD("Medium seek request cannot be accomodated by the current " + "file mapping: offset=%jd, mmap-offset=%jd, " + "mmap-len=%zu", offset, ds_file->mmap_offset, + ds_file->mmap_len); + unmap_ret = ds_file_munmap(ds_file); + if (unmap_ret) { + ret = BT_MSG_ITER_MEDIUM_STATUS_ERROR; + goto end; + } + + ds_file->mmap_offset = offset - offset_in_mapping; + ds_file->request_offset = offset_in_mapping; + ret = ds_file_mmap_next(ds_file); + if (ret != BT_MSG_ITER_MEDIUM_STATUS_OK) { + goto end; + } + } else { + ds_file->request_offset = offset - ds_file->mmap_offset; + } + + ds_file->end_reached = (offset == file_size); +end: + return ret; +} + +BT_HIDDEN +struct bt_msg_iter_medium_ops ctf_fs_ds_file_medops = { + .request_bytes = medop_request_bytes, + .borrow_stream = medop_borrow_stream, + .seek = medop_seek, +}; + +static +int convert_cycles_to_ns(struct ctf_clock_class *clock_class, + uint64_t cycles, int64_t *ns) +{ + return bt_util_clock_cycles_to_ns_from_origin(cycles, + clock_class->frequency, clock_class->offset_seconds, + clock_class->offset_cycles, ns); +} + +static +struct ctf_fs_ds_index *build_index_from_idx_file( + struct ctf_fs_ds_file *ds_file) +{ + int ret; + gchar *directory = NULL; + gchar *basename = NULL; + GString *index_basename = NULL; + gchar *index_file_path = NULL; + GMappedFile *mapped_file = NULL; + gsize filesize; + const char *mmap_begin = NULL, *file_pos = NULL; + const struct ctf_packet_index_file_hdr *header = NULL; + struct ctf_fs_ds_index *index = NULL; + struct ctf_fs_ds_index_entry *index_entry = NULL; + uint64_t total_packets_size = 0; + size_t file_index_entry_size; + size_t file_entry_count; + size_t i; + struct ctf_stream_class *sc; + struct bt_msg_iter_packet_properties props; + + BT_LOGD("Building index from .idx file of stream file %s", + ds_file->file->path->str); + ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props); + if (ret) { + BT_LOGD_STR("Cannot read first packet's header and context fields."); + goto error; + } + + sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, + props.stream_class_id); + BT_ASSERT(sc); + if (!sc->default_clock_class) { + BT_LOGD_STR("Cannot find stream class's default clock class."); + goto error; + } + + /* Look for index file in relative path index/name.idx. */ + basename = g_path_get_basename(ds_file->file->path->str); + if (!basename) { + BT_LOGE("Cannot get the basename of datastream file %s", + ds_file->file->path->str); + goto error; + } + + directory = g_path_get_dirname(ds_file->file->path->str); + if (!directory) { + BT_LOGE("Cannot get dirname of datastream file %s", + ds_file->file->path->str); + goto error; + } + + index_basename = g_string_new(basename); + if (!index_basename) { + BT_LOGE_STR("Cannot allocate index file basename string"); + goto error; + } + + g_string_append(index_basename, ".idx"); + index_file_path = g_build_filename(directory, "index", + index_basename->str, NULL); + mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL); + if (!mapped_file) { + BT_LOGD("Cannot create new mapped file %s", + index_file_path); + goto error; + } + + /* + * The g_mapped_file API limits us to 4GB files on 32-bit. + * Traces with such large indexes have never been seen in the wild, + * but this would need to be adjusted to support them. + */ + filesize = g_mapped_file_get_length(mapped_file); + if (filesize < sizeof(*header)) { + BT_LOGW("Invalid LTTng trace index file: " + "file size (%zu bytes) < header size (%zu bytes)", + filesize, sizeof(*header)); + goto error; + } + + mmap_begin = g_mapped_file_get_contents(mapped_file); + header = (struct ctf_packet_index_file_hdr *) mmap_begin; + + file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header); + if (be32toh(header->magic) != CTF_INDEX_MAGIC) { + BT_LOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed"); + goto error; + } + + file_index_entry_size = be32toh(header->packet_index_len); + file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size; + if ((filesize - sizeof(*header)) % file_index_entry_size) { + BT_LOGW("Invalid LTTng trace index: the index's size after the header " + "(%zu bytes) is not a multiple of the index entry size " + "(%zu bytes)", (filesize - sizeof(*header)), + sizeof(*header)); + goto error; + } + + index = ctf_fs_ds_index_create(); + if (!index) { + goto error; + } + + for (i = 0; i < file_entry_count; i++) { + struct ctf_packet_index *file_index = + (struct ctf_packet_index *) file_pos; + uint64_t packet_size = be64toh(file_index->packet_size); + + if (packet_size % CHAR_BIT) { + BT_LOGW("Invalid packet size encountered in LTTng trace index file"); + goto error; + } + + index_entry = g_new0(struct ctf_fs_ds_index_entry, 1); + if (!index_entry) { + goto error; + } + + /* Convert size in bits to bytes. */ + packet_size /= CHAR_BIT; + index_entry->packet_size = packet_size; + + index_entry->offset = be64toh(file_index->offset); + if (i != 0 && index_entry->offset < (index_entry - 1)->offset) { + BT_LOGW("Invalid, non-monotonic, packet offset encountered in LTTng trace index file: " + "previous offset=%" PRIu64 ", current offset=%" PRIu64, + (index_entry - 1)->offset, index_entry->offset); + goto error; + } + + index_entry->timestamp_begin = be64toh(file_index->timestamp_begin); + index_entry->timestamp_end = be64toh(file_index->timestamp_end); + if (index_entry->timestamp_end < index_entry->timestamp_begin) { + BT_LOGW("Invalid packet time bounds encountered in LTTng trace index file (begin > end): " + "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64, + index_entry->timestamp_begin, + index_entry->timestamp_end); + goto error; + } + + /* Convert the packet's bound to nanoseconds since Epoch. */ + ret = convert_cycles_to_ns(sc->default_clock_class, + index_entry->timestamp_begin, + &index_entry->timestamp_begin_ns); + if (ret) { + BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch during index parsing"); + goto error; + } + ret = convert_cycles_to_ns(sc->default_clock_class, + index_entry->timestamp_end, + &index_entry->timestamp_end_ns); + if (ret) { + BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing"); + goto error; + } + + total_packets_size += packet_size; + file_pos += file_index_entry_size; + + g_ptr_array_add(index->entries, index_entry); + } + + /* Validate that the index addresses the complete stream. */ + if (ds_file->file->size != total_packets_size) { + BT_LOGW("Invalid LTTng trace index file; indexed size != stream file size: " + "file-size=%" PRIu64 ", total-packets-size=%" PRIu64, + ds_file->file->size, total_packets_size); + goto error; + } +end: + g_free(directory); + g_free(basename); + g_free(index_file_path); + if (index_basename) { + g_string_free(index_basename, TRUE); + } + if (mapped_file) { + g_mapped_file_unref(mapped_file); + } + return index; +error: + ctf_fs_ds_index_destroy(index); + g_free(index_entry); + index = NULL; + goto end; +} + +static +int init_index_entry(struct ctf_fs_ds_index_entry *entry, + struct ctf_fs_ds_file *ds_file, + struct bt_msg_iter_packet_properties *props, + off_t packet_size, off_t packet_offset) +{ + int ret = 0; + struct ctf_stream_class *sc; + + sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, + props->stream_class_id); + BT_ASSERT(sc); + BT_ASSERT(packet_offset >= 0); + entry->offset = packet_offset; + BT_ASSERT(packet_size >= 0); + entry->packet_size = packet_size; + + if (props->snapshots.beginning_clock != UINT64_C(-1)) { + /* Convert the packet's bound to nanoseconds since Epoch. */ + ret = convert_cycles_to_ns(sc->default_clock_class, + props->snapshots.beginning_clock, + &entry->timestamp_begin_ns); + if (ret) { + BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); + goto end; + } + } else { + entry->timestamp_begin_ns = UINT64_C(-1); + } + + if (props->snapshots.end_clock != UINT64_C(-1)) { + ret = convert_cycles_to_ns(sc->default_clock_class, + props->snapshots.end_clock, + &entry->timestamp_end_ns); + if (ret) { + BT_LOGD_STR("Failed to convert raw timestamp to nanoseconds since Epoch."); + goto end; + } + } else { + entry->timestamp_end_ns = UINT64_C(-1); + } + +end: + return ret; +} + +static +struct ctf_fs_ds_index *build_index_from_stream_file( + struct ctf_fs_ds_file *ds_file) +{ + int ret; + struct ctf_fs_ds_index *index = NULL; + enum bt_msg_iter_status iter_status = BT_MSG_ITER_STATUS_OK; + off_t current_packet_offset_bytes = 0; + + BT_LOGD("Indexing stream file %s", ds_file->file->path->str); + + index = ctf_fs_ds_index_create(); + if (!index) { + goto error; + } + + do { + off_t current_packet_size_bytes; + struct ctf_fs_ds_index_entry *index_entry; + struct bt_msg_iter_packet_properties props; + + if (current_packet_offset_bytes < 0) { + BT_LOGE_STR("Cannot get the current packet's offset."); + goto error; + } else if (current_packet_offset_bytes > ds_file->file->size) { + BT_LOGE_STR("Unexpected current packet's offset (larger than file)."); + goto error; + } else if (current_packet_offset_bytes == ds_file->file->size) { + /* No more data */ + break; + } + + iter_status = bt_msg_iter_seek(ds_file->msg_iter, + current_packet_offset_bytes); + if (iter_status != BT_MSG_ITER_STATUS_OK) { + goto error; + } + + iter_status = bt_msg_iter_get_packet_properties( + ds_file->msg_iter, &props); + if (iter_status != BT_MSG_ITER_STATUS_OK) { + goto error; + } + + if (props.exp_packet_total_size >= 0) { + current_packet_size_bytes = + (uint64_t) props.exp_packet_total_size / 8; + } else { + current_packet_size_bytes = ds_file->file->size; + } + + if (current_packet_offset_bytes + current_packet_size_bytes > + ds_file->file->size) { + BT_LOGW("Invalid packet size reported in file: stream=\"%s\", " + "packet-offset=%jd, packet-size-bytes=%jd, " + "file-size=%jd", + ds_file->file->path->str, + current_packet_offset_bytes, + current_packet_size_bytes, + ds_file->file->size); + goto error; + } + + index_entry = g_new0(struct ctf_fs_ds_index_entry, 1); + if (!index_entry) { + BT_LOGE_STR("Failed to allocate a new index entry."); + goto error; + } + + ret = init_index_entry(index_entry, ds_file, &props, + current_packet_size_bytes, current_packet_offset_bytes); + if (ret) { + g_free(index_entry); + goto error; + } + + g_ptr_array_add(index->entries, index_entry); + + current_packet_offset_bytes += current_packet_size_bytes; + BT_LOGD("Seeking to next packet: current-packet-offset=%jd, " + "next-packet-offset=%jd", + current_packet_offset_bytes - current_packet_size_bytes, + current_packet_offset_bytes); + + } while (iter_status == BT_MSG_ITER_STATUS_OK); + + if (iter_status != BT_MSG_ITER_STATUS_OK) { + goto error; + } + +end: + return index; + +error: + ctf_fs_ds_index_destroy(index); + index = NULL; + goto end; +} + +BT_HIDDEN +struct ctf_fs_ds_file *ctf_fs_ds_file_create( + struct ctf_fs_trace *ctf_fs_trace, + bt_self_message_iterator *pc_msg_iter, + struct bt_msg_iter *msg_iter, + bt_stream *stream, const char *path) +{ + int ret; + const size_t page_size = bt_common_get_page_size(); + struct ctf_fs_ds_file *ds_file = g_new0(struct ctf_fs_ds_file, 1); + + if (!ds_file) { + goto error; + } + + ds_file->pc_msg_iter = pc_msg_iter; + ds_file->file = ctf_fs_file_create(); + if (!ds_file->file) { + goto error; + } + + ds_file->stream = stream; + bt_stream_get_ref(ds_file->stream); + ds_file->metadata = ctf_fs_trace->metadata; + g_string_assign(ds_file->file->path, path); + ret = ctf_fs_file_open(ds_file->file, "rb"); + if (ret) { + goto error; + } + + ds_file->msg_iter = msg_iter; + bt_msg_iter_set_medops_data(ds_file->msg_iter, ds_file); + if (!ds_file->msg_iter) { + goto error; + } + + ds_file->mmap_max_len = page_size * 2048; + + goto end; + +error: + /* Do not touch "borrowed" file. */ + ctf_fs_ds_file_destroy(ds_file); + ds_file = NULL; + +end: + return ds_file; +} + +BT_HIDDEN +struct ctf_fs_ds_index *ctf_fs_ds_file_build_index( + struct ctf_fs_ds_file *ds_file) +{ + struct ctf_fs_ds_index *index; + + index = build_index_from_idx_file(ds_file); + if (index) { + goto end; + } + + BT_LOGD("Failed to build index from .index file; " + "falling back to stream indexing."); + index = build_index_from_stream_file(ds_file); +end: + return index; +} + +BT_HIDDEN +struct ctf_fs_ds_index *ctf_fs_ds_index_create() +{ + struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1); + + if (!index) { + BT_LOGE_STR("Failed to allocate index"); + goto error; + } + + index->entries = g_ptr_array_new_with_free_func((GDestroyNotify) g_free); + if (!index->entries) { + BT_LOGE("Failed to allocate index entries."); + goto error; + } + + goto end; + +error: + ctf_fs_ds_index_destroy(index); + index = NULL; +end: + return index; +} + +BT_HIDDEN +void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file) +{ + if (!ds_file) { + return; + } + + bt_stream_put_ref(ds_file->stream); + (void) ds_file_munmap(ds_file); + + if (ds_file->file) { + ctf_fs_file_destroy(ds_file->file); + } + + g_free(ds_file); +} + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_ds_file_next( + struct ctf_fs_ds_file *ds_file, + bt_message **msg) +{ + enum bt_msg_iter_status msg_iter_status; + bt_self_message_iterator_status status; + + msg_iter_status = bt_msg_iter_get_next_message( + ds_file->msg_iter, ds_file->pc_msg_iter, msg); + + switch (msg_iter_status) { + case BT_MSG_ITER_STATUS_EOF: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + break; + case BT_MSG_ITER_STATUS_OK: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + break; + case BT_MSG_ITER_STATUS_AGAIN: + /* + * Should not make it this far as this is + * medium-specific; there is nothing for the user to do + * and it should have been handled upstream. + */ + abort(); + case BT_MSG_ITER_STATUS_INVAL: + case BT_MSG_ITER_STATUS_ERROR: + default: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + break; + } + return status; +} + +BT_HIDDEN +void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index) +{ + if (!index) { + return; + } + + if (index->entries) { + g_ptr_array_free(index->entries, TRUE); + } + g_free(index); +} diff --git a/src/plugins/ctf/fs-src/data-stream-file.h b/src/plugins/ctf/fs-src/data-stream-file.h new file mode 100644 index 00000000..c826a656 --- /dev/null +++ b/src/plugins/ctf/fs-src/data-stream-file.h @@ -0,0 +1,116 @@ +#ifndef CTF_FS_DS_FILE_H +#define CTF_FS_DS_FILE_H + +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "common/babeltrace.h" +#include + +#include "../common/msg-iter/msg-iter.h" +#include "lttng-index.h" + +struct ctf_fs_component; +struct ctf_fs_file; +struct ctf_fs_trace; +struct ctf_fs_ds_file; + +struct ctf_fs_ds_file_info { + /* Owned by this. */ + GString *path; + + /* Guaranteed to be set, as opposed to the index. */ + int64_t begin_ns; +}; + +struct ctf_fs_metadata; + +struct ctf_fs_ds_file { + /* Weak */ + struct ctf_fs_metadata *metadata; + + /* Weak */ + bt_self_message_iterator *pc_msg_iter; + + /* Owned by this */ + struct ctf_fs_file *file; + + /* Owned by this */ + bt_stream *stream; + + /* Weak */ + struct bt_msg_iter *msg_iter; + + void *mmap_addr; + + /* + * Max length of chunk to mmap() when updating the current mapping. + * This value must be page-aligned. + */ + size_t mmap_max_len; + + /* Length of the current mapping. Never exceeds the file's length. */ + size_t mmap_len; + + /* Offset in the file where the current mapping starts. */ + off_t mmap_offset; + + /* + * Offset, in the current mapping, of the address to return on the next + * request. + */ + off_t request_offset; + + bool end_reached; +}; + +BT_HIDDEN +struct ctf_fs_ds_file *ctf_fs_ds_file_create( + struct ctf_fs_trace *ctf_fs_trace, + bt_self_message_iterator *pc_msg_iter, + struct bt_msg_iter *msg_iter, + bt_stream *stream, const char *path); + +BT_HIDDEN +void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *stream); + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_ds_file_next( + struct ctf_fs_ds_file *ds_file, + bt_message **msg); + +BT_HIDDEN +struct ctf_fs_ds_index *ctf_fs_ds_file_build_index( + struct ctf_fs_ds_file *ds_file); + +BT_HIDDEN +struct ctf_fs_ds_index *ctf_fs_ds_index_create(); + +BT_HIDDEN +void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index); + +extern struct bt_msg_iter_medium_ops ctf_fs_ds_file_medops; + +#endif /* CTF_FS_DS_FILE_H */ diff --git a/src/plugins/ctf/fs-src/file.c b/src/plugins/ctf/fs-src/file.c new file mode 100644 index 00000000..585faef8 --- /dev/null +++ b/src/plugins/ctf/fs-src/file.c @@ -0,0 +1,119 @@ +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "file.h" + +#define BT_LOG_TAG "PLUGIN-CTF-FS-FILE-SRC" +#include "logging.h" + +BT_HIDDEN +void ctf_fs_file_destroy(struct ctf_fs_file *file) +{ + if (!file) { + return; + } + + if (file->fp) { + BT_LOGD("Closing file \"%s\" (%p)", + file->path ? file->path->str : NULL, file->fp); + + if (fclose(file->fp)) { + BT_LOGE("Cannot close file \"%s\": %s", + file->path ? file->path->str : "NULL", + strerror(errno)); + } + } + + if (file->path) { + g_string_free(file->path, TRUE); + } + + g_free(file); +} + +BT_HIDDEN +struct ctf_fs_file *ctf_fs_file_create(void) +{ + struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1); + + if (!file) { + goto error; + } + + file->path = g_string_new(NULL); + if (!file->path) { + goto error; + } + + goto end; + +error: + ctf_fs_file_destroy(file); + file = NULL; + +end: + return file; +} + +BT_HIDDEN +int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode) +{ + int ret = 0; + struct stat stat; + + BT_LOGD("Opening file \"%s\" with mode \"%s\"", file->path->str, mode); + file->fp = fopen(file->path->str, mode); + if (!file->fp) { + BT_LOGE("Cannot open file \"%s\" with mode \"%s\": %s", + file->path->str, mode, strerror(errno)); + goto error; + } + + BT_LOGD("Opened file: %p", file->fp); + + if (fstat(fileno(file->fp), &stat)) { + BT_LOGE("Cannot get file information: %s", strerror(errno)); + goto error; + } + + file->size = stat.st_size; + BT_LOGD("File is %jd bytes", (intmax_t) file->size); + goto end; + +error: + ret = -1; + + if (file->fp) { + if (fclose(file->fp)) { + BT_LOGE("Cannot close file \"%s\": %s", file->path->str, + strerror(errno)); + } + } + +end: + return ret; +} diff --git a/src/plugins/ctf/fs-src/file.h b/src/plugins/ctf/fs-src/file.h new file mode 100644 index 00000000..509b1e4e --- /dev/null +++ b/src/plugins/ctf/fs-src/file.h @@ -0,0 +1,40 @@ +#ifndef CTF_FS_FILE_H +#define CTF_FS_FILE_H + +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include "fs.h" + +BT_HIDDEN +void ctf_fs_file_destroy(struct ctf_fs_file *file); + +BT_HIDDEN +struct ctf_fs_file *ctf_fs_file_create(void); + +BT_HIDDEN +int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode); + +#endif /* CTF_FS_FILE_H */ diff --git a/src/plugins/ctf/fs-src/fs.c b/src/plugins/ctf/fs-src/fs.c new file mode 100644 index 00000000..5ff6d352 --- /dev/null +++ b/src/plugins/ctf/fs-src/fs.c @@ -0,0 +1,1964 @@ +/* + * fs.c + * + * Babeltrace CTF file system Reader Component + * + * Copyright 2015-2017 Philippe Proulx + * Copyright 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/common.h" +#include +#include "compat/uuid.h" +#include "plugins/plugins-common.h" +#include +#include "common/assert.h" +#include +#include +#include "fs.h" +#include "metadata.h" +#include "data-stream-file.h" +#include "file.h" +#include "../common/metadata/decoder.h" +#include "../common/msg-iter/msg-iter.h" +#include "../common/utils/utils.h" +#include "query.h" + +#define BT_LOG_TAG "PLUGIN-CTF-FS-SRC" +#include "logging.h" + +static +int msg_iter_data_set_current_ds_file(struct ctf_fs_msg_iter_data *msg_iter_data) +{ + struct ctf_fs_ds_file_info *ds_file_info; + int ret = 0; + + BT_ASSERT(msg_iter_data->ds_file_info_index < + msg_iter_data->ds_file_group->ds_file_infos->len); + ds_file_info = g_ptr_array_index( + msg_iter_data->ds_file_group->ds_file_infos, + msg_iter_data->ds_file_info_index); + + ctf_fs_ds_file_destroy(msg_iter_data->ds_file); + msg_iter_data->ds_file = ctf_fs_ds_file_create( + msg_iter_data->ds_file_group->ctf_fs_trace, + msg_iter_data->pc_msg_iter, + msg_iter_data->msg_iter, + msg_iter_data->ds_file_group->stream, + ds_file_info->path->str); + if (!msg_iter_data->ds_file) { + ret = -1; + } + + return ret; +} + +static +void ctf_fs_msg_iter_data_destroy( + struct ctf_fs_msg_iter_data *msg_iter_data) +{ + if (!msg_iter_data) { + return; + } + + ctf_fs_ds_file_destroy(msg_iter_data->ds_file); + + if (msg_iter_data->msg_iter) { + bt_msg_iter_destroy(msg_iter_data->msg_iter); + } + + g_free(msg_iter_data); +} + +static +void set_msg_iter_emits_stream_beginning_end_messages( + struct ctf_fs_msg_iter_data *msg_iter_data) +{ + bt_msg_iter_set_emit_stream_beginning_message( + msg_iter_data->ds_file->msg_iter, + msg_iter_data->ds_file_info_index == 0); + bt_msg_iter_set_emit_stream_end_message( + msg_iter_data->ds_file->msg_iter, + msg_iter_data->ds_file_info_index == + msg_iter_data->ds_file_group->ds_file_infos->len - 1); +} + +static +bt_self_message_iterator_status ctf_fs_iterator_next_one( + struct ctf_fs_msg_iter_data *msg_iter_data, + const bt_message **out_msg) +{ + bt_self_message_iterator_status status; + + BT_ASSERT(msg_iter_data->ds_file); + + while (true) { + bt_message *msg; + + status = ctf_fs_ds_file_next(msg_iter_data->ds_file, &msg); + switch (status) { + case BT_SELF_MESSAGE_ITERATOR_STATUS_OK: + *out_msg = msg; + msg = NULL; + goto end; + case BT_SELF_MESSAGE_ITERATOR_STATUS_END: + { + int ret; + + if (msg_iter_data->ds_file_info_index == + msg_iter_data->ds_file_group->ds_file_infos->len - 1) { + /* End of all group's stream files */ + goto end; + } + + msg_iter_data->ds_file_info_index++; + bt_msg_iter_reset_for_next_stream_file( + msg_iter_data->msg_iter); + set_msg_iter_emits_stream_beginning_end_messages( + msg_iter_data); + + /* + * Open and start reading the next stream file + * within our stream file group. + */ + ret = msg_iter_data_set_current_ds_file(msg_iter_data); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + /* Continue the loop to get the next message */ + break; + } + default: + goto end; + } + } + +end: + return status; +} + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_next( + bt_self_message_iterator *iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + struct ctf_fs_msg_iter_data *msg_iter_data = + bt_self_message_iterator_get_data(iterator); + uint64_t i = 0; + + while (i < capacity && status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]); + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + i++; + } + } + + if (i > 0) { + /* + * Even if ctf_fs_iterator_next_one() returned something + * else than BT_SELF_MESSAGE_ITERATOR_STATUS_OK, we + * accumulated message objects in the output + * message array, so we need to return + * BT_SELF_MESSAGE_ITERATOR_STATUS_OK so that they are + * transfered to downstream. This other status occurs + * again the next time muxer_msg_iter_do_next() is + * called, possibly without any accumulated + * message, in which case we'll return it. + */ + *count = i; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + } + + return status; +} + +static +int ctf_fs_iterator_reset(struct ctf_fs_msg_iter_data *msg_iter_data) +{ + int ret; + + msg_iter_data->ds_file_info_index = 0; + ret = msg_iter_data_set_current_ds_file(msg_iter_data); + if (ret) { + goto end; + } + + bt_msg_iter_reset(msg_iter_data->msg_iter); + set_msg_iter_emits_stream_beginning_end_messages(msg_iter_data); + +end: + return ret; +} + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_seek_beginning( + bt_self_message_iterator *it) +{ + struct ctf_fs_msg_iter_data *msg_iter_data = + bt_self_message_iterator_get_data(it); + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(msg_iter_data); + if (ctf_fs_iterator_reset(msg_iter_data)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } + + return status; +} + +BT_HIDDEN +void ctf_fs_iterator_finalize(bt_self_message_iterator *it) +{ + ctf_fs_msg_iter_data_destroy( + bt_self_message_iterator_get_data(it)); +} + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_source *self_comp, + bt_self_component_port_output *self_port) +{ + struct ctf_fs_port_data *port_data; + struct ctf_fs_msg_iter_data *msg_iter_data = NULL; + bt_self_message_iterator_status ret = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + port_data = bt_self_component_port_get_data( + bt_self_component_port_output_as_self_component_port( + self_port)); + BT_ASSERT(port_data); + msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1); + if (!msg_iter_data) { + ret = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + msg_iter_data->pc_msg_iter = self_msg_iter; + msg_iter_data->msg_iter = bt_msg_iter_create( + port_data->ds_file_group->ctf_fs_trace->metadata->tc, + bt_common_get_page_size() * 8, + ctf_fs_ds_file_medops, NULL); + if (!msg_iter_data->msg_iter) { + BT_LOGE_STR("Cannot create a CTF message iterator."); + ret = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + msg_iter_data->ds_file_group = port_data->ds_file_group; + if (ctf_fs_iterator_reset(msg_iter_data)) { + ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto error; + } + + bt_self_message_iterator_set_data(self_msg_iter, + msg_iter_data); + if (ret != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto error; + } + + msg_iter_data = NULL; + goto end; + +error: + bt_self_message_iterator_set_data(self_msg_iter, NULL); + +end: + ctf_fs_msg_iter_data_destroy(msg_iter_data); + return ret; +} + +BT_HIDDEN +void ctf_fs_destroy(struct ctf_fs_component *ctf_fs) +{ + if (!ctf_fs) { + return; + } + + if (ctf_fs->traces) { + g_ptr_array_free(ctf_fs->traces, TRUE); + } + + if (ctf_fs->port_data) { + g_ptr_array_free(ctf_fs->port_data, TRUE); + } + + g_free(ctf_fs); +} + +static +void port_data_destroy(struct ctf_fs_port_data *port_data) +{ + if (!port_data) { + return; + } + + g_free(port_data); +} + +static +void port_data_destroy_notifier(void *data) { + port_data_destroy(data); +} + +static +void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace) +{ + if (!ctf_fs_trace) { + return; + } + + if (ctf_fs_trace->ds_file_groups) { + g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE); + } + + BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace); + + if (ctf_fs_trace->path) { + g_string_free(ctf_fs_trace->path, TRUE); + } + + if (ctf_fs_trace->name) { + g_string_free(ctf_fs_trace->name, TRUE); + } + + if (ctf_fs_trace->metadata) { + ctf_fs_metadata_fini(ctf_fs_trace->metadata); + g_free(ctf_fs_trace->metadata); + } + + g_free(ctf_fs_trace); +} + +static +void ctf_fs_trace_destroy_notifier(void *data) +{ + struct ctf_fs_trace *trace = data; + ctf_fs_trace_destroy(trace); +} + +struct ctf_fs_component *ctf_fs_component_create(void) +{ + struct ctf_fs_component *ctf_fs; + + ctf_fs = g_new0(struct ctf_fs_component, 1); + if (!ctf_fs) { + goto error; + } + + ctf_fs->port_data = + g_ptr_array_new_with_free_func(port_data_destroy_notifier); + if (!ctf_fs->port_data) { + goto error; + } + + ctf_fs->traces = + g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier); + if (!ctf_fs->traces) { + goto error; + } + + goto end; + +error: + if (ctf_fs) { + ctf_fs_destroy(ctf_fs); + } + +end: + return ctf_fs; +} + +void ctf_fs_finalize(bt_self_component_source *component) +{ + ctf_fs_destroy(bt_self_component_get_data( + bt_self_component_source_as_self_component(component))); +} + +gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group) +{ + GString *name = g_string_new(NULL); + + /* + * The unique port name is generated by concatenating unique identifiers + * for: + * + * - the trace + * - the stream class + * - the stream + */ + + /* For the trace, use the uuid if present, else the path. */ + if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) { + char uuid_str[BABELTRACE_UUID_STR_LEN]; + + bt_uuid_unparse(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str); + g_string_assign(name, uuid_str); + } else { + g_string_assign(name, ds_file_group->ctf_fs_trace->path->str); + } + + /* + * For the stream class, use the id if present. We can omit this field + * otherwise, as there will only be a single stream class. + */ + if (ds_file_group->sc->id != UINT64_C(-1)) { + g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id); + } + + /* For the stream, use the id if present, else, use the path. */ + if (ds_file_group->stream_id != UINT64_C(-1)) { + g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id); + } else { + BT_ASSERT(ds_file_group->ds_file_infos->len == 1); + struct ctf_fs_ds_file_info *ds_file_info = + g_ptr_array_index(ds_file_group->ds_file_infos, 0); + g_string_append_printf(name, " | %s", ds_file_info->path->str); + } + + return g_string_free(name, FALSE); +} + +static +int create_one_port_for_trace(struct ctf_fs_component *ctf_fs, + struct ctf_fs_trace *ctf_fs_trace, + struct ctf_fs_ds_file_group *ds_file_group) +{ + int ret = 0; + struct ctf_fs_port_data *port_data = NULL; + gchar *port_name; + + port_name = ctf_fs_make_port_name(ds_file_group); + if (!port_name) { + goto error; + } + + BT_LOGD("Creating one port named `%s`", port_name); + + /* Create output port for this file */ + port_data = g_new0(struct ctf_fs_port_data, 1); + if (!port_data) { + goto error; + } + + port_data->ctf_fs = ctf_fs; + port_data->ds_file_group = ds_file_group; + ret = bt_self_component_source_add_output_port( + ctf_fs->self_comp, port_name, port_data, NULL); + if (ret) { + goto error; + } + + g_ptr_array_add(ctf_fs->port_data, port_data); + port_data = NULL; + goto end; + +error: + ret = -1; + +end: + if (port_name) { + g_free(port_name); + } + + port_data_destroy(port_data); + return ret; +} + +static +int create_ports_for_trace(struct ctf_fs_component *ctf_fs, + struct ctf_fs_trace *ctf_fs_trace) +{ + int ret = 0; + size_t i; + + /* Create one output port for each stream file group */ + for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { + struct ctf_fs_ds_file_group *ds_file_group = + g_ptr_array_index(ctf_fs_trace->ds_file_groups, i); + + ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace, + ds_file_group); + if (ret) { + BT_LOGE("Cannot create output port."); + goto end; + } + } + +end: + return ret; +} + +static +void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info) +{ + if (!ds_file_info) { + return; + } + + if (ds_file_info->path) { + g_string_free(ds_file_info->path, TRUE); + } + + g_free(ds_file_info); +} + +static +struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path, + int64_t begin_ns) +{ + struct ctf_fs_ds_file_info *ds_file_info; + + ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1); + if (!ds_file_info) { + goto end; + } + + ds_file_info->path = g_string_new(path); + if (!ds_file_info->path) { + ctf_fs_ds_file_info_destroy(ds_file_info); + ds_file_info = NULL; + goto end; + } + + ds_file_info->begin_ns = begin_ns; + +end: + return ds_file_info; +} + +static +void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group) +{ + if (!ds_file_group) { + return; + } + + if (ds_file_group->ds_file_infos) { + g_ptr_array_free(ds_file_group->ds_file_infos, TRUE); + } + + if (ds_file_group->index) { + if (ds_file_group->index->entries) { + g_ptr_array_free(ds_file_group->index->entries, TRUE); + } + g_free(ds_file_group->index); + } + + bt_stream_put_ref(ds_file_group->stream); + g_free(ds_file_group); +} + +static +struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create( + struct ctf_fs_trace *ctf_fs_trace, + struct ctf_stream_class *sc, + uint64_t stream_instance_id, + struct ctf_fs_ds_index *index) +{ + struct ctf_fs_ds_file_group *ds_file_group; + + ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1); + if (!ds_file_group) { + goto error; + } + + ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_fs_ds_file_info_destroy); + if (!ds_file_group->ds_file_infos) { + goto error; + } + + ds_file_group->index = index; + + ds_file_group->stream_id = stream_instance_id; + BT_ASSERT(sc); + ds_file_group->sc = sc; + ds_file_group->ctf_fs_trace = ctf_fs_trace; + goto end; + +error: + ctf_fs_ds_file_group_destroy(ds_file_group); + ctf_fs_ds_index_destroy(index); + ds_file_group = NULL; + +end: + return ds_file_group; +} + +/* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */ +static +void array_insert(GPtrArray *array, gpointer element, size_t pos) +{ + size_t original_array_len = array->len; + + /* Allocate an unused element at the end of the array. */ + g_ptr_array_add(array, NULL); + + /* If we are not inserting at the end, move the elements by one. */ + if (pos < original_array_len) { + memmove(&(array->pdata[pos + 1]), + &(array->pdata[pos]), + (original_array_len - pos) * sizeof(gpointer)); + } + + /* Insert the value. */ + array->pdata[pos] = element; +} + +/* + * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right + * place to keep it sorted. + */ + +static +void ds_file_group_insert_ds_file_info_sorted( + struct ctf_fs_ds_file_group *ds_file_group, + struct ctf_fs_ds_file_info *ds_file_info) +{ + guint i; + + /* Find the spot where to insert this ds_file_info. */ + for (i = 0; i < ds_file_group->ds_file_infos->len; i++) { + struct ctf_fs_ds_file_info *other_ds_file_info = + g_ptr_array_index(ds_file_group->ds_file_infos, i); + + if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) { + break; + } + } + + array_insert(ds_file_group->ds_file_infos, ds_file_info, i); +} + +static +void ds_file_group_insert_ds_index_entry_sorted( + struct ctf_fs_ds_file_group *ds_file_group, + struct ctf_fs_ds_index_entry *entry) +{ + guint i; + + /* Find the spot where to insert this index entry. */ + for (i = 0; i < ds_file_group->index->entries->len; i++) { + struct ctf_fs_ds_index_entry *other_entry = g_ptr_array_index( + ds_file_group->index->entries, i); + + if (entry->timestamp_begin_ns < other_entry->timestamp_begin_ns) { + break; + } + } + + array_insert(ds_file_group->index->entries, entry, i); +} + +/* + * Create a new ds_file_info using the provided path, begin_ns and index, then + * add it to ds_file_group's list of ds_file_infos. + */ + +static +int ctf_fs_ds_file_group_add_ds_file_info( + struct ctf_fs_ds_file_group *ds_file_group, + const char *path, int64_t begin_ns) +{ + struct ctf_fs_ds_file_info *ds_file_info; + int ret = 0; + + ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns); + if (!ds_file_info) { + goto error; + } + + ds_file_group_insert_ds_file_info_sorted(ds_file_group, ds_file_info); + + ds_file_info = NULL; + goto end; + +error: + ctf_fs_ds_file_info_destroy(ds_file_info); + ret = -1; +end: + return ret; +} + +static +int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, + const char *path) +{ + int64_t stream_instance_id = -1; + int64_t begin_ns = -1; + struct ctf_fs_ds_file_group *ds_file_group = NULL; + bool add_group = false; + int ret; + size_t i; + struct ctf_fs_ds_file *ds_file = NULL; + struct ctf_fs_ds_index *index = NULL; + struct bt_msg_iter *msg_iter = NULL; + struct ctf_stream_class *sc = NULL; + struct bt_msg_iter_packet_properties props; + + msg_iter = bt_msg_iter_create(ctf_fs_trace->metadata->tc, + bt_common_get_page_size() * 8, ctf_fs_ds_file_medops, NULL); + if (!msg_iter) { + BT_LOGE_STR("Cannot create a CTF message iterator."); + goto error; + } + + ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, msg_iter, + NULL, path); + if (!ds_file) { + goto error; + } + + ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props); + if (ret) { + BT_LOGE("Cannot get stream file's first packet's header and context fields (`%s`).", + path); + goto error; + } + + sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, + props.stream_class_id); + BT_ASSERT(sc); + stream_instance_id = props.data_stream_id; + + if (props.snapshots.beginning_clock != UINT64_C(-1)) { + BT_ASSERT(sc->default_clock_class); + ret = bt_util_clock_cycles_to_ns_from_origin( + props.snapshots.beginning_clock, + sc->default_clock_class->frequency, + sc->default_clock_class->offset_seconds, + sc->default_clock_class->offset_cycles, &begin_ns); + if (ret) { + BT_LOGE("Cannot convert clock cycles to nanoseconds from origin (`%s`).", + path); + goto error; + } + } + + index = ctf_fs_ds_file_build_index(ds_file); + if (!index) { + BT_LOGW("Failed to index CTF stream file \'%s\'", + ds_file->file->path->str); + } + + if (begin_ns == -1) { + /* + * No beggining timestamp to sort the stream files + * within a stream file group, so consider that this + * file must be the only one within its group. + */ + stream_instance_id = -1; + } + + if (stream_instance_id == -1) { + /* + * No stream instance ID or no beginning timestamp: + * create a unique stream file group for this stream + * file because, even if there's a stream instance ID, + * there's no timestamp to order the file within its + * group. + */ + ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, + sc, UINT64_C(-1), index); + /* Ownership of index is transferred. */ + index = NULL; + + if (!ds_file_group) { + goto error; + } + + ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, + path, begin_ns); + if (ret) { + goto error; + } + + add_group = true; + goto end; + } + + BT_ASSERT(stream_instance_id != -1); + BT_ASSERT(begin_ns != -1); + + /* Find an existing stream file group with this ID */ + for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { + ds_file_group = g_ptr_array_index( + ctf_fs_trace->ds_file_groups, i); + + if (ds_file_group->sc == sc && + ds_file_group->stream_id == + stream_instance_id) { + break; + } + + ds_file_group = NULL; + } + + if (!ds_file_group) { + ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace, + sc, stream_instance_id, index); + /* Ownership of index is transferred. */ + index = NULL; + if (!ds_file_group) { + goto error; + } + + add_group = true; + } + + ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, path, + begin_ns); + if (ret) { + goto error; + } + + goto end; + +error: + ctf_fs_ds_file_group_destroy(ds_file_group); + ds_file_group = NULL; + ret = -1; + +end: + if (add_group && ds_file_group) { + g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group); + } + + ctf_fs_ds_file_destroy(ds_file); + + if (msg_iter) { + bt_msg_iter_destroy(msg_iter); + } + + ctf_fs_ds_index_destroy(index); + return ret; +} + +static +int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace) +{ + int ret = 0; + const char *basename; + GError *error = NULL; + GDir *dir = NULL; + + /* Check each file in the path directory, except specific ones */ + dir = g_dir_open(ctf_fs_trace->path->str, 0, &error); + if (!dir) { + BT_LOGE("Cannot open directory `%s`: %s (code %d)", + ctf_fs_trace->path->str, error->message, + error->code); + goto error; + } + + while ((basename = g_dir_read_name(dir))) { + struct ctf_fs_file *file; + + if (!strcmp(basename, CTF_FS_METADATA_FILENAME)) { + /* Ignore the metadata stream. */ + BT_LOGD("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`", + ctf_fs_trace->path->str, basename); + continue; + } + + if (basename[0] == '.') { + BT_LOGD("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`", + ctf_fs_trace->path->str, basename); + continue; + } + + /* Create the file. */ + file = ctf_fs_file_create(); + if (!file) { + BT_LOGE("Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`", + ctf_fs_trace->path->str, basename); + goto error; + } + + /* Create full path string. */ + g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s", + ctf_fs_trace->path->str, basename); + if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) { + BT_LOGD("Ignoring non-regular file `%s`", + file->path->str); + ctf_fs_file_destroy(file); + file = NULL; + continue; + } + + ret = ctf_fs_file_open(file, "rb"); + if (ret) { + BT_LOGE("Cannot open stream file `%s`", file->path->str); + goto error; + } + + if (file->size == 0) { + /* Skip empty stream. */ + BT_LOGD("Ignoring empty file `%s`", file->path->str); + ctf_fs_file_destroy(file); + continue; + } + + ret = add_ds_file_to_ds_file_group(ctf_fs_trace, + file->path->str); + if (ret) { + BT_LOGE("Cannot add stream file `%s` to stream file group", + file->path->str); + ctf_fs_file_destroy(file); + goto error; + } + + ctf_fs_file_destroy(file); + } + + goto end; + +error: + ret = -1; + +end: + if (dir) { + g_dir_close(dir); + dir = NULL; + } + + if (error) { + g_error_free(error); + } + + return ret; +} + +static +int set_trace_name(bt_trace *trace, const char *name_suffix) +{ + int ret = 0; + const bt_trace_class *tc = bt_trace_borrow_class_const(trace); + const bt_value *val; + GString *name; + + name = g_string_new(NULL); + if (!name) { + BT_LOGE_STR("Failed to allocate a GString."); + ret = -1; + goto end; + } + + /* + * Check if we have a trace environment string value named `hostname`. + * If so, use it as the trace name's prefix. + */ + val = bt_trace_class_borrow_environment_entry_value_by_name_const( + tc, "hostname"); + if (val && bt_value_is_string(val)) { + g_string_append(name, bt_value_string_get(val)); + + if (name_suffix) { + g_string_append_c(name, G_DIR_SEPARATOR); + } + } + + if (name_suffix) { + g_string_append(name, name_suffix); + } + + ret = bt_trace_set_name(trace, name->str); + if (ret) { + goto end; + } + + goto end; + +end: + if (name) { + g_string_free(name, TRUE); + } + + return ret; +} + +static +struct ctf_fs_trace *ctf_fs_trace_create(bt_self_component_source *self_comp, + const char *path, const char *name, + struct ctf_fs_metadata_config *metadata_config) +{ + struct ctf_fs_trace *ctf_fs_trace; + int ret; + + ctf_fs_trace = g_new0(struct ctf_fs_trace, 1); + if (!ctf_fs_trace) { + goto end; + } + + ctf_fs_trace->path = g_string_new(path); + if (!ctf_fs_trace->path) { + goto error; + } + + ctf_fs_trace->name = g_string_new(name); + if (!ctf_fs_trace->name) { + goto error; + } + + ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1); + if (!ctf_fs_trace->metadata) { + goto error; + } + + ctf_fs_metadata_init(ctf_fs_trace->metadata); + ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_fs_ds_file_group_destroy); + if (!ctf_fs_trace->ds_file_groups) { + goto error; + } + + ret = ctf_fs_metadata_set_trace_class(self_comp, + ctf_fs_trace, metadata_config); + if (ret) { + goto error; + } + + if (ctf_fs_trace->metadata->trace_class) { + ctf_fs_trace->trace = + bt_trace_create(ctf_fs_trace->metadata->trace_class); + if (!ctf_fs_trace->trace) { + goto error; + } + } + + if (ctf_fs_trace->trace) { + ret = set_trace_name(ctf_fs_trace->trace, name); + if (ret) { + goto error; + } + } + + ret = create_ds_file_groups(ctf_fs_trace); + if (ret) { + goto error; + } + + goto end; + +error: + ctf_fs_trace_destroy(ctf_fs_trace); + ctf_fs_trace = NULL; + +end: + return ctf_fs_trace; +} + +static +int path_is_ctf_trace(const char *path) +{ + GString *metadata_path = g_string_new(NULL); + int ret = 0; + + if (!metadata_path) { + ret = -1; + goto end; + } + + g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME); + + if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) { + ret = 1; + goto end; + } + +end: + g_string_free(metadata_path, TRUE); + return ret; +} + +static +int add_trace_path(GList **trace_paths, const char *path) +{ + GString *norm_path = NULL; + int ret = 0; + + norm_path = bt_common_normalize_path(path, NULL); + if (!norm_path) { + BT_LOGE("Failed to normalize path `%s`.", path); + ret = -1; + goto end; + } + + // FIXME: Remove or ifdef for __MINGW32__ + if (strcmp(norm_path->str, "/") == 0) { + BT_LOGE("Opening a trace in `/` is not supported."); + ret = -1; + goto end; + } + + *trace_paths = g_list_prepend(*trace_paths, norm_path); + BT_ASSERT(*trace_paths); + norm_path = NULL; + +end: + if (norm_path) { + g_string_free(norm_path, TRUE); + } + + return ret; +} + +static +int ctf_fs_find_traces(GList **trace_paths, const char *start_path) +{ + int ret; + GError *error = NULL; + GDir *dir = NULL; + const char *basename = NULL; + + /* Check if the starting path is a CTF trace itself */ + ret = path_is_ctf_trace(start_path); + if (ret < 0) { + goto end; + } + + if (ret) { + /* + * Stop recursion: a CTF trace cannot contain another + * CTF trace. + */ + ret = add_trace_path(trace_paths, start_path); + goto end; + } + + /* Look for subdirectories */ + if (!g_file_test(start_path, G_FILE_TEST_IS_DIR)) { + /* Starting path is not a directory: end of recursion */ + goto end; + } + + dir = g_dir_open(start_path, 0, &error); + if (!dir) { + if (error->code == G_FILE_ERROR_ACCES) { + BT_LOGD("Cannot open directory `%s`: %s (code %d): continuing", + start_path, error->message, error->code); + goto end; + } + + BT_LOGE("Cannot open directory `%s`: %s (code %d)", + start_path, error->message, error->code); + ret = -1; + goto end; + } + + while ((basename = g_dir_read_name(dir))) { + GString *sub_path = g_string_new(NULL); + + if (!sub_path) { + ret = -1; + goto end; + } + + g_string_printf(sub_path, "%s" G_DIR_SEPARATOR_S "%s", start_path, basename); + ret = ctf_fs_find_traces(trace_paths, sub_path->str); + g_string_free(sub_path, TRUE); + if (ret) { + goto end; + } + } + +end: + if (dir) { + g_dir_close(dir); + } + + if (error) { + g_error_free(error); + } + + return ret; +} + +static +GList *ctf_fs_create_trace_names(GList *trace_paths, const char *base_path) { + GList *trace_names = NULL; + GList *node; + const char *last_sep; + size_t base_dist; + + /* + * At this point we know that all the trace paths are + * normalized, and so is the base path. This means that + * they are absolute and they don't end with a separator. + * We can simply find the location of the last separator + * in the base path, which gives us the name of the actual + * directory to look into, and use this location as the + * start of each trace name within each trace path. + * + * For example: + * + * Base path: /home/user/my-traces/some-trace + * Trace paths: + * - /home/user/my-traces/some-trace/host1/trace1 + * - /home/user/my-traces/some-trace/host1/trace2 + * - /home/user/my-traces/some-trace/host2/trace + * - /home/user/my-traces/some-trace/other-trace + * + * In this case the trace names are: + * + * - some-trace/host1/trace1 + * - some-trace/host1/trace2 + * - some-trace/host2/trace + * - some-trace/other-trace + */ + last_sep = strrchr(base_path, G_DIR_SEPARATOR); + + /* We know there's at least one separator */ + BT_ASSERT(last_sep); + + /* Distance to base */ + base_dist = last_sep - base_path + 1; + + /* Create the trace names */ + for (node = trace_paths; node; node = g_list_next(node)) { + GString *trace_name = g_string_new(NULL); + GString *trace_path = node->data; + + BT_ASSERT(trace_name); + g_string_assign(trace_name, &trace_path->str[base_dist]); + trace_names = g_list_append(trace_names, trace_name); + } + + return trace_names; +} + +/* Helper for ctf_fs_component_create_ctf_fs_traces, to handle a single path/root. */ + +static +int ctf_fs_component_create_ctf_fs_traces_one_root(bt_self_component_source *self_comp, + struct ctf_fs_component *ctf_fs, + const char *path_param) +{ + struct ctf_fs_trace *ctf_fs_trace = NULL; + int ret = 0; + GString *norm_path = NULL; + GList *trace_paths = NULL; + GList *trace_names = NULL; + GList *tp_node; + GList *tn_node; + + norm_path = bt_common_normalize_path(path_param, NULL); + if (!norm_path) { + BT_LOGE("Failed to normalize path: `%s`.", + path_param); + goto error; + } + + ret = ctf_fs_find_traces(&trace_paths, norm_path->str); + if (ret) { + goto error; + } + + if (!trace_paths) { + BT_LOGE("No CTF traces recursively found in `%s`.", + path_param); + goto error; + } + + trace_names = ctf_fs_create_trace_names(trace_paths, norm_path->str); + if (!trace_names) { + BT_LOGE("Cannot create trace names from trace paths."); + goto error; + } + + for (tp_node = trace_paths, tn_node = trace_names; tp_node; + tp_node = g_list_next(tp_node), + tn_node = g_list_next(tn_node)) { + GString *trace_path = tp_node->data; + GString *trace_name = tn_node->data; + + ctf_fs_trace = ctf_fs_trace_create(self_comp, + trace_path->str, trace_name->str, + &ctf_fs->metadata_config); + if (!ctf_fs_trace) { + BT_LOGE("Cannot create trace for `%s`.", + trace_path->str); + goto error; + } + + g_ptr_array_add(ctf_fs->traces, ctf_fs_trace); + ctf_fs_trace = NULL; + } + + goto end; + +error: + ret = -1; + ctf_fs_trace_destroy(ctf_fs_trace); + +end: + for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) { + if (tp_node->data) { + g_string_free(tp_node->data, TRUE); + } + } + + for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) { + if (tn_node->data) { + g_string_free(tn_node->data, TRUE); + } + } + + if (trace_paths) { + g_list_free(trace_paths); + } + + if (trace_names) { + g_list_free(trace_names); + } + + if (norm_path) { + g_string_free(norm_path, TRUE); + } + + return ret; +} + +/* GCompareFunc to sort traces by UUID. */ + +static +gint sort_traces_by_uuid(gconstpointer a, gconstpointer b) +{ + const struct ctf_fs_trace *trace_a = *((const struct ctf_fs_trace **) a); + const struct ctf_fs_trace *trace_b = *((const struct ctf_fs_trace **) b); + + bool trace_a_has_uuid = trace_a->metadata->tc->is_uuid_set; + bool trace_b_has_uuid = trace_b->metadata->tc->is_uuid_set; + gint ret; + + /* Order traces without uuid first. */ + if (!trace_a_has_uuid && trace_b_has_uuid) { + ret = -1; + } else if (trace_a_has_uuid && !trace_b_has_uuid) { + ret = 1; + } else if (!trace_a_has_uuid && !trace_b_has_uuid) { + ret = 0; + } else { + ret = bt_uuid_compare(trace_a->metadata->tc->uuid, trace_b->metadata->tc->uuid); + } + + return ret; +} + +/* + * Count the number of stream and event classes defined by this trace's metadata. + * + * This is used to determine which metadata is the "latest", out of multiple + * traces sharing the same UUID. It is assumed that amongst all these metadatas, + * a bigger metadata is a superset of a smaller metadata. Therefore, it is + * enough to just count the classes. + */ + +static +unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace) +{ + unsigned int num = trace->metadata->tc->stream_classes->len; + guint i; + + for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) { + struct ctf_stream_class *sc = trace->metadata->tc->stream_classes->pdata[i]; + num += sc->event_classes->len; + } + + return num; +} + +/* + * Merge the src ds_file_group into dest. This consists of merging their + * ds_file_infos, making sure to keep the result sorted. + */ + +static +void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest, struct ctf_fs_ds_file_group *src) +{ + guint i; + + for (i = 0; i < src->ds_file_infos->len; i++) { + struct ctf_fs_ds_file_info *ds_file_info = + g_ptr_array_index(src->ds_file_infos, i); + + /* Ownership of the ds_file_info is transferred to dest. */ + g_ptr_array_index(src->ds_file_infos, i) = NULL; + + ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info); + } + + /* Merge both indexes. */ + for (i = 0; i < src->index->entries->len; i++) { + struct ctf_fs_ds_index_entry *entry = g_ptr_array_index( + src->index->entries, i); + + /* + * Ownership of the ctf_fs_ds_index_entry is transferred to + * dest. + */ + g_ptr_array_index(src->index->entries, i) = NULL; + + ds_file_group_insert_ds_index_entry_sorted(dest, entry); + } +} +/* Merge src_trace's data stream file groups into dest_trace's. */ + +static +int merge_matching_ctf_fs_ds_file_groups( + struct ctf_fs_trace *dest_trace, + struct ctf_fs_trace *src_trace) +{ + + GPtrArray *dest = dest_trace->ds_file_groups; + GPtrArray *src = src_trace->ds_file_groups; + guint s_i; + int ret = 0; + + /* + * Save the initial length of dest: we only want to check against the + * original elements in the inner loop. + */ + const guint dest_len = dest->len; + + for (s_i = 0; s_i < src->len; s_i++) { + struct ctf_fs_ds_file_group *src_group = g_ptr_array_index(src, s_i); + struct ctf_fs_ds_file_group *dest_group = NULL; + + /* A stream instance without ID can't match a stream in the other trace. */ + if (src_group->stream_id != -1) { + guint d_i; + + /* Let's search for a matching ds_file_group in the destination. */ + for (d_i = 0; d_i < dest_len; d_i++) { + struct ctf_fs_ds_file_group *candidate_dest = g_ptr_array_index(dest, d_i); + + /* Can't match a stream instance without ID. */ + if (candidate_dest->stream_id == -1) { + continue; + } + + /* + * If the two groups have the same stream instance id + * and belong to the same stream class (stream instance + * ids are per-stream class), they represent the same + * stream instance. + */ + if (candidate_dest->stream_id != src_group->stream_id || + candidate_dest->sc->id != src_group->sc->id) { + continue; + } + + dest_group = candidate_dest; + break; + } + } + + /* + * Didn't find a friend in dest to merge our src_group into? + * Create a new empty one. This can happen if a stream was + * active in the source trace chunk but not in the destination + * trace chunk. + */ + if (!dest_group) { + struct ctf_stream_class *sc; + struct ctf_fs_ds_index *index; + + sc = ctf_trace_class_borrow_stream_class_by_id( + dest_trace->metadata->tc, src_group->sc->id); + BT_ASSERT(sc); + + index = ctf_fs_ds_index_create(); + if (!index) { + ret = -1; + goto end; + } + + dest_group = ctf_fs_ds_file_group_create(dest_trace, sc, + src_group->stream_id, index); + /* Ownership of index is transferred. */ + index = NULL; + if (!dest_group) { + ret = -1; + goto end; + } + + g_ptr_array_add(dest_trace->ds_file_groups, dest_group); + } + + BT_ASSERT(dest_group); + merge_ctf_fs_ds_file_groups(dest_group, src_group); + } + +end: + return ret; +} + +/* + * Collapse the given traces, which must all share the same UUID, in a single + * one. + * + * The trace with the most expansive metadata is chosen and all other traces + * are merged into that one. The array slots of all the traces that get merged + * in the chosen one are set to NULL, so only the slot of the chosen trace + * remains non-NULL. + */ + +static +int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces) +{ + unsigned int winner_count; + struct ctf_fs_trace *winner; + guint i; + int ret = 0; + char uuid_str[BABELTRACE_UUID_STR_LEN]; + + BT_ASSERT(num_traces >= 2); + + winner_count = metadata_count_stream_and_event_classes(traces[0]); + winner = traces[0]; + + /* Find the trace with the largest metadata. */ + for (i = 1; i < num_traces; i++) { + struct ctf_fs_trace *candidate; + unsigned int candidate_count; + + candidate = traces[i]; + + /* A bit of sanity check. */ + BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0); + + candidate_count = metadata_count_stream_and_event_classes(candidate); + + if (candidate_count > winner_count) { + winner_count = candidate_count; + winner = candidate; + } + } + + /* Merge all the other traces in the winning trace. */ + for (i = 0; i < num_traces; i++) { + struct ctf_fs_trace *trace = traces[i]; + + /* Don't merge the winner into itself. */ + if (trace == winner) { + continue; + } + + /* Merge trace's data stream file groups into winner's. */ + ret = merge_matching_ctf_fs_ds_file_groups(winner, trace); + if (ret) { + goto end; + } + + /* Free the trace that got merged into winner, clear the slot in the array. */ + ctf_fs_trace_destroy(trace); + traces[i] = NULL; + } + + /* Use the string representation of the UUID as the trace name. */ + bt_uuid_unparse(winner->metadata->tc->uuid, uuid_str); + g_string_printf(winner->name, "%s", uuid_str); + +end: + return ret; +} + +/* + * Merge all traces of `ctf_fs` that share the same UUID in a single trace. + * Traces with no UUID are not merged. + */ + +static +int merge_traces_with_same_uuid(struct ctf_fs_component *ctf_fs) +{ + GPtrArray *traces = ctf_fs->traces; + guint range_start_idx = 0; + unsigned int num_traces = 0; + guint i; + int ret = 0; + + /* Sort the traces by uuid, then collapse traces with the same uuid in a single one. */ + g_ptr_array_sort(traces, sort_traces_by_uuid); + + /* Find ranges of consecutive traces that share the same UUID. */ + while (range_start_idx < traces->len) { + guint range_len; + struct ctf_fs_trace *range_start_trace = g_ptr_array_index(traces, range_start_idx); + + /* Exclusive end of range. */ + guint range_end_exc_idx = range_start_idx + 1; + + while (range_end_exc_idx < traces->len) { + struct ctf_fs_trace *this_trace = g_ptr_array_index(traces, range_end_exc_idx); + + if (!range_start_trace->metadata->tc->is_uuid_set || + (bt_uuid_compare(range_start_trace->metadata->tc->uuid, this_trace->metadata->tc->uuid) != 0)) { + break; + } + + range_end_exc_idx++; + } + + /* If we have two or more traces with matching UUIDs, merge them. */ + range_len = range_end_exc_idx - range_start_idx; + if (range_len > 1) { + struct ctf_fs_trace **range_start = (struct ctf_fs_trace **) &traces->pdata[range_start_idx]; + ret = merge_ctf_fs_traces(range_start, range_len); + if (ret) { + goto end; + } + } + + num_traces++; + range_start_idx = range_end_exc_idx; + } + + /* Clear any NULL slot (traces that got merged in another one) in the array. */ + for (i = 0; i < traces->len;) { + if (g_ptr_array_index(traces, i) == NULL) { + g_ptr_array_remove_index_fast(traces, i); + } else { + i++; + } + } + + BT_ASSERT(num_traces == traces->len); + +end: + return ret; +} + +int ctf_fs_component_create_ctf_fs_traces(bt_self_component_source *self_comp, + struct ctf_fs_component *ctf_fs, + const bt_value *paths_value) +{ + int ret = 0; + uint64_t i; + + for (i = 0; i < bt_value_array_get_size(paths_value); i++) { + const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i); + const char *path = bt_value_string_get(path_value); + + ret = ctf_fs_component_create_ctf_fs_traces_one_root(self_comp, ctf_fs, path); + if (ret) { + goto end; + } + } + + ret = merge_traces_with_same_uuid(ctf_fs); + +end: + return ret; +} + +static +GString *get_stream_instance_unique_name( + struct ctf_fs_ds_file_group *ds_file_group) +{ + GString *name; + struct ctf_fs_ds_file_info *ds_file_info; + + name = g_string_new(NULL); + if (!name) { + goto end; + } + + /* + * If there's more than one stream file in the stream file + * group, the first (earliest) stream file's path is used as + * the stream's unique name. + */ + BT_ASSERT(ds_file_group->ds_file_infos->len > 0); + ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0); + g_string_assign(name, ds_file_info->path->str); + +end: + return name; +} + +/* Create the IR stream objects for ctf_fs_trace. */ + +static +int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace) +{ + int ret; + GString *name = NULL; + guint i; + + for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) { + struct ctf_fs_ds_file_group *ds_file_group = + g_ptr_array_index(ctf_fs_trace->ds_file_groups, i); + name = get_stream_instance_unique_name(ds_file_group); + + if (!name) { + goto error; + } + + if (ds_file_group->sc->ir_sc) { + BT_ASSERT(ctf_fs_trace->trace); + + if (ds_file_group->stream_id == UINT64_C(-1)) { + /* No stream ID: use 0 */ + ds_file_group->stream = bt_stream_create_with_id( + ds_file_group->sc->ir_sc, + ctf_fs_trace->trace, + ctf_fs_trace->next_stream_id); + ctf_fs_trace->next_stream_id++; + } else { + /* Specific stream ID */ + ds_file_group->stream = bt_stream_create_with_id( + ds_file_group->sc->ir_sc, + ctf_fs_trace->trace, + (uint64_t) ds_file_group->stream_id); + } + } else { + ds_file_group->stream = NULL; + } + + if (!ds_file_group->stream) { + BT_LOGE("Cannot create stream for DS file group: " + "addr=%p, stream-name=\"%s\"", + ds_file_group, name->str); + goto error; + } + + ret = bt_stream_set_name(ds_file_group->stream, + name->str); + if (ret) { + BT_LOGE("Cannot set stream's name: " + "addr=%p, stream-name=\"%s\"", + ds_file_group->stream, name->str); + goto error; + } + + g_string_free(name, TRUE); + name = NULL; + } + + ret = 0; + goto end; + +error: + ret = -1; + +end: + + if (name) { + g_string_free(name, TRUE); + } + return ret; +} + +/* + * Validate the "paths" parameter passed to this component. It must be + * present, and it must be an array of strings. + */ + +static +bool validate_paths_parameter(const bt_value *paths) +{ + bool ret; + bt_value_type type; + uint64_t i; + + if (!paths) { + BT_LOGE("missing \"paths\" parameter"); + goto error; + } + + type = bt_value_get_type(paths); + if (type != BT_VALUE_TYPE_ARRAY) { + BT_LOGE("`paths` parameter: expecting array value: type=%s", + bt_common_value_type_string(type)); + goto error; + } + + for (i = 0; i < bt_value_array_get_size(paths); i++) { + const bt_value *elem; + + elem = bt_value_array_borrow_element_by_index_const(paths, i); + type = bt_value_get_type(elem); + if (type != BT_VALUE_TYPE_STRING) { + BT_LOGE("`paths` parameter: expecting string value: index=%" PRIu64 ", type=%s", + i, bt_common_value_type_string(type)); + goto error; + } + } + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +bool read_src_fs_parameters(const bt_value *params, + const bt_value **paths, struct ctf_fs_component *ctf_fs) { + bool ret; + const bt_value *value; + + /* paths parameter */ + *paths = bt_value_map_borrow_entry_value_const(params, "paths"); + if (!validate_paths_parameter(*paths)) { + goto error; + } + + /* clock-class-offset-s parameter */ + value = bt_value_map_borrow_entry_value_const(params, + "clock-class-offset-s"); + if (value) { + if (!bt_value_is_signed_integer(value)) { + BT_LOGE("clock-class-offset-s must be an integer"); + goto error; + } + ctf_fs->metadata_config.clock_class_offset_s = + bt_value_signed_integer_get(value); + } + + /* clock-class-offset-ns parameter */ + value = bt_value_map_borrow_entry_value_const(params, + "clock-class-offset-ns"); + if (value) { + if (!bt_value_is_signed_integer(value)) { + BT_LOGE("clock-class-offset-ns must be an integer"); + goto error; + } + ctf_fs->metadata_config.clock_class_offset_ns = + bt_value_signed_integer_get(value); + } + + + ret = true; + goto end; + +error: + ret = false; + +end: + return ret; +} + +static +struct ctf_fs_component *ctf_fs_create( + bt_self_component_source *self_comp, + const bt_value *params) +{ + struct ctf_fs_component *ctf_fs = NULL; + guint i; + const bt_value *paths_value; + + ctf_fs = ctf_fs_component_create(); + if (!ctf_fs) { + goto error; + } + + if (!read_src_fs_parameters(params, &paths_value, ctf_fs)) { + goto error; + } + + bt_self_component_set_data( + bt_self_component_source_as_self_component(self_comp), + ctf_fs); + + /* + * We don't need to get a new reference here because as long as + * our private ctf_fs_component object exists, the containing + * private component should also exist. + */ + ctf_fs->self_comp = self_comp; + + if (ctf_fs_component_create_ctf_fs_traces(self_comp, ctf_fs, paths_value)) { + goto error; + } + + for (i = 0; i < ctf_fs->traces->len; i++) { + struct ctf_fs_trace *trace = g_ptr_array_index(ctf_fs->traces, i); + + if (create_streams_for_trace(trace)) { + goto error; + } + + if (create_ports_for_trace(ctf_fs, trace)) { + goto error; + } + } + + goto end; + +error: + ctf_fs_destroy(ctf_fs); + ctf_fs = NULL; + bt_self_component_set_data( + bt_self_component_source_as_self_component(self_comp), + NULL); + +end: + return ctf_fs; +} + +BT_HIDDEN +bt_self_component_status ctf_fs_init( + bt_self_component_source *self_comp, + const bt_value *params, UNUSED_VAR void *init_method_data) +{ + struct ctf_fs_component *ctf_fs; + bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; + + ctf_fs = ctf_fs_create(self_comp, params); + if (!ctf_fs) { + ret = BT_SELF_COMPONENT_STATUS_ERROR; + } + + return ret; +} + +BT_HIDDEN +bt_query_status ctf_fs_query( + bt_self_component_class_source *comp_class, + const bt_query_executor *query_exec, + const char *object, const bt_value *params, + const bt_value **result) +{ + bt_query_status status = BT_QUERY_STATUS_OK; + + if (!strcmp(object, "metadata-info")) { + status = metadata_info_query(comp_class, params, result); + } else if (!strcmp(object, "trace-info")) { + status = trace_info_query(comp_class, params, result); + } else { + BT_LOGE("Unknown query object `%s`", object); + status = BT_QUERY_STATUS_INVALID_OBJECT; + goto end; + } +end: + return status; +} diff --git a/src/plugins/ctf/fs-src/fs.h b/src/plugins/ctf/fs-src/fs.h new file mode 100644 index 00000000..33e1f568 --- /dev/null +++ b/src/plugins/ctf/fs-src/fs.h @@ -0,0 +1,265 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_H +#define BABELTRACE_PLUGIN_CTF_FS_H + +/* + * BabelTrace - CTF on File System Component + * + * Copyright 2016 Jérémie Galarneau + * Copyright 2016 Philippe Proulx + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include +#include "data-stream-file.h" +#include "metadata.h" +#include "../common/metadata/decoder.h" + +BT_HIDDEN +extern bool ctf_fs_debug; + +struct ctf_fs_file { + /* Owned by this */ + GString *path; + + /* Owned by this */ + FILE *fp; + + off_t size; +}; + +struct ctf_fs_metadata { + /* Owned by this */ + struct ctf_metadata_decoder *decoder; + + /* Owned by this */ + bt_trace_class *trace_class; + + /* Weak (owned by `decoder` above) */ + struct ctf_trace_class *tc; + + /* Owned by this */ + + /* Owned by this */ + char *text; + + int bo; +}; + +struct ctf_fs_component { + /* Weak, guaranteed to exist */ + bt_self_component_source *self_comp; + + /* Array of struct ctf_fs_port_data *, owned by this */ + GPtrArray *port_data; + + /* Array of struct ctf_fs_trace *, owned by this */ + GPtrArray *traces; + + struct ctf_fs_metadata_config metadata_config; +}; + +struct ctf_fs_trace { + /* Owned by this */ + struct ctf_fs_metadata *metadata; + + /* Owned by this */ + bt_trace *trace; + + /* Array of struct ctf_fs_ds_file_group *, owned by this */ + GPtrArray *ds_file_groups; + + /* Owned by this */ + GString *path; + + /* Owned by this */ + GString *name; + + /* Next automatic stream ID when not provided by packet header */ + uint64_t next_stream_id; +}; + +struct ctf_fs_ds_index_entry { + /* Position, in bytes, of the packet from the beginning of the file. */ + uint64_t offset; + + /* Size of the packet, in bytes. */ + uint64_t packet_size; + + /* + * Extracted from the packet context, relative to the respective fields' + * mapped clock classes (in cycles). + */ + uint64_t timestamp_begin, timestamp_end; + + /* + * Converted from the packet context, relative to the trace's EPOCH + * (in ns since EPOCH). + */ + int64_t timestamp_begin_ns, timestamp_end_ns; +}; + +struct ctf_fs_ds_index { + /* Array of pointer to struct ctf_fs_fd_index_entry. */ + GPtrArray *entries; +}; + +struct ctf_fs_ds_file_group { + /* + * Array of struct ctf_fs_ds_file_info, owned by this. + * + * This is an _ordered_ array of data stream file infos which + * belong to this group (a single stream instance). + * + * You can call ctf_fs_ds_file_create() with one of those paths + * and the trace IR stream below. + */ + GPtrArray *ds_file_infos; + + /* Owned by this */ + struct ctf_stream_class *sc; + + /* Owned by this */ + bt_stream *stream; + + /* Stream (instance) ID; -1ULL means none */ + uint64_t stream_id; + + /* Weak, belongs to component */ + struct ctf_fs_trace *ctf_fs_trace; + + /* + * Owned by this. May be NULL. + * + * A stream cannot be assumed to be indexed as the indexing might have + * been skipped. Moreover, the index's fields may not all be available + * depending on the producer (e.g. timestamp_begin/end are not + * mandatory). + */ + struct ctf_fs_ds_index *index; +}; + +struct ctf_fs_port_data { + /* Weak, belongs to ctf_fs_trace */ + struct ctf_fs_ds_file_group *ds_file_group; + + /* Weak */ + struct ctf_fs_component *ctf_fs; +}; + +struct ctf_fs_msg_iter_data { + /* Weak */ + bt_self_message_iterator *pc_msg_iter; + + /* Weak, belongs to ctf_fs_trace */ + struct ctf_fs_ds_file_group *ds_file_group; + + /* Owned by this */ + struct ctf_fs_ds_file *ds_file; + + /* Which file the iterator is _currently_ operating on */ + size_t ds_file_info_index; + + /* Owned by this */ + struct bt_msg_iter *msg_iter; +}; + +BT_HIDDEN +bt_self_component_status ctf_fs_init( + bt_self_component_source *source, + const bt_value *params, void *init_method_data); + +BT_HIDDEN +void ctf_fs_finalize(bt_self_component_source *component); + +BT_HIDDEN +bt_query_status ctf_fs_query( + bt_self_component_class_source *comp_class, + const bt_query_executor *query_exec, + const char *object, const bt_value *params, + const bt_value **result); + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_source *self_comp, + bt_self_component_port_output *self_port); + +BT_HIDDEN +void ctf_fs_iterator_finalize(bt_self_message_iterator *it); + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_next( + bt_self_message_iterator *iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +BT_HIDDEN +bt_self_message_iterator_status ctf_fs_iterator_seek_beginning( + bt_self_message_iterator *message_iterator); + +/* Create and initialize a new, empty ctf_fs_component. */ + +BT_HIDDEN +struct ctf_fs_component *ctf_fs_component_create(void); + +/* + * Search recursively under all paths in `paths_value` (an array of strings), + * for CTF traces. For each CTF trace found, create a ctf_fs_trace in + * `ctf_fs` representing that trace. + */ + +BT_HIDDEN +int ctf_fs_component_create_ctf_fs_traces(bt_self_component_source *self_comp, + struct ctf_fs_component *ctf_fs, + const bt_value *paths_value); + +/* Free `ctf_fs` and everything it owns. */ + +BT_HIDDEN +void ctf_fs_destroy(struct ctf_fs_component *ctf_fs); + +/* + * Read and validate parameters taken by the src.ctf.fs plugin. + * + * - The mandatory `paths` parameter is returned in `*paths`. + * - The optional `clock-class-offset-s` and `clock-class-offset-ns`, if + * present, are recorded in the `ctf_fs` structure. + * + * Return true on success, false if any parameter didn't pass validation. + */ + +BT_HIDDEN +bool read_src_fs_parameters(const bt_value *params, + const bt_value **paths, struct ctf_fs_component *ctf_fs); + +/* + * Generate the port name to be used for a given data stream file group. + * + * The result must be freed using g_free by the caller. + */ + +BT_HIDDEN +gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_H */ diff --git a/src/plugins/ctf/fs-src/logging.c b/src/plugins/ctf/fs-src/logging.c new file mode 100644 index 00000000..e9e58629 --- /dev/null +++ b/src/plugins/ctf/fs-src/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL ctf_fs_src_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(BT_LOG_OUTPUT_LEVEL, "BABELTRACE_SRC_CTF_FS_LOG_LEVEL"); diff --git a/src/plugins/ctf/fs-src/logging.h b/src/plugins/ctf/fs-src/logging.h new file mode 100644 index 00000000..2f52057b --- /dev/null +++ b/src/plugins/ctf/fs-src/logging.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CTF_FS_SRC_LOGGING_H +#define CTF_FS_SRC_LOGGING_H + +#define BT_LOG_OUTPUT_LEVEL ctf_fs_src_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_fs_src_log_level); + +#endif /* CTF_FS_SRC_LOGGING_H */ diff --git a/src/plugins/ctf/fs-src/lttng-index.h b/src/plugins/ctf/fs-src/lttng-index.h new file mode 100644 index 00000000..23747719 --- /dev/null +++ b/src/plugins/ctf/fs-src/lttng-index.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 - Julien Desfossez + * Mathieu Desnoyers + * David Goulet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LTTNG_INDEX_H +#define LTTNG_INDEX_H + +#include "compat/limits.h" + +#define CTF_INDEX_MAGIC 0xC1F1DCC1 +#define CTF_INDEX_MAJOR 1 +#define CTF_INDEX_MINOR 1 + +/* + * Header at the beginning of each index file. + * All integer fields are stored in big endian. + */ +struct ctf_packet_index_file_hdr { + uint32_t magic; + uint32_t index_major; + uint32_t index_minor; + /* size of struct ctf_packet_index, in bytes. */ + uint32_t packet_index_len; +} __attribute__((__packed__)); + +/* + * Packet index generated for each trace packet store in a trace file. + * All integer fields are stored in big endian. + */ +struct ctf_packet_index { + uint64_t offset; /* offset of the packet in the file, in bytes */ + uint64_t packet_size; /* packet size, in bits */ + uint64_t content_size; /* content size, in bits */ + uint64_t timestamp_begin; + uint64_t timestamp_end; + uint64_t events_discarded; + uint64_t stream_id; + /* CTF_INDEX 1.0 limit */ + uint64_t stream_instance_id; /* ID of the channel instance */ + uint64_t packet_seq_num; /* packet sequence number */ +} __attribute__((__packed__)); + +#endif /* LTTNG_INDEX_H */ diff --git a/src/plugins/ctf/fs-src/metadata.c b/src/plugins/ctf/fs-src/metadata.c new file mode 100644 index 00000000..30744612 --- /dev/null +++ b/src/plugins/ctf/fs-src/metadata.c @@ -0,0 +1,159 @@ +/* + * Copyright 2016 - Philippe Proulx + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Some functions are based on older functions written by Mathieu Desnoyers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/assert.h" +#include +#include "compat/uuid.h" +#include "compat/memstream.h" +#include + +#include "fs.h" +#include "file.h" +#include "metadata.h" +#include "../common/metadata/decoder.h" + +#define BT_LOG_TAG "PLUGIN-CTF-FS-METADATA-SRC" +#include "logging.h" + +BT_HIDDEN +FILE *ctf_fs_metadata_open_file(const char *trace_path) +{ + GString *metadata_path; + FILE *fp = NULL; + + metadata_path = g_string_new(trace_path); + if (!metadata_path) { + goto end; + } + + g_string_append(metadata_path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME); + fp = fopen(metadata_path->str, "rb"); + g_string_free(metadata_path, TRUE); +end: + return fp; +} + +static struct ctf_fs_file *get_file(const char *trace_path) +{ + struct ctf_fs_file *file = ctf_fs_file_create(); + + if (!file) { + goto error; + } + + g_string_append(file->path, trace_path); + g_string_append(file->path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME); + + if (ctf_fs_file_open(file, "rb")) { + goto error; + } + + goto end; + +error: + if (file) { + ctf_fs_file_destroy(file); + file = NULL; + } + +end: + return file; +} + +BT_HIDDEN +int ctf_fs_metadata_set_trace_class( + bt_self_component_source *self_comp, + struct ctf_fs_trace *ctf_fs_trace, + struct ctf_fs_metadata_config *config) +{ + int ret = 0; + struct ctf_fs_file *file = NULL; + struct ctf_metadata_decoder_config decoder_config = { + .clock_class_offset_s = config ? config->clock_class_offset_s : 0, + .clock_class_offset_ns = config ? config->clock_class_offset_ns : 0, + }; + + file = get_file(ctf_fs_trace->path->str); + if (!file) { + BT_LOGE("Cannot create metadata file object"); + ret = -1; + goto end; + } + + ctf_fs_trace->metadata->decoder = ctf_metadata_decoder_create(self_comp, + config ? &decoder_config : NULL); + if (!ctf_fs_trace->metadata->decoder) { + BT_LOGE("Cannot create metadata decoder object"); + ret = -1; + goto end; + } + + ret = ctf_metadata_decoder_decode(ctf_fs_trace->metadata->decoder, + file->fp); + if (ret) { + BT_LOGE("Cannot decode metadata file"); + goto end; + } + + ctf_fs_trace->metadata->trace_class = + ctf_metadata_decoder_get_ir_trace_class( + ctf_fs_trace->metadata->decoder); + BT_ASSERT(!self_comp || ctf_fs_trace->metadata->trace_class); + ctf_fs_trace->metadata->tc = + ctf_metadata_decoder_borrow_ctf_trace_class( + ctf_fs_trace->metadata->decoder); + BT_ASSERT(ctf_fs_trace->metadata->tc); + +end: + ctf_fs_file_destroy(file); + return ret; +} + +BT_HIDDEN +int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata) +{ + /* Nothing to initialize for the moment. */ + return 0; +} + +BT_HIDDEN +void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata) +{ + if (metadata->text) { + free(metadata->text); + } + + if (metadata->trace_class) { + BT_TRACE_CLASS_PUT_REF_AND_RESET(metadata->trace_class); + } + + if (metadata->decoder) { + ctf_metadata_decoder_destroy(metadata->decoder); + } +} diff --git a/src/plugins/ctf/fs-src/metadata.h b/src/plugins/ctf/fs-src/metadata.h new file mode 100644 index 00000000..414b875b --- /dev/null +++ b/src/plugins/ctf/fs-src/metadata.h @@ -0,0 +1,58 @@ +#ifndef CTF_FS_METADATA_H +#define CTF_FS_METADATA_H + +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include + +#define CTF_FS_METADATA_FILENAME "metadata" + +struct ctf_fs_trace; +struct ctf_fs_metadata; + +struct ctf_fs_metadata_config { + int64_t clock_class_offset_s; + int64_t clock_class_offset_ns; +}; + +BT_HIDDEN +int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata); + +BT_HIDDEN +void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata); + +BT_HIDDEN +int ctf_fs_metadata_set_trace_class(bt_self_component_source *self_comp, + struct ctf_fs_trace *ctf_fs_trace, + struct ctf_fs_metadata_config *config); + +BT_HIDDEN +FILE *ctf_fs_metadata_open_file(const char *trace_path); + +BT_HIDDEN +bool ctf_metadata_is_packetized(FILE *fp, int *byte_order); + +#endif /* CTF_FS_METADATA_H */ diff --git a/src/plugins/ctf/fs-src/query.c b/src/plugins/ctf/fs-src/query.c new file mode 100644 index 00000000..3fa28b86 --- /dev/null +++ b/src/plugins/ctf/fs-src/query.c @@ -0,0 +1,556 @@ +/* + * query.c + * + * Babeltrace CTF file system Reader Component queries + * + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "query.h" +#include +#include "common/assert.h" +#include "metadata.h" +#include "../common/metadata/decoder.h" +#include "common/common.h" +#include "common/babeltrace.h" +#include +#include "fs.h" + +#define BT_LOG_TAG "PLUGIN-CTF-FS-QUERY-SRC" +#include "logging.h" + +#define METADATA_TEXT_SIG "/* CTF 1.8" + +struct range { + int64_t begin_ns; + int64_t end_ns; + bool set; +}; + +BT_HIDDEN +bt_query_status metadata_info_query( + bt_self_component_class_source *comp_class, + const bt_value *params, + const bt_value **user_result) +{ + bt_query_status status = BT_QUERY_STATUS_OK; + bt_value *result = NULL; + const bt_value *path_value = NULL; + char *metadata_text = NULL; + FILE *metadata_fp = NULL; + GString *g_metadata_text = NULL; + int ret; + int bo; + const char *path; + bool is_packetized; + + result = bt_value_map_create(); + if (!result) { + status = BT_QUERY_STATUS_NOMEM; + goto error; + } + + BT_ASSERT(params); + + if (!bt_value_is_map(params)) { + BT_LOGE_STR("Query parameters is not a map value object."); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + path_value = bt_value_map_borrow_entry_value_const(params, "path"); + if (!path_value) { + BT_LOGE_STR("Mandatory `path` parameter missing"); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + if (!bt_value_is_string(path_value)) { + BT_LOGE_STR("`path` parameter is required to be a string value"); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + path = bt_value_string_get(path_value); + + BT_ASSERT(path); + metadata_fp = ctf_fs_metadata_open_file(path); + if (!metadata_fp) { + BT_LOGE("Cannot open trace metadata: path=\"%s\".", path); + goto error; + } + + is_packetized = ctf_metadata_decoder_is_packetized(metadata_fp, + &bo); + + if (is_packetized) { + ret = ctf_metadata_decoder_packetized_file_stream_to_buf( + metadata_fp, &metadata_text, bo); + if (ret) { + BT_LOGE("Cannot decode packetized metadata file: path=\"%s\"", + path); + goto error; + } + } else { + long filesize; + + ret = fseek(metadata_fp, 0, SEEK_END); + if (ret) { + BT_LOGE_ERRNO("Failed to seek to the end of the metadata file", + ": path=\"%s\"", path); + goto error; + } + filesize = ftell(metadata_fp); + if (filesize < 0) { + BT_LOGE_ERRNO("Failed to get the current position in the metadata file", + ": path=\"%s\"", path); + goto error; + } + rewind(metadata_fp); + metadata_text = malloc(filesize + 1); + if (!metadata_text) { + BT_LOGE_STR("Cannot allocate buffer for metadata text."); + goto error; + } + + if (fread(metadata_text, filesize, 1, metadata_fp) != 1) { + BT_LOGE_ERRNO("Cannot read metadata file", ": path=\"%s\"", + path); + goto error; + } + + metadata_text[filesize] = '\0'; + } + + g_metadata_text = g_string_new(NULL); + if (!g_metadata_text) { + goto error; + } + + if (strncmp(metadata_text, METADATA_TEXT_SIG, + sizeof(METADATA_TEXT_SIG) - 1) != 0) { + g_string_assign(g_metadata_text, METADATA_TEXT_SIG); + g_string_append(g_metadata_text, " */\n\n"); + } + + g_string_append(g_metadata_text, metadata_text); + + ret = bt_value_map_insert_string_entry(result, "text", + g_metadata_text->str); + if (ret) { + BT_LOGE_STR("Cannot insert metadata text into query result."); + goto error; + } + + ret = bt_value_map_insert_bool_entry(result, "is-packetized", + is_packetized); + if (ret) { + BT_LOGE_STR("Cannot insert \"is-packetized\" attribute into query result."); + goto error; + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(result); + result = NULL; + + if (status >= 0) { + status = BT_QUERY_STATUS_ERROR; + } + +end: + free(metadata_text); + + if (g_metadata_text) { + g_string_free(g_metadata_text, TRUE); + } + + if (metadata_fp) { + fclose(metadata_fp); + } + + *user_result = result; + return status; +} + +static +int add_range(bt_value *info, struct range *range, + const char *range_name) +{ + int ret = 0; + bt_value_status status; + bt_value *range_map = NULL; + + if (!range->set) { + /* Not an error. */ + goto end; + } + + range_map = bt_value_map_create(); + if (!range_map) { + ret = -1; + goto end; + } + + status = bt_value_map_insert_signed_integer_entry(range_map, "begin", + range->begin_ns); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + + status = bt_value_map_insert_signed_integer_entry(range_map, "end", + range->end_ns); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + + status = bt_value_map_insert_entry(info, range_name, + range_map); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + +end: + bt_value_put_ref(range_map); + return ret; +} + +static +int add_stream_ids(bt_value *info, struct ctf_fs_ds_file_group *ds_file_group) +{ + int ret = 0; + bt_value_status status; + + if (ds_file_group->stream_id != UINT64_C(-1)) { + status = bt_value_map_insert_unsigned_integer_entry(info, "id", + ds_file_group->stream_id); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + } + + status = bt_value_map_insert_unsigned_integer_entry(info, "class-id", + ds_file_group->sc->id); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + +end: + return ret; +} + +static +int populate_stream_info(struct ctf_fs_ds_file_group *group, + bt_value *group_info, struct range *stream_range) +{ + int ret = 0; + size_t file_idx; + bt_value_status status; + bt_value *file_paths; + struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry; + gchar *port_name = NULL; + + file_paths = bt_value_array_create(); + if (!file_paths) { + ret = -1; + goto end; + } + + for (file_idx = 0; file_idx < group->ds_file_infos->len; file_idx++) { + struct ctf_fs_ds_file_info *info = + g_ptr_array_index(group->ds_file_infos, + file_idx); + + status = bt_value_array_append_string_element(file_paths, + info->path->str); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + } + + /* + * Since each `struct ctf_fs_ds_file_group` has a sorted array of + * `struct ctf_fs_ds_index_entry`, we can compute the stream range from + * the timestamp_begin of the first index entry and the timestamp_end + * of the last index entry. + */ + BT_ASSERT(group->index); + BT_ASSERT(group->index->entries); + BT_ASSERT(group->index->entries->len > 0); + + /* First entry. */ + first_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index( + group->index->entries, 0); + + /* Last entry. */ + last_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index( + group->index->entries, group->index->entries->len - 1); + + stream_range->begin_ns = first_ds_index_entry->timestamp_begin_ns; + stream_range->end_ns = last_ds_index_entry->timestamp_end_ns; + + /* + * If any of the begin and end timestamps is not set it means that + * packets don't include `timestamp_begin` _and_ `timestamp_end` fields + * in their packet context so we can't set the range. + */ + stream_range->set = stream_range->begin_ns != UINT64_C(-1) && + stream_range->end_ns != UINT64_C(-1); + + ret = add_range(group_info, stream_range, "range-ns"); + if (ret) { + goto end; + } + + status = bt_value_map_insert_entry(group_info, "paths", + file_paths); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + + ret = add_stream_ids(group_info, group); + if (ret) { + goto end; + } + + port_name = ctf_fs_make_port_name(group); + if (!port_name) { + ret = -1; + goto end; + } + + status = bt_value_map_insert_string_entry(group_info, "port-name", + port_name); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + +end: + bt_value_put_ref(file_paths); + return ret; +} + +static +int populate_trace_info(const struct ctf_fs_trace *trace, bt_value *trace_info) +{ + int ret = 0; + size_t group_idx; + bt_value_status status; + bt_value *file_groups = NULL; + struct range trace_range = { + .begin_ns = INT64_MAX, + .end_ns = 0, + .set = false, + }; + struct range trace_intersection = { + .begin_ns = 0, + .end_ns = INT64_MAX, + .set = false, + }; + + BT_ASSERT(trace->ds_file_groups); + /* Add trace range info only if it contains streams. */ + if (trace->ds_file_groups->len == 0) { + ret = -1; + goto end; + } + + file_groups = bt_value_array_create(); + if (!file_groups) { + goto end; + } + + status = bt_value_map_insert_string_entry(trace_info, "name", + trace->name->str); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + status = bt_value_map_insert_string_entry(trace_info, "path", + trace->path->str); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + + /* Find range of all stream groups, and of the trace. */ + for (group_idx = 0; group_idx < trace->ds_file_groups->len; + group_idx++) { + bt_value *group_info; + struct range group_range = { .set = false }; + struct ctf_fs_ds_file_group *group = g_ptr_array_index( + trace->ds_file_groups, group_idx); + + group_info = bt_value_map_create(); + if (!group_info) { + ret = -1; + goto end; + } + + ret = populate_stream_info(group, group_info, &group_range); + if (ret) { + bt_value_put_ref(group_info); + goto end; + } + + status = bt_value_array_append_element(file_groups, group_info); + bt_value_put_ref(group_info); + if (status != BT_VALUE_STATUS_OK) { + goto end; + } + + if (group_range.set) { + trace_range.begin_ns = min(trace_range.begin_ns, + group_range.begin_ns); + trace_range.end_ns = max(trace_range.end_ns, + group_range.end_ns); + trace_range.set = true; + + trace_intersection.begin_ns = max(trace_intersection.begin_ns, + group_range.begin_ns); + trace_intersection.end_ns = min(trace_intersection.end_ns, + group_range.end_ns); + trace_intersection.set = true; + } + } + + ret = add_range(trace_info, &trace_range, "range-ns"); + if (ret) { + goto end; + } + + if (trace_intersection.begin_ns < trace_intersection.end_ns) { + ret = add_range(trace_info, &trace_intersection, + "intersection-range-ns"); + if (ret) { + goto end; + } + } + + status = bt_value_map_insert_entry(trace_info, "streams", + file_groups); + BT_VALUE_PUT_REF_AND_RESET(file_groups); + if (status != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + +end: + bt_value_put_ref(file_groups); + return ret; +} + +BT_HIDDEN +bt_query_status trace_info_query( + bt_self_component_class_source *comp_class, + const bt_value *params, + const bt_value **user_result) +{ + struct ctf_fs_component *ctf_fs = NULL; + bt_query_status status = BT_QUERY_STATUS_OK; + bt_value *result = NULL; + const bt_value *paths_value = NULL; + int ret = 0; + guint i; + + BT_ASSERT(params); + + if (!bt_value_is_map(params)) { + BT_LOGE("Query parameters is not a map value object."); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + ctf_fs = ctf_fs_component_create(); + if (!ctf_fs) { + goto error; + } + + if (!read_src_fs_parameters(params, &paths_value, ctf_fs)) { + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + if (ctf_fs_component_create_ctf_fs_traces(NULL, ctf_fs, paths_value)) { + goto error; + } + + result = bt_value_array_create(); + if (!result) { + status = BT_QUERY_STATUS_NOMEM; + goto error; + } + + for (i = 0; i < ctf_fs->traces->len; i++) { + struct ctf_fs_trace *trace; + bt_value *trace_info; + bt_value_status status; + + trace = g_ptr_array_index(ctf_fs->traces, i); + BT_ASSERT(trace); + + trace_info = bt_value_map_create(); + if (!trace_info) { + BT_LOGE("Failed to create trace info map."); + goto error; + } + + ret = populate_trace_info(trace, trace_info); + if (ret) { + bt_value_put_ref(trace_info); + goto error; + } + + status = bt_value_array_append_element(result, trace_info); + bt_value_put_ref(trace_info); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(result); + result = NULL; + + if (status >= 0) { + status = BT_QUERY_STATUS_ERROR; + } + +end: + if (ctf_fs) { + ctf_fs_destroy(ctf_fs); + ctf_fs = NULL; + } + + *user_result = result; + return status; +} diff --git a/src/plugins/ctf/fs-src/query.h b/src/plugins/ctf/fs-src/query.h new file mode 100644 index 00000000..4125e4a9 --- /dev/null +++ b/src/plugins/ctf/fs-src/query.h @@ -0,0 +1,41 @@ +#ifndef BABELTRACE_PLUGIN_CTF_FS_QUERY_H +#define BABELTRACE_PLUGIN_CTF_FS_QUERY_H + +/* + * BabelTrace - CTF on File System Component + * + * Copyright 2017 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include + +BT_HIDDEN +bt_query_status metadata_info_query( + bt_self_component_class_source *comp_class, + const bt_value *params, const bt_value **result); + +BT_HIDDEN +bt_query_status trace_info_query( + bt_self_component_class_source *comp_class, + const bt_value *params, const bt_value **result); + +#endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */ diff --git a/src/plugins/ctf/lttng-live/Makefile.am b/src/plugins/ctf/lttng-live/Makefile.am new file mode 100644 index 00000000..853f47d6 --- /dev/null +++ b/src/plugins/ctf/lttng-live/Makefile.am @@ -0,0 +1,18 @@ +libbabeltrace2_plugin_ctf_lttng_live_la_SOURCES = \ + lttng-live.c \ + lttng-live.h \ + data-stream.c \ + data-stream.h \ + metadata.c \ + metadata.h \ + viewer-connection.c \ + viewer-connection.h \ + lttng-viewer-abi.h \ + logging.c \ + logging.h + +if BABELTRACE_BUILD_WITH_MINGW +libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD = -lws2_32 +endif + +noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-lttng-live.la diff --git a/src/plugins/ctf/lttng-live/data-stream.c b/src/plugins/ctf/lttng-live/data-stream.c new file mode 100644 index 00000000..25a8e4cf --- /dev/null +++ b/src/plugins/ctf/lttng-live/data-stream.c @@ -0,0 +1,273 @@ +/* + * Copyright 2019 Francis Deslauriers + * Copyright 2016 - Philippe Proulx + * Copyright 2016 - Jérémie Galarneau + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-DS" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include "compat/mman.h" +#include +#include "../common/msg-iter/msg-iter.h" +#include "common/assert.h" + +#include "data-stream.h" + +#define STREAM_NAME_PREFIX "stream-" + +static +enum bt_msg_iter_medium_status medop_request_bytes( + size_t request_sz, uint8_t **buffer_addr, + size_t *buffer_sz, void *data) +{ + enum bt_msg_iter_medium_status status = + BT_MSG_ITER_MEDIUM_STATUS_OK; + struct lttng_live_stream_iterator *stream = data; + struct lttng_live_trace *trace = stream->trace; + struct lttng_live_session *session = trace->session; + struct lttng_live_msg_iter *live_msg_iter = session->lttng_live_msg_iter; + uint64_t recv_len = 0; + uint64_t len_left; + uint64_t read_len; + + len_left = stream->base_offset + stream->len - stream->offset; + if (!len_left) { + stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; + status = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; + return status; + } + read_len = MIN(request_sz, stream->buflen); + read_len = MIN(read_len, len_left); + status = lttng_live_get_stream_bytes(live_msg_iter, + stream, stream->buf, stream->offset, + read_len, &recv_len); + *buffer_addr = stream->buf; + *buffer_sz = recv_len; + stream->offset += recv_len; + return status; +} + +static +bt_stream *medop_borrow_stream(bt_stream_class *stream_class, + int64_t stream_id, void *data) +{ + struct lttng_live_stream_iterator *lttng_live_stream = data; + + if (!lttng_live_stream->stream) { + uint64_t stream_class_id = bt_stream_class_get_id(stream_class); + + BT_LOGD("Creating stream %s (ID: %" PRIu64 ") out of stream " + "class %" PRId64, lttng_live_stream->name->str, + stream_id, stream_class_id); + + if (stream_id < 0) { + /* + * No stream instance ID in the stream. It's possible + * to encounter this situation with older version of + * LTTng. In these cases, use the viewer_stream_id that + * is unique for a live viewer session. + */ + lttng_live_stream->stream = bt_stream_create_with_id( + stream_class, lttng_live_stream->trace->trace, + lttng_live_stream->viewer_stream_id); + } else { + lttng_live_stream->stream = bt_stream_create_with_id( + stream_class, lttng_live_stream->trace->trace, + (uint64_t) stream_id); + } + + if (!lttng_live_stream->stream) { + BT_LOGE("Cannot create stream %s (stream class ID " + "%" PRId64 ", stream ID %" PRIu64 ")", + lttng_live_stream->name->str, + stream_class_id, stream_id); + } + bt_stream_set_name(lttng_live_stream->stream, + lttng_live_stream->name->str); + } + + return lttng_live_stream->stream; +} + +static struct bt_msg_iter_medium_ops medops = { + .request_bytes = medop_request_bytes, + .seek = NULL, + .borrow_stream = medop_borrow_stream, +}; + +BT_HIDDEN +enum lttng_live_iterator_status lttng_live_lazy_msg_init( + struct lttng_live_session *session) +{ + struct lttng_live_component *lttng_live = + session->lttng_live_msg_iter->lttng_live_comp; + uint64_t trace_idx, stream_iter_idx; + + if (!session->lazy_stream_msg_init) { + return LTTNG_LIVE_ITERATOR_STATUS_OK; + } + + for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + + for (stream_iter_idx = 0; + stream_iter_idx < trace->stream_iterators->len; + stream_iter_idx++) { + struct ctf_trace_class *ctf_tc; + struct lttng_live_stream_iterator *stream_iter = + g_ptr_array_index(trace->stream_iterators, + stream_iter_idx); + + if (stream_iter->msg_iter) { + continue; + } + ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class( + trace->metadata->decoder); + stream_iter->msg_iter = bt_msg_iter_create(ctf_tc, + lttng_live->max_query_size, medops, + stream_iter); + if (!stream_iter->msg_iter) { + goto error; + } + } + } + + session->lazy_stream_msg_init = false; + + return LTTNG_LIVE_ITERATOR_STATUS_OK; + +error: + return LTTNG_LIVE_ITERATOR_STATUS_ERROR; +} + +BT_HIDDEN +struct lttng_live_stream_iterator *lttng_live_stream_iterator_create( + struct lttng_live_session *session, + uint64_t ctf_trace_id, + uint64_t stream_id) +{ + struct lttng_live_stream_iterator *stream_iter; + struct lttng_live_component *lttng_live; + struct lttng_live_trace *trace; + + BT_ASSERT(session); + BT_ASSERT(session->lttng_live_msg_iter); + BT_ASSERT(session->lttng_live_msg_iter->lttng_live_comp); + + lttng_live = session->lttng_live_msg_iter->lttng_live_comp; + + stream_iter = g_new0(struct lttng_live_stream_iterator, 1); + if (!stream_iter) { + goto error; + } + + trace = lttng_live_borrow_trace(session, ctf_trace_id); + if (!trace) { + goto error; + } + + stream_iter->trace = trace; + stream_iter->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; + stream_iter->viewer_stream_id = stream_id; + stream_iter->ctf_stream_class_id = -1ULL; + stream_iter->last_inactivity_ts = INT64_MIN; + + if (trace->trace) { + struct ctf_trace_class *ctf_tc = + ctf_metadata_decoder_borrow_ctf_trace_class( + trace->metadata->decoder); + BT_ASSERT(!stream_iter->msg_iter); + stream_iter->msg_iter = bt_msg_iter_create(ctf_tc, + lttng_live->max_query_size, medops, + stream_iter); + if (!stream_iter->msg_iter) { + goto error; + } + } + stream_iter->buf = g_new0(uint8_t, lttng_live->max_query_size); + if (!stream_iter->buf) { + goto error; + } + + stream_iter->buflen = lttng_live->max_query_size; + stream_iter->name = g_string_new(NULL); + if (!stream_iter->name) { + goto error; + } + + g_string_printf(stream_iter->name, STREAM_NAME_PREFIX "%" PRIu64, + stream_iter->viewer_stream_id); + g_ptr_array_add(trace->stream_iterators, stream_iter); + + /* Track the number of active stream iterator. */ + session->lttng_live_msg_iter->active_stream_iter++; + + goto end; +error: + lttng_live_stream_iterator_destroy(stream_iter); + stream_iter = NULL; +end: + return stream_iter; +} + +BT_HIDDEN +void lttng_live_stream_iterator_destroy( + struct lttng_live_stream_iterator *stream_iter) +{ + if (!stream_iter) { + return; + } + + if (stream_iter->stream) { + BT_STREAM_PUT_REF_AND_RESET(stream_iter->stream); + } + + if (stream_iter->msg_iter) { + bt_msg_iter_destroy(stream_iter->msg_iter); + } + if (stream_iter->buf) { + g_free(stream_iter->buf); + } + if (stream_iter->name) { + g_string_free(stream_iter->name, TRUE); + } + + bt_message_put_ref(stream_iter->current_msg); + + /* Track the number of active stream iterator. */ + stream_iter->trace->session->lttng_live_msg_iter->active_stream_iter--; + + /* + * Ensure we poke the trace metadata in the future, which is + * required to release the metadata reference on the trace. + */ + stream_iter->trace->new_metadata_needed = true; + g_free(stream_iter); +} diff --git a/src/plugins/ctf/lttng-live/data-stream.h b/src/plugins/ctf/lttng-live/data-stream.h new file mode 100644 index 00000000..d9e273a8 --- /dev/null +++ b/src/plugins/ctf/lttng-live/data-stream.h @@ -0,0 +1,45 @@ +#ifndef LTTNG_LIVE_DATA_STREAM_H +#define LTTNG_LIVE_DATA_STREAM_H + +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include + +#include "lttng-live.h" +#include "../common/msg-iter/msg-iter.h" + +enum lttng_live_iterator_status lttng_live_lazy_msg_init( + struct lttng_live_session *session); + +struct lttng_live_stream_iterator *lttng_live_stream_iterator_create( + struct lttng_live_session *session, + uint64_t ctf_trace_id, + uint64_t stream_id); + +void lttng_live_stream_iterator_destroy( + struct lttng_live_stream_iterator *stream); + +#endif /* LTTNG_LIVE_DATA_STREAM_H */ diff --git a/src/plugins/ctf/lttng-live/logging.c b/src/plugins/ctf/lttng-live/logging.c new file mode 100644 index 00000000..98f1b639 --- /dev/null +++ b/src/plugins/ctf/lttng-live/logging.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL lttng_live_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(lttng_live_log_level, "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL"); diff --git a/src/plugins/ctf/lttng-live/logging.h b/src/plugins/ctf/lttng-live/logging.h new file mode 100644 index 00000000..de11a5ed --- /dev/null +++ b/src/plugins/ctf/lttng-live/logging.h @@ -0,0 +1,31 @@ +#ifndef LTTNG_LIVE_LOGGING_H +#define LTTNG_LIVE_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL lttng_live_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(lttng_live_log_level); + +#endif /* LTTNG_LIVE_LOGGING_H */ diff --git a/src/plugins/ctf/lttng-live/lttng-live.c b/src/plugins/ctf/lttng-live/lttng-live.c new file mode 100644 index 00000000..1a24a734 --- /dev/null +++ b/src/plugins/ctf/lttng-live/lttng-live.c @@ -0,0 +1,1601 @@ +/* + * lttng-live.c + * + * Babeltrace CTF LTTng-live Client Component + * + * Copyright 2019 Francis Deslauriers + * Copyright 2016 Jérémie Galarneau + * Copyright 2016 Mathieu Desnoyers + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC" +#include "logging.h" + +#include +#include +#include + +#include "common/assert.h" +#include +#include "compat/compiler.h" +#include +#include "plugins/plugins-common.h" + +#include "data-stream.h" +#include "metadata.h" +#include "lttng-live.h" + +#define MAX_QUERY_SIZE (256*1024) +#define URL_PARAM "url" +#define SESS_NOT_FOUND_ACTION_PARAM "session-not-found-action" +#define SESS_NOT_FOUND_ACTION_CONTINUE_STR "continue" +#define SESS_NOT_FOUND_ACTION_FAIL_STR "fail" +#define SESS_NOT_FOUND_ACTION_END_STR "end" + +#define print_dbg(fmt, ...) BT_LOGD(fmt, ## __VA_ARGS__) + +static +const char *print_live_iterator_status(enum lttng_live_iterator_status status) +{ + switch (status) { + case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE: + return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE"; + case LTTNG_LIVE_ITERATOR_STATUS_AGAIN: + return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN"; + case LTTNG_LIVE_ITERATOR_STATUS_END: + return "LTTNG_LIVE_ITERATOR_STATUS_END"; + case LTTNG_LIVE_ITERATOR_STATUS_OK: + return "LTTNG_LIVE_ITERATOR_STATUS_OK"; + case LTTNG_LIVE_ITERATOR_STATUS_INVAL: + return "LTTNG_LIVE_ITERATOR_STATUS_INVAL"; + case LTTNG_LIVE_ITERATOR_STATUS_ERROR: + return "LTTNG_LIVE_ITERATOR_STATUS_ERROR"; + case LTTNG_LIVE_ITERATOR_STATUS_NOMEM: + return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM"; + case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED: + return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED"; + default: + abort(); + } +} + +static +const char *print_state(struct lttng_live_stream_iterator *s) +{ + switch (s->state) { + case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA: + return "ACTIVE_NO_DATA"; + case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA: + return "QUIESCENT_NO_DATA"; + case LTTNG_LIVE_STREAM_QUIESCENT: + return "QUIESCENT"; + case LTTNG_LIVE_STREAM_ACTIVE_DATA: + return "ACTIVE_DATA"; + case LTTNG_LIVE_STREAM_EOF: + return "EOF"; + default: + return "ERROR"; + } +} + +#define print_stream_state(live_stream_iter) \ + do { \ + BT_LOGD("stream state %s last_inact_ts %" PRId64 \ + ", curr_inact_ts %" PRId64, \ + print_state(live_stream_iter), \ + live_stream_iter->last_inactivity_ts, \ + live_stream_iter->current_inactivity_ts); \ + } while (0); + +BT_HIDDEN +bool lttng_live_graph_is_canceled(struct lttng_live_component *lttng_live) +{ + const bt_component *component; + bool ret; + + if (!lttng_live) { + ret = false; + goto end; + } + + component = bt_component_source_as_component_const( + bt_self_component_source_as_component_source( + lttng_live->self_comp)); + + ret = bt_component_graph_is_canceled(component); + +end: + return ret; +} + +static +struct lttng_live_trace *lttng_live_find_trace(struct lttng_live_session *session, + uint64_t trace_id) +{ + uint64_t trace_idx; + struct lttng_live_trace *ret_trace = NULL; + + for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + if (trace->id == trace_id) { + ret_trace = trace; + goto end; + } + } + +end: + return ret_trace; +} + +static +void lttng_live_destroy_trace(struct lttng_live_trace *trace) +{ + BT_LOGD("Destroy lttng_live_trace"); + + BT_ASSERT(trace->stream_iterators); + g_ptr_array_free(trace->stream_iterators, TRUE); + + BT_TRACE_PUT_REF_AND_RESET(trace->trace); + BT_TRACE_CLASS_PUT_REF_AND_RESET(trace->trace_class); + + lttng_live_metadata_fini(trace); + g_free(trace); +} + +static +struct lttng_live_trace *lttng_live_create_trace(struct lttng_live_session *session, + uint64_t trace_id) +{ + struct lttng_live_trace *trace = NULL; + + trace = g_new0(struct lttng_live_trace, 1); + if (!trace) { + goto error; + } + trace->session = session; + trace->id = trace_id; + trace->trace_class = NULL; + trace->trace = NULL; + trace->stream_iterators = g_ptr_array_new_with_free_func( + (GDestroyNotify) lttng_live_stream_iterator_destroy); + BT_ASSERT(trace->stream_iterators); + trace->new_metadata_needed = true; + g_ptr_array_add(session->traces, trace); + + BT_LOGI("Create trace"); + goto end; +error: + g_free(trace); + trace = NULL; +end: + return trace; +} + +BT_HIDDEN +struct lttng_live_trace *lttng_live_borrow_trace( + struct lttng_live_session *session, uint64_t trace_id) +{ + struct lttng_live_trace *trace; + + trace = lttng_live_find_trace(session, trace_id); + if (trace) { + goto end; + } + + /* The session is the owner of the newly created trace. */ + trace = lttng_live_create_trace(session, trace_id); + +end: + return trace; +} + +BT_HIDDEN +int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, + uint64_t session_id, const char *hostname, + const char *session_name) +{ + int ret = 0; + struct lttng_live_session *session; + + session = g_new0(struct lttng_live_session, 1); + if (!session) { + goto error; + } + + session->id = session_id; + session->traces = g_ptr_array_new_with_free_func( + (GDestroyNotify) lttng_live_destroy_trace); + BT_ASSERT(session->traces); + session->lttng_live_msg_iter = lttng_live_msg_iter; + session->new_streams_needed = true; + session->hostname = g_string_new(hostname); + BT_ASSERT(session->hostname); + + session->session_name = g_string_new(session_name); + BT_ASSERT(session->session_name); + + BT_LOGI("Reading from session: %" PRIu64 " hostname: %s session_name: %s", + session->id, hostname, session_name); + g_ptr_array_add(lttng_live_msg_iter->sessions, session); + goto end; +error: + BT_LOGE("Error adding session"); + g_free(session); + ret = -1; +end: + return ret; +} + +static +void lttng_live_destroy_session(struct lttng_live_session *session) +{ + struct lttng_live_component *live_comp; + + if (!session) { + goto end; + } + + BT_LOGD("Destroy lttng live session"); + if (session->id != -1ULL) { + if (lttng_live_detach_session(session)) { + live_comp = session->lttng_live_msg_iter->lttng_live_comp; + if (session->lttng_live_msg_iter && + !lttng_live_graph_is_canceled(live_comp)) { + /* Old relayd cannot detach sessions. */ + BT_LOGD("Unable to detach lttng live session %" PRIu64, + session->id); + } + } + session->id = -1ULL; + } + + if (session->traces) { + g_ptr_array_free(session->traces, TRUE); + } + + if (session->hostname) { + g_string_free(session->hostname, TRUE); + } + if (session->session_name) { + g_string_free(session->session_name, TRUE); + } + g_free(session); + +end: + return; +} + +static +void lttng_live_msg_iter_destroy(struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + if (!lttng_live_msg_iter) { + goto end; + } + + if (lttng_live_msg_iter->sessions) { + g_ptr_array_free(lttng_live_msg_iter->sessions, TRUE); + } + + BT_OBJECT_PUT_REF_AND_RESET(lttng_live_msg_iter->viewer_connection); + BT_ASSERT(lttng_live_msg_iter->lttng_live_comp); + BT_ASSERT(lttng_live_msg_iter->lttng_live_comp->has_msg_iter); + + /* All stream iterators must be destroyed at this point. */ + BT_ASSERT(lttng_live_msg_iter->active_stream_iter == 0); + lttng_live_msg_iter->lttng_live_comp->has_msg_iter = false; + + g_free(lttng_live_msg_iter); + +end: + return; +} + +BT_HIDDEN +void lttng_live_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) +{ + struct lttng_live_msg_iter *lttng_live_msg_iter; + + BT_ASSERT(self_msg_iter); + + lttng_live_msg_iter = bt_self_message_iterator_get_data(self_msg_iter); + BT_ASSERT(lttng_live_msg_iter); + lttng_live_msg_iter_destroy(lttng_live_msg_iter); +} + +static +enum lttng_live_iterator_status lttng_live_iterator_next_check_stream_state( + struct lttng_live_stream_iterator *lttng_live_stream) +{ + switch (lttng_live_stream->state) { + case LTTNG_LIVE_STREAM_QUIESCENT: + case LTTNG_LIVE_STREAM_ACTIVE_DATA: + break; + case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA: + /* Invalid state. */ + BT_LOGF("Unexpected stream state \"ACTIVE_NO_DATA\""); + abort(); + case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA: + /* Invalid state. */ + BT_LOGF("Unexpected stream state \"QUIESCENT_NO_DATA\""); + abort(); + case LTTNG_LIVE_STREAM_EOF: + break; + } + return LTTNG_LIVE_ITERATOR_STATUS_OK; +} + +/* + * For active no data stream, fetch next data. It can be either: + * - quiescent: need to put it in the prio heap at quiescent end + * timestamp, + * - have data: need to wire up first event into the prio heap, + * - have no data on this stream at this point: need to retry (AGAIN) or + * return EOF. + */ +static +enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_data_stream( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *lttng_live_stream) +{ + enum lttng_live_iterator_status ret = + LTTNG_LIVE_ITERATOR_STATUS_OK; + struct packet_index index; + enum lttng_live_stream_state orig_state = lttng_live_stream->state; + + if (lttng_live_stream->trace->new_metadata_needed) { + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + goto end; + } + if (lttng_live_stream->trace->session->new_streams_needed) { + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + goto end; + } + if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_NO_DATA && + lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA) { + goto end; + } + ret = lttng_live_get_next_index(lttng_live_msg_iter, lttng_live_stream, + &index); + if (ret != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + BT_ASSERT(lttng_live_stream->state != LTTNG_LIVE_STREAM_EOF); + if (lttng_live_stream->state == LTTNG_LIVE_STREAM_QUIESCENT) { + uint64_t last_inact_ts = lttng_live_stream->last_inactivity_ts, + curr_inact_ts = lttng_live_stream->current_inactivity_ts; + + if (orig_state == LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA && + last_inact_ts == curr_inact_ts) { + ret = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + print_stream_state(lttng_live_stream); + } else { + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + } + goto end; + } + lttng_live_stream->base_offset = index.offset; + lttng_live_stream->offset = index.offset; + lttng_live_stream->len = index.packet_size / CHAR_BIT; +end: + if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK) { + ret = lttng_live_iterator_next_check_stream_state(lttng_live_stream); + } + return ret; +} + +/* + * Creation of the message requires the ctf trace class to be created + * beforehand, but the live protocol gives us all streams (including metadata) + * at once. So we split it in three steps: getting streams, getting metadata + * (which creates the ctf trace class), and then creating the per-stream + * messages. + */ +static +enum lttng_live_iterator_status lttng_live_get_session( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_session *session) +{ + enum lttng_live_iterator_status status; + uint64_t trace_idx; + int ret = 0; + + if (!session->attached) { + ret = lttng_live_attach_session(session); + if (ret) { + if (lttng_live_msg_iter && lttng_live_graph_is_canceled( + lttng_live_msg_iter->lttng_live_comp)) { + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + } else { + status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + } + goto end; + } + } + + status = lttng_live_get_new_streams(session); + if (status != LTTNG_LIVE_ITERATOR_STATUS_OK && + status != LTTNG_LIVE_ITERATOR_STATUS_END) { + goto end; + } + for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) { + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + + status = lttng_live_metadata_update(trace); + if (status != LTTNG_LIVE_ITERATOR_STATUS_OK && + status != LTTNG_LIVE_ITERATOR_STATUS_END) { + goto end; + } + } + status = lttng_live_lazy_msg_init(session); + +end: + return status; +} + +BT_HIDDEN +void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + uint64_t session_idx; + + for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; + session_idx++) { + struct lttng_live_session *session = + g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); + session->new_streams_needed = true; + } +} + +static +void lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + uint64_t session_idx, trace_idx; + + for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; + session_idx++) { + struct lttng_live_session *session = + g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); + session->new_streams_needed = true; + for (trace_idx = 0; trace_idx < session->traces->len; + trace_idx++) { + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + trace->new_metadata_needed = true; + } + } +} + +static +enum lttng_live_iterator_status +lttng_live_iterator_handle_new_streams_and_metadata( + struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + enum lttng_live_iterator_status ret = + LTTNG_LIVE_ITERATOR_STATUS_OK; + uint64_t session_idx = 0, nr_sessions_opened = 0; + struct lttng_live_session *session; + enum session_not_found_action sess_not_found_act = + lttng_live_msg_iter->lttng_live_comp->params.sess_not_found_act; + + /* + * In a remotely distant future, we could add a "new + * session" flag to the protocol, which would tell us that we + * need to query for new sessions even though we have sessions + * currently ongoing. + */ + if (lttng_live_msg_iter->sessions->len == 0) { + if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) { + ret = LTTNG_LIVE_ITERATOR_STATUS_END; + goto end; + } else { + /* + * Retry to create a viewer session for the requested + * session name. + */ + if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { + ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + goto end; + } + } + } + + for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; + session_idx++) { + session = g_ptr_array_index(lttng_live_msg_iter->sessions, + session_idx); + ret = lttng_live_get_session(lttng_live_msg_iter, session); + switch (ret) { + case LTTNG_LIVE_ITERATOR_STATUS_OK: + break; + case LTTNG_LIVE_ITERATOR_STATUS_END: + ret = LTTNG_LIVE_ITERATOR_STATUS_OK; + break; + default: + goto end; + } + if (!session->closed) { + nr_sessions_opened++; + } + } +end: + if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK && + sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE && + nr_sessions_opened == 0) { + ret = LTTNG_LIVE_ITERATOR_STATUS_END; + } + return ret; +} + +static +enum lttng_live_iterator_status emit_inactivity_message( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream_iter, + bt_message **message, uint64_t timestamp) +{ + enum lttng_live_iterator_status ret = + LTTNG_LIVE_ITERATOR_STATUS_OK; + bt_message *msg = NULL; + + BT_ASSERT(stream_iter->trace->clock_class); + + msg = bt_message_message_iterator_inactivity_create( + lttng_live_msg_iter->self_msg_iter, + stream_iter->trace->clock_class, + timestamp); + if (!msg) { + goto error; + } + + *message = msg; +end: + return ret; + +error: + ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + bt_message_put_ref(msg); + goto end; +} + +static +enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quiescent_stream( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *lttng_live_stream, + bt_message **message) +{ + enum lttng_live_iterator_status ret = + LTTNG_LIVE_ITERATOR_STATUS_OK; + + if (lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT) { + return LTTNG_LIVE_ITERATOR_STATUS_OK; + } + + if (lttng_live_stream->current_inactivity_ts == + lttng_live_stream->last_inactivity_ts) { + lttng_live_stream->state = LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA; + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + goto end; + } + + ret = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream, + message, lttng_live_stream->current_inactivity_ts); + + lttng_live_stream->last_inactivity_ts = + lttng_live_stream->current_inactivity_ts; +end: + return ret; +} + +static +int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter, + struct lttng_live_msg_iter *lttng_live_msg_iter, + const bt_message *msg, int64_t last_msg_ts_ns, + int64_t *ts_ns) +{ + const bt_clock_class *clock_class = NULL; + const bt_clock_snapshot *clock_snapshot = NULL; + int ret = 0; + bt_message_stream_activity_clock_snapshot_state sa_cs_state; + + BT_ASSERT(msg); + BT_ASSERT(ts_ns); + + BT_LOGV("Getting message's timestamp: iter-data-addr=%p, msg-addr=%p, " + "last-msg-ts=%" PRId64, lttng_live_msg_iter, msg, + last_msg_ts_ns); + + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_EVENT: + clock_class = + bt_message_event_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + clock_class = + bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_PACKET_END: + clock_class = + bt_message_packet_end_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + clock_class = + bt_message_discarded_events_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + clock_class = + bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + clock_class = + bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + clock_class = + bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( + msg); + BT_ASSERT(clock_class); + + sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + clock_snapshot = + bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + msg); + break; + default: + /* All the other messages have a higher priority */ + BT_LOGV_STR("Message has no timestamp: using the last message timestamp."); + *ts_ns = last_msg_ts_ns; + goto end; + } + + clock_class = bt_clock_snapshot_borrow_clock_class_const(clock_snapshot); + BT_ASSERT(clock_class); + + ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns); + if (ret) { + BT_LOGE("Cannot get nanoseconds from Epoch of clock snapshot: " + "clock-snapshot-addr=%p", clock_snapshot); + goto error; + } + + goto end; + +no_clock_snapshot: + BT_LOGV_STR("Message's default clock snapshot is missing: " + "using the last message timestamp."); + *ts_ns = last_msg_ts_ns; + goto end; + +error: + ret = -1; + +end: + if (ret == 0) { + BT_LOGV("Found message's timestamp: " + "iter-data-addr=%p, msg-addr=%p, " + "last-msg-ts=%" PRId64 ", ts=%" PRId64, + lttng_live_msg_iter, msg, last_msg_ts_ns, *ts_ns); + } + + return ret; +} + +static +enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_active_data_stream( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *lttng_live_stream, + bt_message **message) +{ + enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK; + enum bt_msg_iter_status status; + uint64_t session_idx, trace_idx; + + for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len; + session_idx++) { + struct lttng_live_session *session = + g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx); + + if (session->new_streams_needed) { + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + goto end; + } + for (trace_idx = 0; trace_idx < session->traces->len; + trace_idx++) { + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + if (trace->new_metadata_needed) { + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + goto end; + } + } + } + + if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_DATA) { + ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + goto end; + } + + status = bt_msg_iter_get_next_message(lttng_live_stream->msg_iter, + lttng_live_msg_iter->self_msg_iter, message); + switch (status) { + case BT_MSG_ITER_STATUS_EOF: + ret = LTTNG_LIVE_ITERATOR_STATUS_END; + break; + case BT_MSG_ITER_STATUS_OK: + ret = LTTNG_LIVE_ITERATOR_STATUS_OK; + break; + case BT_MSG_ITER_STATUS_AGAIN: + /* + * Continue immediately (end of packet). The next + * get_index may return AGAIN to delay the following + * attempt. + */ + ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + break; + case BT_MSG_ITER_STATUS_INVAL: + /* No argument provided by the user, so don't return INVAL. */ + case BT_MSG_ITER_STATUS_ERROR: + default: + ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + BT_LOGW("CTF msg iterator return an error or failed msg_iter=%p", + lttng_live_stream->msg_iter); + break; + } + +end: + return ret; +} + +/* + * helper function: + * handle_no_data_streams() + * retry: + * - for each ACTIVE_NO_DATA stream: + * - query relayd for stream data, or quiescence info. + * - if need metadata, get metadata, goto retry. + * - if new stream, get new stream as ACTIVE_NO_DATA, goto retry + * - if quiescent, move to QUIESCENT streams + * - if fetched data, move to ACTIVE_DATA streams + * (at this point each stream either has data, or is quiescent) + * + * + * iterator_next: + * handle_new_streams_and_metadata() + * - query relayd for known streams, add them as ACTIVE_NO_DATA + * - query relayd for metadata + * + * call handle_active_no_data_streams() + * + * handle_quiescent_streams() + * - if at least one stream is ACTIVE_DATA: + * - peek stream event with lowest timestamp -> next_ts + * - for each quiescent stream + * - if next_ts >= quiescent end + * - set state to ACTIVE_NO_DATA + * - else + * - for each quiescent stream + * - set state to ACTIVE_NO_DATA + * + * call handle_active_no_data_streams() + * + * handle_active_data_streams() + * - if at least one stream is ACTIVE_DATA: + * - get stream event with lowest timestamp from heap + * - make that stream event the current message. + * - move this stream heap position to its next event + * - if we need to fetch data from relayd, move + * stream to ACTIVE_NO_DATA. + * - return OK + * - return AGAIN + * + * end criterion: ctrl-c on client. If relayd exits or the session + * closes on the relay daemon side, we keep on waiting for streams. + * Eventually handle --end timestamp (also an end criterion). + * + * When disconnected from relayd: try to re-connect endlessly. + */ +static +enum lttng_live_iterator_status lttng_live_iterator_next_on_stream( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream_iter, + bt_message **curr_msg) +{ + enum lttng_live_iterator_status live_status; + +retry: + print_stream_state(stream_iter); + live_status = lttng_live_iterator_handle_new_streams_and_metadata( + lttng_live_msg_iter); + if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + live_status = lttng_live_iterator_next_handle_one_no_data_stream( + lttng_live_msg_iter, stream_iter); + if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + live_status = lttng_live_iterator_next_handle_one_quiescent_stream( + lttng_live_msg_iter, stream_iter, curr_msg); + if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + BT_ASSERT(*curr_msg == NULL); + goto end; + } + if (*curr_msg) { + goto end; + } + live_status = lttng_live_iterator_next_handle_one_active_data_stream( + lttng_live_msg_iter, stream_iter, curr_msg); + if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + BT_ASSERT(*curr_msg == NULL); + } + +end: + if (live_status == LTTNG_LIVE_ITERATOR_STATUS_CONTINUE) { + goto retry; + } + + return live_status; +} + +static +enum lttng_live_iterator_status next_stream_iterator_for_trace( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_trace *live_trace, + struct lttng_live_stream_iterator **candidate_stream_iter) +{ + struct lttng_live_stream_iterator *curr_candidate_stream_iter = NULL; + enum lttng_live_iterator_status stream_iter_status;; + int64_t curr_candidate_msg_ts = INT64_MAX; + uint64_t stream_iter_idx; + + BT_ASSERT(live_trace); + BT_ASSERT(live_trace->stream_iterators); + /* + * Update the current message of every stream iterators of this trace. + * The current msg of every stream must have a timestamp equal or + * larger than the last message returned by this iterator. We must + * ensure monotonicity. + */ + stream_iter_idx = 0; + while (stream_iter_idx < live_trace->stream_iterators->len) { + bool stream_iter_is_ended = false; + struct lttng_live_stream_iterator *stream_iter = + g_ptr_array_index(live_trace->stream_iterators, + stream_iter_idx); + + /* + * Find if there is are now current message for this stream + * iterator get it. + */ + while (!stream_iter->current_msg) { + bt_message *msg = NULL; + int64_t curr_msg_ts_ns = INT64_MAX; + stream_iter_status = lttng_live_iterator_next_on_stream( + lttng_live_msg_iter, stream_iter, &msg); + + BT_LOGD("live stream iterator returned status :%s", + print_live_iterator_status(stream_iter_status)); + if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { + stream_iter_is_ended = true; + break; + } + + if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + + BT_ASSERT(msg); + + /* + * Get the timestamp in nanoseconds from origin of this + * messsage. + */ + live_get_msg_ts_ns(stream_iter, lttng_live_msg_iter, + msg, lttng_live_msg_iter->last_msg_ts_ns, + &curr_msg_ts_ns); + + /* + * Check if the message of the current live stream + * iterator occured at the exact same time or after the + * last message returned by this component's message + * iterator. If not, we return an error. + */ + if (curr_msg_ts_ns >= lttng_live_msg_iter->last_msg_ts_ns) { + stream_iter->current_msg = msg; + stream_iter->current_msg_ts_ns = curr_msg_ts_ns; + } else { + /* + * We received a message in the past. To ensure + * monotonicity, we can't send it forward. + */ + BT_LOGE("Message's timestamp is less than " + "lttng-live's message iterator's last " + "returned timestamp: " + "lttng-live-msg-iter-addr=%p, ts=%" PRId64 ", " + "last-msg-ts=%" PRId64, + lttng_live_msg_iter, curr_msg_ts_ns, + lttng_live_msg_iter->last_msg_ts_ns); + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + goto end; + } + } + + if (!stream_iter_is_ended && + stream_iter->current_msg_ts_ns <= curr_candidate_msg_ts) { + /* + * Update the current best candidate message for the + * stream iterator of thise live trace to be forwarded + * downstream. + */ + curr_candidate_msg_ts = stream_iter->current_msg_ts_ns; + curr_candidate_stream_iter = stream_iter; + } + + if (stream_iter_is_ended) { + /* + * The live stream iterator is ENDed. We remove that + * iterator from the list and we restart the iteration + * at the beginning of the live stream iterator array + * to because the removal will shuffle the array. + */ + g_ptr_array_remove_index_fast(live_trace->stream_iterators, + stream_iter_idx); + stream_iter_idx = 0; + } else { + stream_iter_idx++; + } + } + + if (curr_candidate_stream_iter) { + *candidate_stream_iter = curr_candidate_stream_iter; + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; + } else { + /* + * The only case where we don't have a candidate for this trace + * is if we reached the end of all the iterators. + */ + BT_ASSERT(live_trace->stream_iterators->len == 0); + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END; + } + +end: + return stream_iter_status; +} + +static +enum lttng_live_iterator_status next_stream_iterator_for_session( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_session *session, + struct lttng_live_stream_iterator **candidate_session_stream_iter) +{ + enum lttng_live_iterator_status stream_iter_status; + uint64_t trace_idx = 0; + int64_t curr_candidate_msg_ts = INT64_MAX; + struct lttng_live_stream_iterator *curr_candidate_stream_iter = NULL; + + /* + * Make sure we are attached to the session and look for new streams + * and metadata. + */ + stream_iter_status = lttng_live_get_session(lttng_live_msg_iter, session); + if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK && + stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_CONTINUE && + stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_END) { + goto end; + } + + BT_ASSERT(session->traces); + + /* + * Use while loops here rather then for loops so we can restart the + * iteration if an element is removed from the array during the + * looping. + */ + while (trace_idx < session->traces->len) { + bool trace_is_ended = false; + struct lttng_live_stream_iterator *stream_iter; + struct lttng_live_trace *trace = + g_ptr_array_index(session->traces, trace_idx); + + stream_iter_status = next_stream_iterator_for_trace( + lttng_live_msg_iter, trace, &stream_iter); + if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { + /* + * All the live stream iterators for this trace are + * ENDed. Remove the trace from this session. + */ + trace_is_ended = true; + } else if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + + if (!trace_is_ended) { + BT_ASSERT(stream_iter); + + if (stream_iter->current_msg_ts_ns <= curr_candidate_msg_ts) { + curr_candidate_msg_ts = stream_iter->current_msg_ts_ns; + curr_candidate_stream_iter = stream_iter; + } + trace_idx++; + } else { + g_ptr_array_remove_index_fast(session->traces, trace_idx); + trace_idx = 0; + } + } + if (curr_candidate_stream_iter) { + *candidate_session_stream_iter = curr_candidate_stream_iter; + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; + } else { + /* + * The only cases where we don't have a candidate for this + * trace is: + * 1. if we reached the end of all the iterators of all the + * traces of this session, + * 2. if we never had live stream iterator in the first place. + * + * In either cases, we return END. + */ + BT_ASSERT(session->traces->len == 0); + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END; + } +end: + return stream_iter_status; +} + +static inline +void put_messages(bt_message_array_const msgs, uint64_t count) +{ + uint64_t i; + + for (i = 0; i < count; i++) { + BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); + } +} + +BT_HIDDEN +bt_self_message_iterator_status lttng_live_msg_iter_next( + bt_self_message_iterator *self_msg_it, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status; + struct lttng_live_msg_iter *lttng_live_msg_iter = + bt_self_message_iterator_get_data(self_msg_it); + struct lttng_live_component *lttng_live = + lttng_live_msg_iter->lttng_live_comp; + enum lttng_live_iterator_status stream_iter_status; + uint64_t session_idx; + + *count = 0; + + BT_ASSERT(lttng_live_msg_iter); + + /* + * Clear all the invalid message reference that might be left over in + * the output array. + */ + memset(msgs, 0, capacity * sizeof(*msgs)); + + /* + * If no session are exposed on the relay found at the url provided by + * the user, session count will be 0. In this case, we return status + * end to return gracefully. + */ + if (lttng_live_msg_iter->sessions->len == 0) { + if (lttng_live->params.sess_not_found_act != + SESSION_NOT_FOUND_ACTION_CONTINUE) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + goto no_session; + } else { + /* + * The are no more active session for this session + * name. Retry to create a viewer session for the + * requested session name. + */ + if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto no_session; + } + } + } + + if (lttng_live_msg_iter->active_stream_iter == 0) { + lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter); + } + + /* + * Here the muxing of message is done. + * + * We need to iterate over all the streams of all the traces of all the + * viewer sessions in order to get the message with the smallest + * timestamp. In this case, a session is a viewer session and there is + * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or + * kernel). Each viewer session can have multiple traces, for example, + * 64bit UST viewer sessions could have multiple per-pid traces. + * + * We iterate over the streams of each traces to update and see what is + * their next message's timestamp. From those timestamps, we select the + * message with the smallest timestamp as the best candidate message + * for that trace and do the same thing across all the sessions. + * + * We then compare the timestamp of best candidate message of all the + * sessions to pick the message with the smallest timestamp and we + * return it. + */ + while (*count < capacity) { + struct lttng_live_stream_iterator *next_stream_iter = NULL, + *candidate_stream_iter = NULL; + int64_t next_msg_ts_ns = INT64_MAX; + + BT_ASSERT(lttng_live_msg_iter->sessions); + session_idx = 0; + /* + * Use a while loop instead of a for loop so we can restart the + * iteration if we remove an element. We can safely call + * next_stream_iterator_for_session() multiple times on the + * same session as we only fetch a new message if there is no + * current next message for each live stream iterator. + * If all live stream iterator of that session already have a + * current next message, the function will simply exit return + * the same candidate live stream iterator every time. + */ + while (session_idx < lttng_live_msg_iter->sessions->len) { + struct lttng_live_session *session = + g_ptr_array_index(lttng_live_msg_iter->sessions, + session_idx); + + /* Find the best candidate message to send downstream. */ + stream_iter_status = next_stream_iterator_for_session( + lttng_live_msg_iter, session, + &candidate_stream_iter); + + /* If we receive an END status, it means that either: + * - Those traces never had active streams (UST with no + * data produced yet), + * - All live stream iterators have ENDed.*/ + if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) { + if (session->closed && session->traces->len == 0) { + /* + * Remove the session from the list and restart the + * iteration at the beginning of the array since the + * removal shuffle the elements of the array. + */ + g_ptr_array_remove_index_fast( + lttng_live_msg_iter->sessions, + session_idx); + session_idx = 0; + } else { + session_idx++; + } + continue; + } + + if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) { + goto end; + } + + if (candidate_stream_iter->current_msg_ts_ns <= next_msg_ts_ns) { + next_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns; + next_stream_iter = candidate_stream_iter; + } + + session_idx++; + } + + if (!next_stream_iter) { + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + goto end; + } + + BT_ASSERT(next_stream_iter->current_msg); + /* Ensure monotonicity. */ + BT_ASSERT(lttng_live_msg_iter->last_msg_ts_ns <= + next_stream_iter->current_msg_ts_ns); + + /* + * Insert the next message to the message batch. This will set + * stream iterator current messsage to NULL so that next time + * we fetch the next message of that stream iterator + */ + BT_MESSAGE_MOVE_REF(msgs[*count], next_stream_iter->current_msg); + (*count)++; + + /* Update the last timestamp in nanoseconds sent downstream. */ + lttng_live_msg_iter->last_msg_ts_ns = next_msg_ts_ns; + next_stream_iter->current_msg_ts_ns = INT64_MAX; + + stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK; + } +end: + switch (stream_iter_status) { + case LTTNG_LIVE_ITERATOR_STATUS_OK: + case LTTNG_LIVE_ITERATOR_STATUS_AGAIN: + if (*count > 0) { + /* + * We received a again status but we have some messages + * to send downstream. We send them and return OK for + * now. On the next call we return again if there are + * still no new message to send. + */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + } else { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; + } + break; + case LTTNG_LIVE_ITERATOR_STATUS_END: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + break; + case LTTNG_LIVE_ITERATOR_STATUS_NOMEM: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + break; + case LTTNG_LIVE_ITERATOR_STATUS_ERROR: + case LTTNG_LIVE_ITERATOR_STATUS_INVAL: + case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + /* Put all existing messages on error. */ + put_messages(msgs, *count); + break; + default: + abort(); + } + +no_session: + return status; +} + +BT_HIDDEN +bt_self_message_iterator_status lttng_live_msg_iter_init( + bt_self_message_iterator *self_msg_it, + bt_self_component_source *self_comp_src, + bt_self_component_port_output *self_port) +{ + bt_self_message_iterator_status ret = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + bt_self_component *self_comp = + bt_self_component_source_as_self_component(self_comp_src); + struct lttng_live_component *lttng_live; + struct lttng_live_msg_iter *lttng_live_msg_iter; + + BT_ASSERT(self_msg_it); + + lttng_live = bt_self_component_get_data(self_comp); + + /* There can be only one downstream iterator at the same time. */ + BT_ASSERT(!lttng_live->has_msg_iter); + lttng_live->has_msg_iter = true; + + lttng_live_msg_iter = g_new0(struct lttng_live_msg_iter, 1); + if (!lttng_live_msg_iter) { + ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + lttng_live_msg_iter->lttng_live_comp = lttng_live; + lttng_live_msg_iter->self_msg_iter = self_msg_it; + + lttng_live_msg_iter->active_stream_iter = 0; + lttng_live_msg_iter->last_msg_ts_ns = INT64_MIN; + lttng_live_msg_iter->sessions = g_ptr_array_new_with_free_func( + (GDestroyNotify) lttng_live_destroy_session); + BT_ASSERT(lttng_live_msg_iter->sessions); + + lttng_live_msg_iter->viewer_connection = + live_viewer_connection_create(lttng_live->params.url->str, false, + lttng_live_msg_iter); + if (!lttng_live_msg_iter->viewer_connection) { + goto error; + } + + if (lttng_live_create_viewer_session(lttng_live_msg_iter)) { + goto error; + } + if (lttng_live_msg_iter->sessions->len == 0) { + switch (lttng_live->params.sess_not_found_act) { + case SESSION_NOT_FOUND_ACTION_CONTINUE: + BT_LOGI("Unable to connect to the requested live viewer " + "session. Keep trying to connect because of " + "%s=\"%s\" component parameter: url=\"%s\"", + SESS_NOT_FOUND_ACTION_PARAM, + SESS_NOT_FOUND_ACTION_CONTINUE_STR, + lttng_live->params.url->str); + break; + case SESSION_NOT_FOUND_ACTION_FAIL: + BT_LOGE("Unable to connect to the requested live viewer " + "session. Fail the message iterator" + "initialization because of %s=\"%s\" " + "component parameter: url =\"%s\"", + SESS_NOT_FOUND_ACTION_PARAM, + SESS_NOT_FOUND_ACTION_FAIL_STR, + lttng_live->params.url->str); + goto error; + case SESSION_NOT_FOUND_ACTION_END: + BT_LOGI("Unable to connect to the requested live viewer " + "session. End gracefully at the first _next() " + "call because of %s=\"%s\" component parameter: " + "url=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM, + SESS_NOT_FOUND_ACTION_END_STR, + lttng_live->params.url->str); + break; + } + } + + bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter); + + goto end; +error: + ret = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + lttng_live_msg_iter_destroy(lttng_live_msg_iter); +end: + return ret; +} + +static +bt_query_status lttng_live_query_list_sessions(const bt_value *params, + const bt_value **result) +{ + bt_query_status status = BT_QUERY_STATUS_OK; + const bt_value *url_value = NULL; + const char *url; + struct live_viewer_connection *viewer_connection = NULL; + + url_value = bt_value_map_borrow_entry_value_const(params, URL_PARAM); + if (!url_value) { + BT_LOGW("Mandatory `%s` parameter missing", URL_PARAM); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + if (!bt_value_is_string(url_value)) { + BT_LOGW("`%s` parameter is required to be a string value", + URL_PARAM); + status = BT_QUERY_STATUS_INVALID_PARAMS; + goto error; + } + + url = bt_value_string_get(url_value); + + viewer_connection = live_viewer_connection_create(url, true, NULL); + if (!viewer_connection) { + goto error; + } + + status = live_viewer_connection_list_sessions(viewer_connection, + result); + if (status != BT_QUERY_STATUS_OK) { + goto error; + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(*result); + + if (status >= 0) { + status = BT_QUERY_STATUS_ERROR; + } + +end: + if (viewer_connection) { + live_viewer_connection_destroy(viewer_connection); + } + return status; +} + +BT_HIDDEN +bt_query_status lttng_live_query(bt_self_component_class_source *comp_class, + const bt_query_executor *query_exec, + const char *object, const bt_value *params, + const bt_value **result) +{ + bt_query_status status = BT_QUERY_STATUS_OK; + + if (strcmp(object, "sessions") == 0) { + status = lttng_live_query_list_sessions(params, result); + } else { + BT_LOGW("Unknown query object `%s`", object); + status = BT_QUERY_STATUS_INVALID_OBJECT; + goto end; + } + +end: + return status; +} + +static +void lttng_live_component_destroy_data(struct lttng_live_component *lttng_live) +{ + if (!lttng_live) { + return; + } + if (lttng_live->params.url) { + g_string_free(lttng_live->params.url, TRUE); + } + g_free(lttng_live); +} + +BT_HIDDEN +void lttng_live_component_finalize(bt_self_component_source *component) +{ + void *data = bt_self_component_get_data( + bt_self_component_source_as_self_component(component)); + + if (!data) { + return; + } + lttng_live_component_destroy_data(data); +} + +static +enum session_not_found_action parse_session_not_found_action_param( + const bt_value *no_session_param) +{ + enum session_not_found_action action; + const char *no_session_act_str; + no_session_act_str = bt_value_string_get(no_session_param); + if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_CONTINUE_STR) == 0) { + action = SESSION_NOT_FOUND_ACTION_CONTINUE; + } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_FAIL_STR) == 0) { + action = SESSION_NOT_FOUND_ACTION_FAIL; + } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_END_STR) == 0) { + action = SESSION_NOT_FOUND_ACTION_END; + } else { + action = -1; + } + + return action; +} + +struct lttng_live_component *lttng_live_component_create(const bt_value *params) +{ + struct lttng_live_component *lttng_live; + const bt_value *value = NULL; + const char *url; + + lttng_live = g_new0(struct lttng_live_component, 1); + if (!lttng_live) { + goto end; + } + lttng_live->max_query_size = MAX_QUERY_SIZE; + lttng_live->has_msg_iter = false; + + value = bt_value_map_borrow_entry_value_const(params, URL_PARAM); + if (!value || !bt_value_is_string(value)) { + BT_LOGW("Mandatory `%s` parameter missing or not a string", + URL_PARAM); + goto error; + } + url = bt_value_string_get(value); + lttng_live->params.url = g_string_new(url); + if (!lttng_live->params.url) { + goto error; + } + + value = bt_value_map_borrow_entry_value_const(params, + SESS_NOT_FOUND_ACTION_PARAM); + + if (value && bt_value_is_string(value)) { + lttng_live->params.sess_not_found_act = + parse_session_not_found_action_param(value); + if (lttng_live->params.sess_not_found_act == -1) { + BT_LOGE("Unexpected value for `%s` parameter: " + "value=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM, + bt_value_string_get(value)); + goto error; + } + } else { + BT_LOGW("Optional `%s` parameter is missing or " + "not a string value. Defaulting to %s=\"%s\".", + SESS_NOT_FOUND_ACTION_PARAM, + SESS_NOT_FOUND_ACTION_PARAM, + SESS_NOT_FOUND_ACTION_CONTINUE_STR); + lttng_live->params.sess_not_found_act = + SESSION_NOT_FOUND_ACTION_CONTINUE; + } + + goto end; + +error: + lttng_live_component_destroy_data(lttng_live); + lttng_live = NULL; +end: + return lttng_live; +} + +BT_HIDDEN +bt_self_component_status lttng_live_component_init( + bt_self_component_source *self_comp, + const bt_value *params, UNUSED_VAR void *init_method_data) +{ + struct lttng_live_component *lttng_live; + bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; + + lttng_live = lttng_live_component_create(params); + if (!lttng_live) { + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto error; + } + lttng_live->self_comp = self_comp; + + if (lttng_live_graph_is_canceled(lttng_live)) { + ret = BT_SELF_COMPONENT_STATUS_END; + goto error; + } + + ret = bt_self_component_source_add_output_port( + lttng_live->self_comp, "out", + NULL, NULL); + if (ret != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + bt_self_component_set_data( + bt_self_component_source_as_self_component(self_comp), + lttng_live); + goto end; + +error: + lttng_live_component_destroy_data(lttng_live); + lttng_live = NULL; +end: + return ret; +} diff --git a/src/plugins/ctf/lttng-live/lttng-live.h b/src/plugins/ctf/lttng-live/lttng-live.h new file mode 100644 index 00000000..5c8dc41b --- /dev/null +++ b/src/plugins/ctf/lttng-live/lttng-live.h @@ -0,0 +1,300 @@ +#ifndef BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H +#define BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H + +/* + * BabelTrace - LTTng-live client Component + * + * Copyright 2019 Francis Deslauriers + * Copyright 2016 Jérémie Galarneau + * Copyright 2016 Mathieu Desnoyers + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "common/babeltrace.h" +#include + +#include "../common/metadata/decoder.h" +#include "../common/msg-iter/msg-iter.h" + +#include "viewer-connection.h" + +struct lttng_live_component; +struct lttng_live_session; +struct lttng_live_msg_iter; + +enum lttng_live_stream_state { + /* This stream won't have data until some known time in the future. */ + LTTNG_LIVE_STREAM_QUIESCENT, + /* + * This stream won't have data until some known time in the future and + * the message iterator inactivity message was already sent downstream. + */ + LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA, /* */ + /* This stream has data ready to be consumed. */ + LTTNG_LIVE_STREAM_ACTIVE_DATA, + /* + * This stream has no data left to consume. We should asked the relay + * for more. + */ + LTTNG_LIVE_STREAM_ACTIVE_NO_DATA, + /* This stream won't have anymore data, ever. */ + LTTNG_LIVE_STREAM_EOF, +}; + +/* Iterator over a live stream. */ +struct lttng_live_stream_iterator { + /* Owned by this. */ + bt_stream *stream; + + /* Weak reference. */ + struct lttng_live_trace *trace; + + /* + * Since only a single iterator per viewer connection, we have + * only a single message iterator per stream. + */ + struct bt_msg_iter *msg_iter; + + uint64_t viewer_stream_id; + + uint64_t ctf_stream_class_id; + + /* base offset in current index. */ + uint64_t base_offset; + /* len to read in current index. */ + uint64_t len; + /* offset in current index. */ + uint64_t offset; + + /* + * Clock Snapshot value of the last message iterator inactivity message + * sent downstream. + */ + uint64_t last_inactivity_ts; + + /* + * Clock Snapshot value of the current message iterator inactivity + * message we might want to send downstream. + */ + uint64_t current_inactivity_ts; + + enum lttng_live_stream_state state; + + /* + * The current message produced by this live stream iterator. Owned by + * this. + */ + bt_message *current_msg; + + /* Timestamp in nanoseconds of the current message (current_msg). */ + int64_t current_msg_ts_ns; + + /* Owned by this. */ + uint8_t *buf; + size_t buflen; + + /* Owned by this. */ + GString *name; +}; + +struct lttng_live_metadata { + /* Weak reference. */ + struct lttng_live_trace *trace; + + uint64_t stream_id; + /* Weak reference. */ + struct ctf_metadata_decoder *decoder; + + bool closed; +}; + +struct lttng_live_trace { + /* Back reference to session. */ + struct lttng_live_session *session; + + /* ctf trace ID within the session. */ + uint64_t id; + + /* Owned by this. */ + bt_trace *trace; + + /* Weak reference. */ + bt_trace_class *trace_class; + + struct lttng_live_metadata *metadata; + + const bt_clock_class *clock_class; + + /* Array of pointers to struct lttng_live_stream_iterator. */ + /* Owned by this. */ + GPtrArray *stream_iterators; + + bool new_metadata_needed; +}; + +struct lttng_live_session { + /* Weak reference. */ + struct lttng_live_msg_iter *lttng_live_msg_iter; + + /* Owned by this. */ + GString *hostname; + + /* Owned by this. */ + GString *session_name; + + uint64_t id; + + /* Array of pointers to struct lttng_live_trace. */ + GPtrArray *traces; + + bool attached; + bool new_streams_needed; + bool lazy_stream_msg_init; + bool closed; +}; + +enum session_not_found_action { + SESSION_NOT_FOUND_ACTION_CONTINUE, + SESSION_NOT_FOUND_ACTION_FAIL, + SESSION_NOT_FOUND_ACTION_END, +}; + +/* + * A component instance is an iterator on a single session. + */ +struct lttng_live_component { + /* Weak reference. */ + bt_self_component_source *self_comp; + + struct { + GString *url; + enum session_not_found_action sess_not_found_act; + } params; + + size_t max_query_size; + + /* + * Keeps track of whether the downstream component already has a + * message iterator on this component. + */ + bool has_msg_iter; +}; + +struct lttng_live_msg_iter { + /* Weak reference. */ + struct lttng_live_component *lttng_live_comp; + + /* Weak reference. */ + bt_self_message_iterator *self_msg_iter; + + /* Owned by this. */ + struct live_viewer_connection *viewer_connection; + + /* Array of pointers to struct lttng_live_session. */ + GPtrArray *sessions; + + /* Number of live stream iterator this message iterator has.*/ + uint64_t active_stream_iter; + + /* Timestamp in nanosecond of the last message sent downstream. */ + int64_t last_msg_ts_ns; +}; + +enum lttng_live_iterator_status { + /** Iterator state has progressed. Continue iteration immediately. */ + LTTNG_LIVE_ITERATOR_STATUS_CONTINUE = 3, + /** No message available for now. Try again later. */ + LTTNG_LIVE_ITERATOR_STATUS_AGAIN = 2, + /** No more CTF_LTTNG_LIVEs to be delivered. */ + LTTNG_LIVE_ITERATOR_STATUS_END = 1, + /** No error, okay. */ + LTTNG_LIVE_ITERATOR_STATUS_OK = 0, + /** Invalid arguments. */ + LTTNG_LIVE_ITERATOR_STATUS_INVAL = -1, + /** General error. */ + LTTNG_LIVE_ITERATOR_STATUS_ERROR = -2, + /** Out of memory. */ + LTTNG_LIVE_ITERATOR_STATUS_NOMEM = -3, + /** Unsupported iterator feature. */ + LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED = -4, +}; + +bt_self_component_status lttng_live_component_init( + bt_self_component_source *self_comp, + const bt_value *params, void *init_method_data); + +bt_query_status lttng_live_query( + bt_self_component_class_source *comp_class, + const bt_query_executor *query_exec, + const char *object, const bt_value *params, + const bt_value **result); + +void lttng_live_component_finalize(bt_self_component_source *component); + +bt_self_message_iterator_status lttng_live_msg_iter_next( + bt_self_message_iterator *iterator, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +bt_self_message_iterator_status lttng_live_msg_iter_init( + bt_self_message_iterator *self_msg_it, + bt_self_component_source *self_comp, + bt_self_component_port_output *self_port); + +void lttng_live_msg_iter_finalize(bt_self_message_iterator *it); + +int lttng_live_create_viewer_session(struct lttng_live_msg_iter *lttng_live_msg_iter); +int lttng_live_attach_session(struct lttng_live_session *session); +int lttng_live_detach_session(struct lttng_live_session *session); +enum lttng_live_iterator_status lttng_live_get_new_streams( + struct lttng_live_session *session); + +int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter, + uint64_t session_id, + const char *hostname, + const char *session_name); + +ssize_t lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, + FILE *fp); +enum lttng_live_iterator_status lttng_live_get_next_index( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream, + struct packet_index *index); + +enum bt_msg_iter_medium_status lttng_live_get_stream_bytes( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream, uint8_t *buf, + uint64_t offset, uint64_t req_len, uint64_t *recv_len); +void lttng_live_add_stream_iterator(struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream_iter); +void lttng_live_remove_stream_iterator(struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream_iter); + +struct lttng_live_trace *lttng_live_borrow_trace( + struct lttng_live_session *session, uint64_t trace_id); +void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter); + +bool lttng_live_graph_is_canceled(struct lttng_live_component *lttng_live); + +#endif /* BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H */ diff --git a/src/plugins/ctf/lttng-live/lttng-viewer-abi.h b/src/plugins/ctf/lttng-live/lttng-viewer-abi.h new file mode 100644 index 00000000..9f6154ba --- /dev/null +++ b/src/plugins/ctf/lttng-live/lttng-viewer-abi.h @@ -0,0 +1,254 @@ +#ifndef LTTNG_VIEWER_ABI_H +#define LTTNG_VIEWER_ABI_H + +/* + * Copyright (C) 2013 - Julien Desfossez + * Mathieu Desnoyers + * David Goulet + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "compat/limits.h" + +#define LTTNG_VIEWER_PATH_MAX 4096 +#define LTTNG_VIEWER_NAME_MAX 255 +#define LTTNG_VIEWER_HOST_NAME_MAX 64 + +/* Flags in reply to get_next_index and get_packet. */ +enum { + /* New metadata is required to read this packet. */ + LTTNG_VIEWER_FLAG_NEW_METADATA = (1 << 0), + /* New stream got added to the trace. */ + LTTNG_VIEWER_FLAG_NEW_STREAM = (1 << 1), +}; + +enum lttng_viewer_command { + LTTNG_VIEWER_CONNECT = 1, + LTTNG_VIEWER_LIST_SESSIONS = 2, + LTTNG_VIEWER_ATTACH_SESSION = 3, + LTTNG_VIEWER_GET_NEXT_INDEX = 4, + LTTNG_VIEWER_GET_PACKET = 5, + LTTNG_VIEWER_GET_METADATA = 6, + LTTNG_VIEWER_GET_NEW_STREAMS = 7, + LTTNG_VIEWER_CREATE_SESSION = 8, + LTTNG_VIEWER_DETACH_SESSION = 9, +}; + +enum lttng_viewer_attach_return_code { + LTTNG_VIEWER_ATTACH_OK = 1, /* The attach command succeeded. */ + LTTNG_VIEWER_ATTACH_ALREADY = 2, /* A viewer is already attached. */ + LTTNG_VIEWER_ATTACH_UNK = 3, /* The session ID is unknown. */ + LTTNG_VIEWER_ATTACH_NOT_LIVE = 4, /* The session is not live. */ + LTTNG_VIEWER_ATTACH_SEEK_ERR = 5, /* Seek error. */ + LTTNG_VIEWER_ATTACH_NO_SESSION = 6, /* No viewer session created. */ +}; + +enum lttng_viewer_detach_session_return_code { + LTTNG_VIEWER_DETACH_SESSION_OK = 1, + LTTNG_VIEWER_DETACH_SESSION_UNK = 2, + LTTNG_VIEWER_DETACH_SESSION_ERR = 3, +}; + +enum lttng_viewer_next_index_return_code { + LTTNG_VIEWER_INDEX_OK = 1, /* Index is available. */ + LTTNG_VIEWER_INDEX_RETRY = 2, /* Index not yet available. */ + LTTNG_VIEWER_INDEX_HUP = 3, /* Index closed (trace destroyed). */ + LTTNG_VIEWER_INDEX_ERR = 4, /* Unknow error. */ + LTTNG_VIEWER_INDEX_INACTIVE = 5, /* Inactive stream beacon. */ + LTTNG_VIEWER_INDEX_EOF = 6, /* End of index file. */ +}; + +enum lttng_viewer_get_packet_return_code { + LTTNG_VIEWER_GET_PACKET_OK = 1, + LTTNG_VIEWER_GET_PACKET_RETRY = 2, + LTTNG_VIEWER_GET_PACKET_ERR = 3, + LTTNG_VIEWER_GET_PACKET_EOF = 4, +}; + +enum lttng_viewer_get_metadata_return_code { + LTTNG_VIEWER_METADATA_OK = 1, + LTTNG_VIEWER_NO_NEW_METADATA = 2, + LTTNG_VIEWER_METADATA_ERR = 3, +}; + +enum lttng_viewer_connection_type { + LTTNG_VIEWER_CLIENT_COMMAND = 1, + LTTNG_VIEWER_CLIENT_MESSAGE = 2, +}; + +enum lttng_viewer_seek { + /* Receive the trace packets from the beginning. */ + LTTNG_VIEWER_SEEK_BEGINNING = 1, + /* Receive the trace packets from now. */ + LTTNG_VIEWER_SEEK_LAST = 2, +}; + +enum lttng_viewer_new_streams_return_code { + LTTNG_VIEWER_NEW_STREAMS_OK = 1, /* If new streams are being sent. */ + LTTNG_VIEWER_NEW_STREAMS_NO_NEW = 2, /* If no new streams are available. */ + LTTNG_VIEWER_NEW_STREAMS_ERR = 3, /* Error. */ + LTTNG_VIEWER_NEW_STREAMS_HUP = 4, /* Session closed. */ +}; + +enum lttng_viewer_create_session_return_code { + LTTNG_VIEWER_CREATE_SESSION_OK = 1, + LTTNG_VIEWER_CREATE_SESSION_ERR = 2, +}; + +struct lttng_viewer_session { + uint64_t id; + uint32_t live_timer; + uint32_t clients; + uint32_t streams; + char hostname[LTTNG_VIEWER_HOST_NAME_MAX]; + char session_name[LTTNG_VIEWER_NAME_MAX]; +} __attribute__((__packed__)); + +struct lttng_viewer_stream { + uint64_t id; + uint64_t ctf_trace_id; + uint32_t metadata_flag; + char path_name[LTTNG_VIEWER_PATH_MAX]; + char channel_name[LTTNG_VIEWER_NAME_MAX]; +} __attribute__((__packed__)); + +struct lttng_viewer_cmd { + uint64_t data_size; /* data size following this header */ + uint32_t cmd; /* enum lttcomm_relayd_command */ + uint32_t cmd_version; /* command version */ +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_CONNECT payload. + */ +struct lttng_viewer_connect { + /* session ID assigned by the relay for command connections */ + uint64_t viewer_session_id; + uint32_t major; + uint32_t minor; + uint32_t type; /* enum lttng_viewer_connection_type */ +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_LIST_SESSIONS payload. + */ +struct lttng_viewer_list_sessions { + uint32_t sessions_count; + char session_list[]; /* struct lttng_viewer_session */ +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_ATTACH_SESSION payload. + */ +struct lttng_viewer_attach_session_request { + uint64_t session_id; + uint64_t offset; /* unused for now */ + uint32_t seek; /* enum lttng_viewer_seek */ +} __attribute__((__packed__)); + +struct lttng_viewer_attach_session_response { + /* enum lttng_viewer_attach_return_code */ + uint32_t status; + uint32_t streams_count; + /* struct lttng_viewer_stream */ + char stream_list[]; +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_GET_NEXT_INDEX payload. + */ +struct lttng_viewer_get_next_index { + uint64_t stream_id; +} __attribute__ ((__packed__)); + +struct lttng_viewer_index { + uint64_t offset; + uint64_t packet_size; + uint64_t content_size; + uint64_t timestamp_begin; + uint64_t timestamp_end; + uint64_t events_discarded; + uint64_t stream_id; + uint32_t status; /* enum lttng_viewer_next_index_return_code */ + uint32_t flags; /* LTTNG_VIEWER_FLAG_* */ +} __attribute__ ((__packed__)); + +/* + * LTTNG_VIEWER_GET_PACKET payload. + */ +struct lttng_viewer_get_packet { + uint64_t stream_id; + uint64_t offset; + uint32_t len; +} __attribute__((__packed__)); + +struct lttng_viewer_trace_packet { + uint32_t status; /* enum lttng_viewer_get_packet_return_code */ + uint32_t len; + uint32_t flags; /* LTTNG_VIEWER_FLAG_* */ + char data[]; +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_GET_METADATA payload. + */ +struct lttng_viewer_get_metadata { + uint64_t stream_id; +} __attribute__((__packed__)); + +struct lttng_viewer_metadata_packet { + uint64_t len; + uint32_t status; /* enum lttng_viewer_get_metadata_return_code */ + char data[]; +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_GET_NEW_STREAMS payload. + */ +struct lttng_viewer_new_streams_request { + uint64_t session_id; +} __attribute__((__packed__)); + +struct lttng_viewer_new_streams_response { + /* enum lttng_viewer_new_streams_return_code */ + uint32_t status; + uint32_t streams_count; + /* struct lttng_viewer_stream */ + char stream_list[]; +} __attribute__((__packed__)); + +struct lttng_viewer_create_session_response { + /* enum lttng_viewer_create_session_return_code */ + uint32_t status; +} __attribute__((__packed__)); + +/* + * LTTNG_VIEWER_DETACH_SESSION payload. + */ +struct lttng_viewer_detach_session_request { + uint64_t session_id; +} __attribute__((__packed__)); + +struct lttng_viewer_detach_session_response { + /* enum lttng_viewer_detach_session_return_code */ + uint32_t status; +} __attribute__((__packed__)); + +#endif /* LTTNG_VIEWER_ABI_H */ diff --git a/src/plugins/ctf/lttng-live/metadata.c b/src/plugins/ctf/lttng-live/metadata.c new file mode 100644 index 00000000..2d605e15 --- /dev/null +++ b/src/plugins/ctf/lttng-live/metadata.c @@ -0,0 +1,314 @@ +/* + * Copyright 2019 - Francis Deslauriers + * Copyright 2016 - Philippe Proulx + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Some functions are based on older functions written by Mathieu Desnoyers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-METADATA" +#include "logging.h" + +#include +#include +#include +#include +#include +#include "compat/memstream.h" +#include + +#include "metadata.h" +#include "../common/metadata/decoder.h" + +#define TSDL_MAGIC 0x75d11d57 + +struct packet_header { + uint32_t magic; + uint8_t uuid[16]; + uint32_t checksum; + uint32_t content_size; + uint32_t packet_size; + uint8_t compression_scheme; + uint8_t encryption_scheme; + uint8_t checksum_scheme; + uint8_t major; + uint8_t minor; +} __attribute__((__packed__)); + + +static +bool stream_classes_all_have_default_clock_class(bt_trace_class *tc) +{ + uint64_t i, sc_count; + const bt_clock_class *cc = NULL; + const bt_stream_class *sc; + bool ret = true; + + sc_count = bt_trace_class_get_stream_class_count(tc); + for (i = 0; i < sc_count; i++) { + sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i); + + BT_ASSERT(sc); + + cc = bt_stream_class_borrow_default_clock_class_const(sc); + if (!cc) { + ret = false; + BT_LOGE("Stream class doesn't have a default clock class: " + "sc-id=%" PRIu64 ", sc-name=\"%s\"", + bt_stream_class_get_id(sc), + bt_stream_class_get_name(sc)); + goto end; + } + } + +end: + return ret; +} +/* + * Iterate over the stream classes and returns the first clock class + * encountered. This is useful to create message iterator inactivity message as + * we don't need a particular clock class. + */ +static +const bt_clock_class *borrow_any_clock_class(bt_trace_class *tc) +{ + uint64_t i, sc_count; + const bt_clock_class *cc = NULL; + const bt_stream_class *sc; + + sc_count = bt_trace_class_get_stream_class_count(tc); + for (i = 0; i < sc_count; i++) { + sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i); + BT_ASSERT(sc); + + cc = bt_stream_class_borrow_default_clock_class_const(sc); + if (cc) { + goto end; + } + } +end: + BT_ASSERT(cc); + return cc; +} + +BT_HIDDEN +enum lttng_live_iterator_status lttng_live_metadata_update( + struct lttng_live_trace *trace) +{ + struct lttng_live_session *session = trace->session; + struct lttng_live_metadata *metadata = trace->metadata; + struct lttng_live_component *lttng_live = + session->lttng_live_msg_iter->lttng_live_comp; + ssize_t ret = 0; + size_t size, len_read = 0; + char *metadata_buf = NULL; + FILE *fp = NULL; + enum ctf_metadata_decoder_status decoder_status; + enum lttng_live_iterator_status status = + LTTNG_LIVE_ITERATOR_STATUS_OK; + + /* No metadata stream yet. */ + if (!metadata) { + if (session->new_streams_needed) { + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + } else { + session->new_streams_needed = true; + status = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE; + } + goto end; + } + + if (!metadata->trace) { + trace->new_metadata_needed = false; + } + + if (!trace->new_metadata_needed) { + goto end; + } + + /* Open for writing */ + fp = bt_open_memstream(&metadata_buf, &size); + if (!fp) { + BT_LOGE("Metadata open_memstream: %s", strerror(errno)); + goto error; + } + + /* Grab all available metadata. */ + do { + /* + * get_one_metadata_packet returns the number of bytes + * received, 0 when we have received everything, a + * negative value on error. + */ + ret = lttng_live_get_one_metadata_packet(trace, fp); + if (ret > 0) { + len_read += ret; + } + } while (ret > 0); + + /* + * Consider metadata closed as soon as we get an error reading + * it (e.g. cannot be found). + */ + if (ret < 0) { + if (!metadata->closed) { + metadata->closed = true; + /* + * Release our reference on the trace as soon as + * we know the metadata stream is not available + * anymore. This won't necessarily teardown the + * metadata objects immediately, but only when + * the data streams are done. + */ + metadata->trace = NULL; + } + if (errno == EINTR) { + if (lttng_live_graph_is_canceled(lttng_live)) { + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + goto end; + } + } + } + + if (bt_close_memstream(&metadata_buf, &size, fp)) { + BT_LOGE("bt_close_memstream: %s", strerror(errno)); + } + ret = 0; + fp = NULL; + + if (len_read == 0) { + if (!trace->trace) { + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + goto end; + } + trace->new_metadata_needed = false; + goto end; + } + + fp = bt_fmemopen(metadata_buf, len_read, "rb"); + if (!fp) { + BT_LOGE("Cannot memory-open metadata buffer: %s", + strerror(errno)); + goto error; + } + + /* + * The call to ctf_metadata_decoder_decode will append new metadata to + * our current trace class. + */ + decoder_status = ctf_metadata_decoder_decode(metadata->decoder, fp); + switch (decoder_status) { + case CTF_METADATA_DECODER_STATUS_OK: + if (!trace->trace_class) { + trace->trace_class = + ctf_metadata_decoder_get_ir_trace_class( + metadata->decoder); + trace->trace = bt_trace_create(trace->trace_class); + if (!stream_classes_all_have_default_clock_class( + trace->trace_class)) { + /* Error logged in function. */ + goto error; + } + trace->clock_class = + borrow_any_clock_class(trace->trace_class); + } + trace->new_metadata_needed = false; + + break; + case CTF_METADATA_DECODER_STATUS_INCOMPLETE: + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + break; + case CTF_METADATA_DECODER_STATUS_ERROR: + case CTF_METADATA_DECODER_STATUS_INVAL_VERSION: + case CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR: + goto error; + } + + goto end; +error: + status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; +end: + if (fp) { + int closeret; + + closeret = fclose(fp); + if (closeret) { + BT_LOGE("Error on fclose"); + } + } + free(metadata_buf); + return status; +} + +BT_HIDDEN +int lttng_live_metadata_create_stream(struct lttng_live_session *session, + uint64_t ctf_trace_id, + uint64_t stream_id, + const char *trace_name) +{ + struct lttng_live_component *lttng_live = + session->lttng_live_msg_iter->lttng_live_comp; + struct lttng_live_metadata *metadata = NULL; + struct lttng_live_trace *trace; + const char *match; + + metadata = g_new0(struct lttng_live_metadata, 1); + if (!metadata) { + return -1; + } + metadata->stream_id = stream_id; + + match = strstr(trace_name, session->session_name->str); + if (!match) { + goto error; + } + + metadata->decoder = ctf_metadata_decoder_create( + lttng_live->self_comp, NULL); + if (!metadata->decoder) { + goto error; + } + trace = lttng_live_borrow_trace(session, ctf_trace_id); + if (!trace) { + goto error; + } + metadata->trace = trace; + trace->metadata = metadata; + return 0; + +error: + ctf_metadata_decoder_destroy(metadata->decoder); + g_free(metadata); + return -1; +} + +BT_HIDDEN +void lttng_live_metadata_fini(struct lttng_live_trace *trace) +{ + struct lttng_live_metadata *metadata = trace->metadata; + + if (!metadata) { + return; + } + ctf_metadata_decoder_destroy(metadata->decoder); + trace->metadata = NULL; + g_free(metadata); +} diff --git a/src/plugins/ctf/lttng-live/metadata.h b/src/plugins/ctf/lttng-live/metadata.h new file mode 100644 index 00000000..2cf8ae19 --- /dev/null +++ b/src/plugins/ctf/lttng-live/metadata.h @@ -0,0 +1,41 @@ +#ifndef LTTNG_LIVE_METADATA_H +#define LTTNG_LIVE_METADATA_H + +/* + * Copyright 2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include +#include "lttng-live.h" + +int lttng_live_metadata_create_stream(struct lttng_live_session *session, + uint64_t ctf_trace_id, uint64_t stream_id, + const char *trace_name); + +enum lttng_live_iterator_status lttng_live_metadata_update( + struct lttng_live_trace *trace); + +void lttng_live_metadata_fini(struct lttng_live_trace *trace); + +#endif /* LTTNG_LIVE_METADATA_H */ diff --git a/src/plugins/ctf/lttng-live/viewer-connection.c b/src/plugins/ctf/lttng-live/viewer-connection.c new file mode 100644 index 00000000..4a74ff9f --- /dev/null +++ b/src/plugins/ctf/lttng-live/viewer-connection.c @@ -0,0 +1,1524 @@ +/* + * Copyright 2019 - Francis Deslauriers + * Copyright 2016 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-LIVE-SRC-VIEWER" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat/socket.h" +#include "compat/endian.h" +#include "compat/compiler.h" +#include "common/common.h" +#include + +#include "lttng-live.h" +#include "viewer-connection.h" +#include "lttng-viewer-abi.h" +#include "data-stream.h" +#include "metadata.h" + +static +ssize_t lttng_live_recv(struct live_viewer_connection *viewer_connection, + void *buf, size_t len) +{ + ssize_t ret; + size_t copied = 0, to_copy = len; + struct lttng_live_msg_iter *lttng_live_msg_iter = + viewer_connection->lttng_live_msg_iter; + BT_SOCKET sock = viewer_connection->control_sock; + + do { + ret = bt_socket_recv(sock, buf + copied, to_copy, 0); + if (ret > 0) { + BT_ASSERT(ret <= to_copy); + copied += ret; + to_copy -= ret; + } + if (ret == BT_SOCKET_ERROR && bt_socket_interrupted()) { + if (!viewer_connection->in_query && + lttng_live_graph_is_canceled(lttng_live_msg_iter->lttng_live_comp)) { + break; + } else { + continue; + } + } + } while (ret > 0 && to_copy > 0); + if (ret > 0) + ret = copied; + /* ret = 0 means orderly shutdown, ret == BT_SOCKET_ERROR is error. */ + return ret; +} + +static +ssize_t lttng_live_send(struct live_viewer_connection *viewer_connection, + const void *buf, size_t len) +{ + struct lttng_live_msg_iter *lttng_live_msg_iter = + viewer_connection->lttng_live_msg_iter; + BT_SOCKET sock = viewer_connection->control_sock; + ssize_t ret; + + for (;;) { + ret = bt_socket_send_nosigpipe(sock, buf, len); + if (ret == BT_SOCKET_ERROR && bt_socket_interrupted()) { + if (!viewer_connection->in_query && + lttng_live_graph_is_canceled(lttng_live_msg_iter->lttng_live_comp)) { + break; + } else { + continue; + } + } else { + break; + } + } + return ret; +} + +static +int parse_url(struct live_viewer_connection *viewer_connection) +{ + char error_buf[256] = { 0 }; + struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; + int ret = -1; + const char *path = viewer_connection->url->str; + + if (!path) { + goto end; + } + + lttng_live_url_parts = bt_common_parse_lttng_live_url(path, + error_buf, sizeof(error_buf)); + if (!lttng_live_url_parts.proto) { + BT_LOGW("Invalid LTTng live URL format: %s", error_buf); + goto end; + } + + viewer_connection->relay_hostname = + lttng_live_url_parts.hostname; + lttng_live_url_parts.hostname = NULL; + + if (lttng_live_url_parts.port >= 0) { + viewer_connection->port = lttng_live_url_parts.port; + } else { + viewer_connection->port = LTTNG_DEFAULT_NETWORK_VIEWER_PORT; + } + + viewer_connection->target_hostname = + lttng_live_url_parts.target_hostname; + lttng_live_url_parts.target_hostname = NULL; + + if (lttng_live_url_parts.session_name) { + viewer_connection->session_name = + lttng_live_url_parts.session_name; + lttng_live_url_parts.session_name = NULL; + } + + BT_LOGD("Connecting to hostname : %s, port : %d, " + "target hostname : %s, session name : %s, " + "proto : %s", + viewer_connection->relay_hostname->str, + viewer_connection->port, + viewer_connection->target_hostname == NULL ? + "" : viewer_connection->target_hostname->str, + viewer_connection->session_name == NULL ? + "" : viewer_connection->session_name->str, + lttng_live_url_parts.proto->str); + ret = 0; + +end: + bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); + return ret; +} + +static +int lttng_live_handshake(struct live_viewer_connection *viewer_connection) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_connect connect; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect); + char cmd_buf[cmd_buf_len]; + int ret; + ssize_t ret_len; + + cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT); + cmd.data_size = htobe64((uint64_t) sizeof(connect)); + cmd.cmd_version = htobe32(0); + + connect.viewer_session_id = -1ULL; /* will be set on recv */ + connect.major = htobe32(LTTNG_LIVE_MAJOR); + connect.minor = htobe32(LTTNG_LIVE_MINOR); + connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &connect, sizeof(connect)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending version: %s", bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + + ret_len = lttng_live_recv(viewer_connection, &connect, sizeof(connect)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving version: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(connect)); + + BT_LOGD("Received viewer session ID : %" PRIu64, + (uint64_t) be64toh(connect.viewer_session_id)); + BT_LOGD("Relayd version : %u.%u", be32toh(connect.major), + be32toh(connect.minor)); + + if (LTTNG_LIVE_MAJOR != be32toh(connect.major)) { + BT_LOGE("Incompatible lttng-relayd protocol"); + goto error; + } + /* Use the smallest protocol version implemented. */ + if (LTTNG_LIVE_MINOR > be32toh(connect.minor)) { + viewer_connection->minor = be32toh(connect.minor); + } else { + viewer_connection->minor = LTTNG_LIVE_MINOR; + } + viewer_connection->major = LTTNG_LIVE_MAJOR; + ret = 0; + return ret; + +error: + BT_LOGE("Unable to establish connection"); + return -1; +} + +static +int lttng_live_connect_viewer(struct live_viewer_connection *viewer_connection) +{ + struct hostent *host; + struct sockaddr_in server_addr; + int ret; + + if (parse_url(viewer_connection)) { + goto error; + } + + host = gethostbyname(viewer_connection->relay_hostname->str); + if (!host) { + BT_LOGE("Cannot lookup hostname %s", + viewer_connection->relay_hostname->str); + goto error; + } + + if ((viewer_connection->control_sock = socket(AF_INET, SOCK_STREAM, 0)) == BT_INVALID_SOCKET) { + BT_LOGE("Socket creation failed: %s", bt_socket_errormsg()); + goto error; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(viewer_connection->port); + server_addr.sin_addr = *((struct in_addr *) host->h_addr); + memset(&(server_addr.sin_zero), 0, 8); + + if (connect(viewer_connection->control_sock, (struct sockaddr *) &server_addr, + sizeof(struct sockaddr)) == BT_SOCKET_ERROR) { + BT_LOGE("Connection failed: %s", bt_socket_errormsg()); + goto error; + } + if (lttng_live_handshake(viewer_connection)) { + goto error; + } + + ret = 0; + + return ret; + +error: + if (viewer_connection->control_sock != BT_INVALID_SOCKET) { + if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) { + BT_LOGE("Close: %s", bt_socket_errormsg()); + } + } + viewer_connection->control_sock = BT_INVALID_SOCKET; + return -1; +} + +static +void lttng_live_disconnect_viewer( + struct live_viewer_connection *viewer_connection) +{ + if (viewer_connection->control_sock == BT_INVALID_SOCKET) { + return; + } + if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) { + BT_LOGE("Close: %s", bt_socket_errormsg()); + viewer_connection->control_sock = BT_INVALID_SOCKET; + } +} + +static +void connection_release(bt_object *obj) +{ + struct live_viewer_connection *conn = + container_of(obj, struct live_viewer_connection, obj); + + live_viewer_connection_destroy(conn); +} + +static +int list_update_session(bt_value *results, + const struct lttng_viewer_session *session, + bool *_found) +{ + int ret = 0; + bt_value *map = NULL; + bt_value *hostname = NULL; + bt_value *session_name = NULL; + bt_value *btval = NULL; + int i, len; + bool found = false; + + len = bt_value_array_get_size(results); + if (len < 0) { + BT_LOGE_STR("Error getting size of array."); + ret = -1; + goto end; + } + for (i = 0; i < len; i++) { + const char *hostname_str = NULL; + const char *session_name_str = NULL; + + map = bt_value_array_borrow_element_by_index(results, (size_t) i); + if (!map) { + BT_LOGE_STR("Error borrowing map."); + ret = -1; + goto end; + } + hostname = bt_value_map_borrow_entry_value(map, "target-hostname"); + if (!hostname) { + BT_LOGE_STR("Error borrowing \"target-hostname\" entry."); + ret = -1; + goto end; + } + session_name = bt_value_map_borrow_entry_value(map, "session-name"); + if (!session_name) { + BT_LOGE_STR("Error borrowing \"session-name\" entry."); + ret = -1; + goto end; + } + hostname_str = bt_value_string_get(hostname); + session_name_str = bt_value_string_get(session_name); + + if (!strcmp(session->hostname, hostname_str) + && !strcmp(session->session_name, + session_name_str)) { + int64_t val; + uint32_t streams = be32toh(session->streams); + uint32_t clients = be32toh(session->clients); + + found = true; + + btval = bt_value_map_borrow_entry_value(map, "stream-count"); + if (!btval) { + BT_LOGE_STR("Error borrowing \"stream-count\" entry."); + ret = -1; + goto end; + } + val = bt_value_signed_integer_get(btval); + /* sum */ + val += streams; + bt_value_signed_integer_set(btval, val); + + btval = bt_value_map_borrow_entry_value(map, "client-count"); + if (!btval) { + BT_LOGE_STR("Error borrowing \"client-count\" entry."); + ret = -1; + goto end; + } + val = bt_value_signed_integer_get(btval); + /* max */ + val = max_t(int64_t, clients, val); + bt_value_signed_integer_set(btval, val); + } + + if (found) { + break; + } + } +end: + *_found = found; + return ret; +} + +static +int list_append_session(bt_value *results, + GString *base_url, + const struct lttng_viewer_session *session) +{ + int ret = 0; + bt_value_status ret_status; + bt_value *map = NULL; + GString *url = NULL; + bool found = false; + + /* + * If the session already exists, add the stream count to it, + * and do max of client counts. + */ + ret = list_update_session(results, session, &found); + if (ret || found) { + goto end; + } + + map = bt_value_map_create(); + if (!map) { + BT_LOGE_STR("Error creating map value."); + ret = -1; + goto end; + } + + if (base_url->len < 1) { + BT_LOGE_STR("Error: base_url length smaller than 1."); + ret = -1; + goto end; + } + /* + * key = "url", + * value = , + */ + url = g_string_new(base_url->str); + g_string_append(url, "/host/"); + g_string_append(url, session->hostname); + g_string_append_c(url, '/'); + g_string_append(url, session->session_name); + + ret_status = bt_value_map_insert_string_entry(map, "url", url->str); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"url\" entry."); + ret = -1; + goto end; + } + + /* + * key = "target-hostname", + * value = , + */ + ret_status = bt_value_map_insert_string_entry(map, "target-hostname", + session->hostname); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"target-hostname\" entry."); + ret = -1; + goto end; + } + + /* + * key = "session-name", + * value = , + */ + ret_status = bt_value_map_insert_string_entry(map, "session-name", + session->session_name); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"session-name\" entry."); + ret = -1; + goto end; + } + + /* + * key = "timer-us", + * value = , + */ + { + uint32_t live_timer = be32toh(session->live_timer); + + ret_status = bt_value_map_insert_signed_integer_entry( + map, "timer-us", live_timer); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"timer-us\" entry."); + ret = -1; + goto end; + } + } + + /* + * key = "stream-count", + * value = , + */ + { + uint32_t streams = be32toh(session->streams); + + ret_status = bt_value_map_insert_signed_integer_entry(map, + "stream-count", streams); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"stream-count\" entry."); + ret = -1; + goto end; + } + } + + /* + * key = "client-count", + * value = , + */ + { + uint32_t clients = be32toh(session->clients); + + ret_status = bt_value_map_insert_signed_integer_entry(map, + "client-count", clients); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error inserting \"client-count\" entry."); + ret = -1; + goto end; + } + } + + ret_status = bt_value_array_append_element(results, map); + if (ret_status != BT_VALUE_STATUS_OK) { + BT_LOGE_STR("Error appending map to results."); + ret = -1; + } + +end: + if (url) { + g_string_free(url, true); + } + BT_VALUE_PUT_REF_AND_RESET(map); + return ret; +} + +/* + * Data structure returned: + * + * { + * = { + * [n] = { + * = { + * { + * key = "url", + * value = , + * }, + * { + * key = "target-hostname", + * value = , + * }, + * { + * key = "session-name", + * value = , + * }, + * { + * key = "timer-us", + * value = , + * }, + * { + * key = "stream-count", + * value = , + * }, + * { + * key = "client-count", + * value = , + * }, + * }, + * } + * } + */ + +BT_HIDDEN +bt_query_status live_viewer_connection_list_sessions( + struct live_viewer_connection *viewer_connection, + const bt_value **user_result) +{ + bt_query_status status = BT_QUERY_STATUS_OK; + bt_value *result = NULL; + struct lttng_viewer_cmd cmd; + struct lttng_viewer_list_sessions list; + uint32_t i, sessions_count; + ssize_t ret_len; + + if (lttng_live_handshake(viewer_connection)) { + goto error; + } + + result = bt_value_array_create(); + if (!result) { + BT_LOGE("Error creating array"); + status = BT_QUERY_STATUS_NOMEM; + goto error; + } + + cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); + cmd.data_size = htobe64((uint64_t) 0); + cmd.cmd_version = htobe32(0); + + ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); + status = BT_QUERY_STATUS_ERROR; + goto error; + } + BT_ASSERT(ret_len == sizeof(cmd)); + + ret_len = lttng_live_recv(viewer_connection, &list, sizeof(list)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + status = BT_QUERY_STATUS_ERROR; + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving session list: %s", bt_socket_errormsg()); + status = BT_QUERY_STATUS_ERROR; + goto error; + } + BT_ASSERT(ret_len == sizeof(list)); + + sessions_count = be32toh(list.sessions_count); + for (i = 0; i < sessions_count; i++) { + struct lttng_viewer_session lsession; + + ret_len = lttng_live_recv(viewer_connection, &lsession, + sizeof(lsession)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + status = BT_QUERY_STATUS_ERROR; + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving session: %s", bt_socket_errormsg()); + status = BT_QUERY_STATUS_ERROR; + goto error; + } + BT_ASSERT(ret_len == sizeof(lsession)); + lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0'; + lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; + if (list_append_session(result, viewer_connection->url, + &lsession)) { + status = BT_QUERY_STATUS_ERROR; + goto error; + } + } + + *user_result = result; + goto end; +error: + BT_VALUE_PUT_REF_AND_RESET(result); +end: + return status; +} + +static +int lttng_live_query_session_ids(struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_list_sessions list; + struct lttng_viewer_session lsession; + uint32_t i, sessions_count; + ssize_t ret_len; + uint64_t session_id; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + + cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS); + cmd.data_size = htobe64((uint64_t) 0); + cmd.cmd_version = htobe32(0); + + ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(cmd)); + + ret_len = lttng_live_recv(viewer_connection, &list, sizeof(list)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving session list: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(list)); + + sessions_count = be32toh(list.sessions_count); + for (i = 0; i < sessions_count; i++) { + ret_len = lttng_live_recv(viewer_connection, + &lsession, sizeof(lsession)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving session: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(lsession)); + lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0'; + lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; + session_id = be64toh(lsession.id); + + BT_LOGD("Adding session %" PRIu64 " hostname: %s session_name: %s", + session_id, lsession.hostname, lsession.session_name); + + if ((strncmp(lsession.session_name, + viewer_connection->session_name->str, + LTTNG_VIEWER_NAME_MAX) == 0) && (strncmp(lsession.hostname, + viewer_connection->target_hostname->str, + LTTNG_VIEWER_HOST_NAME_MAX) == 0)) { + if (lttng_live_add_session(lttng_live_msg_iter, session_id, + lsession.hostname, + lsession.session_name)) { + goto error; + } + } + } + + return 0; + +error: + BT_LOGE("Unable to query session ids"); + return -1; +} + +BT_HIDDEN +int lttng_live_create_viewer_session( + struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_create_session_response resp; + ssize_t ret_len; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + + cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION); + cmd.data_size = htobe64((uint64_t) 0); + cmd.cmd_version = htobe32(0); + + ret_len = lttng_live_send(viewer_connection, &cmd, sizeof(cmd)); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending cmd: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(cmd)); + + ret_len = lttng_live_recv(viewer_connection, &resp, sizeof(resp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving create session reply: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(resp)); + + if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) { + BT_LOGE("Error creating viewer session"); + goto error; + } + if (lttng_live_query_session_ids(lttng_live_msg_iter)) { + goto error; + } + + return 0; + +error: + return -1; +} + +static +int receive_streams(struct lttng_live_session *session, + uint32_t stream_count) +{ + ssize_t ret_len; + uint32_t i; + struct lttng_live_msg_iter *lttng_live_msg_iter = + session->lttng_live_msg_iter; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + + BT_LOGD("Getting %" PRIu32 " new streams:", stream_count); + for (i = 0; i < stream_count; i++) { + struct lttng_viewer_stream stream; + struct lttng_live_stream_iterator *live_stream; + uint64_t stream_id; + uint64_t ctf_trace_id; + + ret_len = lttng_live_recv(viewer_connection, &stream, sizeof(stream)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving stream"); + goto error; + } + BT_ASSERT(ret_len == sizeof(stream)); + stream.path_name[LTTNG_VIEWER_PATH_MAX - 1] = '\0'; + stream.channel_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0'; + stream_id = be64toh(stream.id); + ctf_trace_id = be64toh(stream.ctf_trace_id); + + if (stream.metadata_flag) { + BT_LOGD(" metadata stream %" PRIu64 " : %s/%s", + stream_id, stream.path_name, + stream.channel_name); + if (lttng_live_metadata_create_stream(session, + ctf_trace_id, stream_id, + stream.path_name)) { + BT_LOGE("Error creating metadata stream"); + + goto error; + } + session->lazy_stream_msg_init = true; + } else { + BT_LOGD(" stream %" PRIu64 " : %s/%s", + stream_id, stream.path_name, + stream.channel_name); + live_stream = lttng_live_stream_iterator_create(session, + ctf_trace_id, stream_id); + if (!live_stream) { + BT_LOGE("Error creating streamn"); + goto error; + } + } + } + return 0; + +error: + return -1; +} + +BT_HIDDEN +int lttng_live_attach_session(struct lttng_live_session *session) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_attach_session_request rq; + struct lttng_viewer_attach_session_response rp; + ssize_t ret_len; + struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + uint64_t session_id = session->id; + uint32_t streams_count; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + + cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.session_id = htobe64(session_id); + // TODO: add cmd line parameter to select seek beginning + // rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING); + rq.seek = htobe32(LTTNG_VIEWER_SEEK_LAST); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending attach request: %s", bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving attach response: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(rp)); + + streams_count = be32toh(rp.streams_count); + switch(be32toh(rp.status)) { + case LTTNG_VIEWER_ATTACH_OK: + break; + case LTTNG_VIEWER_ATTACH_UNK: + BT_LOGW("Session id %" PRIu64 " is unknown", session_id); + goto error; + case LTTNG_VIEWER_ATTACH_ALREADY: + BT_LOGW("There is already a viewer attached to this session"); + goto error; + case LTTNG_VIEWER_ATTACH_NOT_LIVE: + BT_LOGW("Not a live session"); + goto error; + case LTTNG_VIEWER_ATTACH_SEEK_ERR: + BT_LOGE("Wrong seek parameter"); + goto error; + default: + BT_LOGE("Unknown attach return code %u", be32toh(rp.status)); + goto error; + } + + /* We receive the initial list of streams. */ + if (receive_streams(session, streams_count)) { + goto error; + } + + session->attached = true; + session->new_streams_needed = false; + + return 0; + +error: + return -1; +} + +BT_HIDDEN +int lttng_live_detach_session(struct lttng_live_session *session) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_detach_session_request rq; + struct lttng_viewer_detach_session_response rp; + ssize_t ret_len; + struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + uint64_t session_id = session->id; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + + if (!session->attached) { + return 0; + } + + cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.session_id = htobe64(session_id); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending detach request: %s", bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving detach response: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(rp)); + + switch(be32toh(rp.status)) { + case LTTNG_VIEWER_DETACH_SESSION_OK: + break; + case LTTNG_VIEWER_DETACH_SESSION_UNK: + BT_LOGW("Session id %" PRIu64 " is unknown", session_id); + goto error; + case LTTNG_VIEWER_DETACH_SESSION_ERR: + BT_LOGW("Error detaching session id %" PRIu64 "", session_id); + goto error; + default: + BT_LOGE("Unknown detach return code %u", be32toh(rp.status)); + goto error; + } + + session->attached = false; + + return 0; + +error: + return -1; +} + +BT_HIDDEN +ssize_t lttng_live_get_one_metadata_packet(struct lttng_live_trace *trace, + FILE *fp) +{ + uint64_t len = 0; + int ret; + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_metadata rq; + struct lttng_viewer_metadata_packet rp; + char *data = NULL; + ssize_t ret_len; + struct lttng_live_session *session = trace->session; + struct lttng_live_msg_iter *lttng_live_msg_iter = session->lttng_live_msg_iter; + struct lttng_live_metadata *metadata = trace->metadata; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + + rq.stream_id = htobe64(metadata->stream_id); + cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending get_metadata request: %s", bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving get_metadata response: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(rp)); + + switch (be32toh(rp.status)) { + case LTTNG_VIEWER_METADATA_OK: + BT_LOGD("get_metadata : OK"); + break; + case LTTNG_VIEWER_NO_NEW_METADATA: + BT_LOGD("get_metadata : NO NEW"); + ret = 0; + goto end; + case LTTNG_VIEWER_METADATA_ERR: + BT_LOGD("get_metadata : ERR"); + goto error; + default: + BT_LOGD("get_metadata : UNKNOWN"); + goto error; + } + + len = be64toh(rp.len); + BT_LOGD("Writing %" PRIu64" bytes to metadata", len); + if (len <= 0) { + goto error; + } + + data = zmalloc(len); + if (!data) { + BT_LOGE("relay data zmalloc: %s", strerror(errno)); + goto error; + } + ret_len = lttng_live_recv(viewer_connection, data, len); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error_free_data; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving trace packet: %s", bt_socket_errormsg()); + goto error_free_data; + } + BT_ASSERT(ret_len == len); + + do { + ret_len = fwrite(data, 1, len, fp); + } while (ret_len < 0 && errno == EINTR); + if (ret_len < 0) { + BT_LOGE("Writing in the metadata fp"); + goto error_free_data; + } + BT_ASSERT(ret_len == len); + free(data); + ret = len; +end: + return ret; + +error_free_data: + free(data); +error: + return -1; +} + +/* + * Assign the fields from a lttng_viewer_index to a packet_index. + */ +static +void lttng_index_to_packet_index(struct lttng_viewer_index *lindex, + struct packet_index *pindex) +{ + BT_ASSERT(lindex); + BT_ASSERT(pindex); + + pindex->offset = be64toh(lindex->offset); + pindex->packet_size = be64toh(lindex->packet_size); + pindex->content_size = be64toh(lindex->content_size); + pindex->ts_cycles.timestamp_begin = be64toh(lindex->timestamp_begin); + pindex->ts_cycles.timestamp_end = be64toh(lindex->timestamp_end); + pindex->events_discarded = be64toh(lindex->events_discarded); +} + +BT_HIDDEN +enum lttng_live_iterator_status lttng_live_get_next_index( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream, + struct packet_index *index) +{ + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_next_index rq; + ssize_t ret_len; + struct lttng_viewer_index rp; + uint32_t flags, status; + enum lttng_live_iterator_status retstatus = + LTTNG_LIVE_ITERATOR_STATUS_OK; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + struct lttng_live_trace *trace = stream->trace; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + struct lttng_live_component *lttng_live = + lttng_live_msg_iter->lttng_live_comp; + + cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + + memset(&rq, 0, sizeof(rq)); + rq.stream_id = htobe64(stream->viewer_stream_id); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending get_next_index request: %s", + bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving get_next_index response: %s", + bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == sizeof(rp)); + + flags = be32toh(rp.flags); + status = be32toh(rp.status); + + switch (status) { + case LTTNG_VIEWER_INDEX_INACTIVE: + { + uint64_t ctf_stream_class_id; + + BT_LOGD("get_next_index: inactive"); + memset(index, 0, sizeof(struct packet_index)); + index->ts_cycles.timestamp_end = be64toh(rp.timestamp_end); + stream->current_inactivity_ts = index->ts_cycles.timestamp_end; + ctf_stream_class_id = be64toh(rp.stream_id); + if (stream->ctf_stream_class_id != -1ULL) { + BT_ASSERT(stream->ctf_stream_class_id == + ctf_stream_class_id); + } else { + stream->ctf_stream_class_id = ctf_stream_class_id; + } + stream->state = LTTNG_LIVE_STREAM_QUIESCENT; + break; + } + case LTTNG_VIEWER_INDEX_OK: + { + uint64_t ctf_stream_class_id; + + BT_LOGD("get_next_index: OK"); + lttng_index_to_packet_index(&rp, index); + ctf_stream_class_id = be64toh(rp.stream_id); + if (stream->ctf_stream_class_id != -1ULL) { + BT_ASSERT(stream->ctf_stream_class_id == + ctf_stream_class_id); + } else { + stream->ctf_stream_class_id = ctf_stream_class_id; + } + + stream->state = LTTNG_LIVE_STREAM_ACTIVE_DATA; + + if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { + BT_LOGD("get_next_index: new metadata needed"); + trace->new_metadata_needed = true; + } + if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) { + BT_LOGD("get_next_index: new streams needed"); + lttng_live_need_new_streams(lttng_live_msg_iter); + } + break; + } + case LTTNG_VIEWER_INDEX_RETRY: + BT_LOGD("get_next_index: retry"); + memset(index, 0, sizeof(struct packet_index)); + retstatus = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; + goto end; + case LTTNG_VIEWER_INDEX_HUP: + BT_LOGD("get_next_index: stream hung up"); + memset(index, 0, sizeof(struct packet_index)); + index->offset = EOF; + retstatus = LTTNG_LIVE_ITERATOR_STATUS_END; + stream->state = LTTNG_LIVE_STREAM_EOF; + break; + case LTTNG_VIEWER_INDEX_ERR: + BT_LOGE("get_next_index: error"); + memset(index, 0, sizeof(struct packet_index)); + stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; + goto error; + default: + BT_LOGE("get_next_index: unknown value"); + memset(index, 0, sizeof(struct packet_index)); + stream->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA; + goto error; + } +end: + return retstatus; + +error: + if (lttng_live_graph_is_canceled(lttng_live)) { + retstatus = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + } else { + retstatus = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + } + return retstatus; +} + +BT_HIDDEN +enum bt_msg_iter_medium_status lttng_live_get_stream_bytes( + struct lttng_live_msg_iter *lttng_live_msg_iter, + struct lttng_live_stream_iterator *stream, uint8_t *buf, + uint64_t offset, uint64_t req_len, uint64_t *recv_len) +{ + enum bt_msg_iter_medium_status retstatus = BT_MSG_ITER_MEDIUM_STATUS_OK; + struct lttng_viewer_cmd cmd; + struct lttng_viewer_get_packet rq; + struct lttng_viewer_trace_packet rp; + ssize_t ret_len; + uint32_t flags, status; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + struct lttng_live_trace *trace = stream->trace; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + struct lttng_live_component *lttng_live = + lttng_live_msg_iter->lttng_live_comp; + + BT_LOGD("lttng_live_get_stream_bytes: offset=%" PRIu64 ", req_len=%" PRIu64, + offset, req_len); + cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.stream_id = htobe64(stream->viewer_stream_id); + rq.offset = htobe64(offset); + rq.len = htobe32(req_len); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending get_data request: %s", bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving get_data response: %s", bt_socket_errormsg()); + goto error; + } + if (ret_len != sizeof(rp)) { + BT_LOGE("get_data_packet: expected %zu" + ", received %zd", sizeof(rp), + ret_len); + goto error; + } + + flags = be32toh(rp.flags); + status = be32toh(rp.status); + + switch (status) { + case LTTNG_VIEWER_GET_PACKET_OK: + req_len = be32toh(rp.len); + BT_LOGD("get_data_packet: Ok, packet size : %" PRIu64 "", req_len); + break; + case LTTNG_VIEWER_GET_PACKET_RETRY: + /* Unimplemented by relay daemon */ + BT_LOGD("get_data_packet: retry"); + retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; + goto end; + case LTTNG_VIEWER_GET_PACKET_ERR: + if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) { + BT_LOGD("get_data_packet: new metadata needed, try again later"); + trace->new_metadata_needed = true; + } + if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) { + BT_LOGD("get_data_packet: new streams needed, try again later"); + lttng_live_need_new_streams(lttng_live_msg_iter); + } + if (flags & (LTTNG_VIEWER_FLAG_NEW_METADATA + | LTTNG_VIEWER_FLAG_NEW_STREAM)) { + retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; + goto end; + } + BT_LOGE("get_data_packet: error"); + goto error; + case LTTNG_VIEWER_GET_PACKET_EOF: + retstatus = BT_MSG_ITER_MEDIUM_STATUS_EOF; + goto end; + default: + BT_LOGE("get_data_packet: unknown"); + goto error; + } + + if (req_len == 0) { + goto error; + } + + ret_len = lttng_live_recv(viewer_connection, buf, req_len); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving trace packet: %s", bt_socket_errormsg()); + goto error; + } + BT_ASSERT(ret_len == req_len); + *recv_len = ret_len; +end: + return retstatus; + +error: + if (lttng_live_graph_is_canceled(lttng_live)) { + retstatus = BT_MSG_ITER_MEDIUM_STATUS_AGAIN; + } else { + retstatus = BT_MSG_ITER_MEDIUM_STATUS_ERROR; + } + return retstatus; +} + +/* + * Request new streams for a session. + */ +BT_HIDDEN +enum lttng_live_iterator_status lttng_live_get_new_streams( + struct lttng_live_session *session) +{ + enum lttng_live_iterator_status status = + LTTNG_LIVE_ITERATOR_STATUS_OK; + struct lttng_viewer_cmd cmd; + struct lttng_viewer_new_streams_request rq; + struct lttng_viewer_new_streams_response rp; + ssize_t ret_len; + struct lttng_live_msg_iter *lttng_live_msg_iter = + session->lttng_live_msg_iter; + struct live_viewer_connection *viewer_connection = + lttng_live_msg_iter->viewer_connection; + struct lttng_live_component *lttng_live = + lttng_live_msg_iter->lttng_live_comp; + uint32_t streams_count; + const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq); + char cmd_buf[cmd_buf_len]; + + if (!session->new_streams_needed) { + return LTTNG_LIVE_ITERATOR_STATUS_OK; + } + + cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS); + cmd.data_size = htobe64((uint64_t) sizeof(rq)); + cmd.cmd_version = htobe32(0); + + memset(&rq, 0, sizeof(rq)); + rq.session_id = htobe64(session->id); + + /* + * Merge the cmd and connection request to prevent a write-write + * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the + * second write to be performed quickly in presence of Nagle's algorithm. + */ + memcpy(cmd_buf, &cmd, sizeof(cmd)); + memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq)); + ret_len = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len); + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error sending get_new_streams request: %s", + bt_socket_errormsg()); + goto error; + } + + BT_ASSERT(ret_len == cmd_buf_len); + ret_len = lttng_live_recv(viewer_connection, &rp, sizeof(rp)); + if (ret_len == 0) { + BT_LOGI("Remote side has closed connection"); + goto error; + } + if (ret_len == BT_SOCKET_ERROR) { + BT_LOGE("Error receiving get_new_streams response"); + goto error; + } + BT_ASSERT(ret_len == sizeof(rp)); + + streams_count = be32toh(rp.streams_count); + + switch(be32toh(rp.status)) { + case LTTNG_VIEWER_NEW_STREAMS_OK: + session->new_streams_needed = false; + break; + case LTTNG_VIEWER_NEW_STREAMS_NO_NEW: + session->new_streams_needed = false; + goto end; + case LTTNG_VIEWER_NEW_STREAMS_HUP: + session->new_streams_needed = false; + session->closed = true; + status = LTTNG_LIVE_ITERATOR_STATUS_END; + goto end; + case LTTNG_VIEWER_NEW_STREAMS_ERR: + BT_LOGE("get_new_streams error"); + goto error; + default: + BT_LOGE("Unknown return code %u", be32toh(rp.status)); + goto error; + } + + if (receive_streams(session, streams_count)) { + goto error; + } +end: + return status; + +error: + if (lttng_live_graph_is_canceled(lttng_live)) { + status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN; + } else { + status = LTTNG_LIVE_ITERATOR_STATUS_ERROR; + } + return status; +} + +BT_HIDDEN +struct live_viewer_connection *live_viewer_connection_create( + const char *url, bool in_query, + struct lttng_live_msg_iter *lttng_live_msg_iter) +{ + struct live_viewer_connection *viewer_connection; + + viewer_connection = g_new0(struct live_viewer_connection, 1); + + if (bt_socket_init() != 0) { + goto error; + } + + bt_object_init_shared(&viewer_connection->obj, connection_release); + viewer_connection->control_sock = BT_INVALID_SOCKET; + viewer_connection->port = -1; + viewer_connection->in_query = in_query; + viewer_connection->lttng_live_msg_iter = lttng_live_msg_iter; + viewer_connection->url = g_string_new(url); + if (!viewer_connection->url) { + goto error; + } + + BT_LOGD("Establishing connection to url \"%s\"...", url); + if (lttng_live_connect_viewer(viewer_connection)) { + goto error_report; + } + BT_LOGD("Connection to url \"%s\" is established", url); + return viewer_connection; + +error_report: + BT_LOGW("Failure to establish connection to url \"%s\"", url); +error: + g_free(viewer_connection); + return NULL; +} + +BT_HIDDEN +void live_viewer_connection_destroy( + struct live_viewer_connection *viewer_connection) +{ + BT_LOGD("Closing connection to url \"%s\"", viewer_connection->url->str); + lttng_live_disconnect_viewer(viewer_connection); + g_string_free(viewer_connection->url, true); + if (viewer_connection->relay_hostname) { + g_string_free(viewer_connection->relay_hostname, true); + } + if (viewer_connection->target_hostname) { + g_string_free(viewer_connection->target_hostname, true); + } + if (viewer_connection->session_name) { + g_string_free(viewer_connection->session_name, true); + } + g_free(viewer_connection); + + bt_socket_fini(); +} diff --git a/src/plugins/ctf/lttng-live/viewer-connection.h b/src/plugins/ctf/lttng-live/viewer-connection.h new file mode 100644 index 00000000..09adc6cf --- /dev/null +++ b/src/plugins/ctf/lttng-live/viewer-connection.h @@ -0,0 +1,92 @@ +#ifndef LTTNG_LIVE_VIEWER_CONNECTION_H +#define LTTNG_LIVE_VIEWER_CONNECTION_H + +/* + * Copyright 2016 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "common/babeltrace.h" +#include "compat/socket.h" + +//TODO: this should not be used by plugins. Should copy code into plugin +//instead. +#include "lib/object.h" + +#define LTTNG_DEFAULT_NETWORK_VIEWER_PORT 5344 + +#define LTTNG_LIVE_MAJOR 2 +#define LTTNG_LIVE_MINOR 4 + +struct lttng_live_component; + +struct live_viewer_connection { + bt_object obj; + + GString *url; + + GString *relay_hostname; + GString *target_hostname; + GString *session_name; + + BT_SOCKET control_sock; + int port; + + int32_t major; + int32_t minor; + + bool in_query; + struct lttng_live_msg_iter *lttng_live_msg_iter; +}; + +struct packet_index_time { + uint64_t timestamp_begin; + uint64_t timestamp_end; +}; + +struct packet_index { + off_t offset; /* offset of the packet in the file, in bytes */ + int64_t data_offset; /* offset of data within the packet, in bits */ + uint64_t packet_size; /* packet size, in bits */ + uint64_t content_size; /* content size, in bits */ + uint64_t events_discarded; + uint64_t events_discarded_len; /* length of the field, in bits */ + struct packet_index_time ts_cycles; /* timestamp in cycles */ + struct packet_index_time ts_real; /* realtime timestamp */ + /* CTF_INDEX 1.0 limit */ + uint64_t stream_instance_id; /* ID of the channel instance */ + uint64_t packet_seq_num; /* packet sequence number */ +}; + +struct live_viewer_connection * live_viewer_connection_create( + const char *url, bool in_query, + struct lttng_live_msg_iter *lttng_live_msg_iter); + +void live_viewer_connection_destroy( + struct live_viewer_connection *conn); + +bt_query_status live_viewer_connection_list_sessions( + struct live_viewer_connection *viewer_connection, + const bt_value **user_result); + +#endif /* LTTNG_LIVE_VIEWER_CONNECTION_H */ diff --git a/src/plugins/ctf/plugin.c b/src/plugins/ctf/plugin.c new file mode 100644 index 00000000..95974030 --- /dev/null +++ b/src/plugins/ctf/plugin.c @@ -0,0 +1,81 @@ +/* + * plugin.c + * + * Babeltrace CTF Plug-in Registration Symbols + * + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "fs-src/fs.h" +#include "fs-sink/fs-sink.h" +#include "lttng-live/lttng-live.h" + +#ifndef BT_BUILT_IN_PLUGINS +BT_PLUGIN_MODULE(); +#endif + +/* Initialize plug-in description. */ +BT_PLUGIN(ctf); +BT_PLUGIN_DESCRIPTION("CTF source and sink support"); +BT_PLUGIN_AUTHOR("Julien Desfossez, Mathieu Desnoyers, Jérémie Galarneau, Philippe Proulx"); +BT_PLUGIN_LICENSE("MIT"); + +/* ctf.fs source */ +BT_PLUGIN_SOURCE_COMPONENT_CLASS(fs, ctf_fs_iterator_next); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(fs, + "Read CTF traces from the file system."); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD(fs, ctf_fs_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD(fs, ctf_fs_query); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_finalize); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(fs, + ctf_fs_iterator_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(fs, + ctf_fs_iterator_finalize); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(fs, + ctf_fs_iterator_seek_beginning); + +/* ctf.fs sink */ +BT_PLUGIN_SINK_COMPONENT_CLASS(fs, ctf_fs_sink_consume); +BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(fs, ctf_fs_sink_init); +BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_sink_finalize); +BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(fs, + ctf_fs_sink_graph_is_configured); +BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(fs, "Write CTF traces to the file system."); + +/* ctf.lttng-live source */ +BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, lttng_live, "lttng-live", + lttng_live_msg_iter_next); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION_WITH_ID(auto, lttng_live, + "Connect to an LTTng relay daemon and receive CTF streams."); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD_WITH_ID(auto, lttng_live, + lttng_live_component_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(auto, lttng_live, + lttng_live_query); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(auto, lttng_live, + lttng_live_component_finalize); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD_WITH_ID(auto, + lttng_live, lttng_live_msg_iter_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD_WITH_ID(auto, + lttng_live, lttng_live_msg_iter_finalize); diff --git a/src/plugins/ctf/print.h b/src/plugins/ctf/print.h new file mode 100644 index 00000000..eab99d77 --- /dev/null +++ b/src/plugins/ctf/print.h @@ -0,0 +1,58 @@ +#ifndef CTF_PRINT_H +#define CTF_PRINT_H + +/* + * Define PRINT_PREFIX, PRINT_DBG_CHECK, and PRINT_ERR_STREAM, then + * include this file. + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#define PERR(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "[error " PRINT_PREFIX "] " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PWARN(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "[warning " PRINT_PREFIX "] " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PDBG(fmt, ...) \ + do { \ + if (PRINT_DBG_CHECK) { \ + fprintf(stderr, \ + "[debug " PRINT_PREFIX "] " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* CTF_PRINT_H */ diff --git a/src/plugins/lttng-utils/Makefile.am b/src/plugins/lttng-utils/Makefile.am new file mode 100644 index 00000000..d7d980ae --- /dev/null +++ b/src/plugins/lttng-utils/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = + +babeltrace_plugin_lttng_utils_la_LIBADD = + +if ENABLE_DEBUG_INFO +SUBDIRS += debug-info +babeltrace_plugin_lttng_utils_la_LIBADD += \ + debug-info/libdebug-info.la +endif + +plugindir = "$(PLUGINSDIR)" +plugin_LTLIBRARIES = babeltrace-plugin-lttng-utils.la + +babeltrace_plugin_lttng_utils_la_SOURCES = \ + plugin.c + +babeltrace_plugin_lttng_utils_la_LDFLAGS = \ + $(LT_NO_UNDEFINED) \ + -avoid-version -module \ + $(ELFUTILS_LIBS) + +if !ENABLE_BUILT_IN_PLUGINS +babeltrace_plugin_lttng_utils_la_LIBADD += \ + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la +endif diff --git a/src/plugins/lttng-utils/debug-info/Makefile.am b/src/plugins/lttng-utils/debug-info/Makefile.am new file mode 100644 index 00000000..a231785b --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libdebug-info.la + +libdebug_info_la_LIBADD = \ + $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la + +libdebug_info_la_SOURCES = \ + bin-info.c \ + bin-info.h \ + crc32.c \ + crc32.h \ + debug-info.c \ + debug-info.h \ + dwarf.c \ + dwarf.h \ + logging.c \ + logging.h \ + trace-ir-data-copy.c \ + trace-ir-data-copy.h \ + trace-ir-mapping.c \ + trace-ir-mapping.h \ + trace-ir-metadata-copy.c \ + trace-ir-metadata-copy.h \ + trace-ir-metadata-field-class-copy.c \ + trace-ir-metadata-field-class-copy.h \ + utils.c \ + utils.h diff --git a/src/plugins/lttng-utils/debug-info/bin-info.c b/src/plugins/lttng-utils/debug-info/bin-info.c new file mode 100644 index 00000000..2ecffdf8 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/bin-info.c @@ -0,0 +1,1583 @@ +/* + * bin-info.c + * + * Babeltrace - Executable and Shared Object Debug Info Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-UTILS-DEBUG-INFO-FLT-BIN-INFO" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/common.h" + +#include "bin-info.h" +#include "crc32.h" +#include "dwarf.h" +#include "utils.h" + +/* + * An address printed in hex is at most 20 bytes (16 for 64-bits + + * leading 0x + optional leading '+' if addr is an offset + null + * character). + */ +#define ADDR_STR_LEN 20 +#define BUILD_ID_NOTE_NAME "GNU" + +BT_HIDDEN +int bin_info_init(void) +{ + int ret = 0; + + if (elf_version(EV_CURRENT) == EV_NONE) { + BT_LOGD("ELF library initialization failed: %s.", + elf_errmsg(-1)); + ret = -1; + } + + return ret; +} + +BT_HIDDEN +struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path, + uint64_t low_addr, uint64_t memsz, bool is_pic, + const char *debug_info_dir, const char *target_prefix) +{ + struct bin_info *bin = NULL; + + BT_ASSERT(fdc); + + if (!path) { + goto error; + } + + bin = g_new0(struct bin_info, 1); + if (!bin) { + goto error; + } + + if (target_prefix) { + bin->elf_path = g_build_filename(target_prefix, path, NULL); + } else { + bin->elf_path = g_strdup(path); + } + + if (!bin->elf_path) { + goto error; + } + + if (debug_info_dir) { + bin->debug_info_dir = g_strdup(debug_info_dir); + if (!bin->debug_info_dir) { + goto error; + } + } + + bin->is_pic = is_pic; + bin->memsz = memsz; + bin->low_addr = low_addr; + bin->high_addr = bin->low_addr + bin->memsz; + bin->build_id = NULL; + bin->build_id_len = 0; + bin->file_build_id_matches = false; + bin->fd_cache = fdc; + + return bin; + +error: + bin_info_destroy(bin); + return NULL; +} + +BT_HIDDEN +void bin_info_destroy(struct bin_info *bin) +{ + if (!bin) { + return; + } + + dwarf_end(bin->dwarf_info); + + g_free(bin->debug_info_dir); + g_free(bin->elf_path); + g_free(bin->dwarf_path); + g_free(bin->build_id); + g_free(bin->dbg_link_filename); + + elf_end(bin->elf_file); + + bt_fd_cache_put_handle(bin->fd_cache, bin->elf_handle); + bt_fd_cache_put_handle(bin->fd_cache, bin->dwarf_handle); + + g_free(bin); +} + +/** + * Initialize the ELF file for a given executable. + * + * @param bin bin_info instance + * @returns 0 on success, negative value on error. + */ +static +int bin_info_set_elf_file(struct bin_info *bin) +{ + struct bt_fd_cache_handle *elf_handle = NULL; + Elf *elf_file = NULL; + + if (!bin) { + goto error; + } + + elf_handle = bt_fd_cache_get_handle(bin->fd_cache, bin->elf_path); + if (!elf_handle) { + BT_LOGD("Failed to open %s", bin->elf_path); + goto error; + } + bin->elf_handle = elf_handle; + + elf_file = elf_begin(bt_fd_cache_handle_get_fd(bin->elf_handle), + ELF_C_READ, NULL); + if (!elf_file) { + BT_LOGE("elf_begin failed: %s", elf_errmsg(-1)); + goto error; + } + + bin->elf_file = elf_file; + + if (elf_kind(elf_file) != ELF_K_ELF) { + BT_LOGE("Error: %s is not an ELF object", bin->elf_path); + goto error; + } + + return 0; + +error: + bt_fd_cache_put_handle(bin->fd_cache, elf_handle); + elf_end(elf_file); + return -1; +} + +/** + * From a note section data struct, check if it is a build id note. + * + * @param note_data Pointer to a note section + * + * @returns 1 on match, 0 if `buf` does not contain a + * valid build id note + */ +static +int is_build_id_note_section(Elf_Data *note_data) +{ + size_t name_offset, desc_offset; + GElf_Nhdr note_header; + int ret = 0; + + /* + * Discard the return value as it contains the size of the note section + * and we don't need it. + */ + (void) gelf_getnote(note_data, 0, ¬e_header, &name_offset, + &desc_offset); + + /* + * Check the note name length. The name_sz field includes the + * terminating null byte. + */ + if (note_header.n_namesz != sizeof(BUILD_ID_NOTE_NAME)) { + goto invalid; + } + + /* Check the note type. */ + if (note_header.n_type != NT_GNU_BUILD_ID) { + goto invalid; + } + + /* Check the note name. */ + if (memcmp(note_data->d_buf + name_offset, BUILD_ID_NOTE_NAME, + note_header.n_namesz) != 0) { + goto invalid; + } + + ret = 1; + +invalid: + return ret; +} + +/** + * From a build id note section data struct, check if the build id it contains + * is identical to the build id passed as parameter. + * + * @param note_data Pointer to the file build id note section. + * @param build_id Pointer to a build id to compare to. + * @param build_id_len length of the build id. + * + * @returns 1 on match, 0 otherwise. + */ +static +int is_build_id_note_section_matching(Elf_Data *note_data, + uint8_t *build_id, size_t build_id_len) +{ + size_t name_offset, desc_offset; + GElf_Nhdr note_header; + + if (build_id_len <= 0) { + goto end; + } + + /* + * Discard the return value as it contains the size of the note section + * and we don't need it. + */ + (void) gelf_getnote(note_data, 0, ¬e_header, &name_offset, + &desc_offset); + + /* + * Compare the binary build id with the supplied build id. + */ + if (memcmp(build_id, note_data->d_buf + desc_offset, + build_id_len) == 0) { + return 1; + } +end: + return 0; +} + +/** + * Checks if the build id stored in `bin` (bin->build_id) is matching the build + * id of the ondisk file (bin->elf_file). + * + * @param bin bin_info instance + * @param build_id build id to compare ot the on disk file + * @param build_id_len length of the build id + * + * @returns 1 on if the build id of stored in `bin` matches + * the build id of the ondisk file. + * 0 on if they are different or an error occured. + */ +static +int is_build_id_matching(struct bin_info *bin) +{ + int ret, is_build_id, is_matching = 0; + Elf_Scn *curr_section = NULL, *next_section = NULL; + GElf_Shdr curr_section_hdr; + + if (!bin->build_id) { + goto error; + } + + /* Set ELF file if it hasn't been accessed yet. */ + if (!bin->elf_file) { + ret = bin_info_set_elf_file(bin); + if (ret) { + /* Failed to set ELF file. */ + goto error; + } + } + + next_section = elf_nextscn(bin->elf_file, curr_section); + if (!next_section) { + goto error; + } + + while (next_section) { + Elf_Data *note_data = NULL; + + curr_section = next_section; + next_section = elf_nextscn(bin->elf_file, curr_section); + + if (!gelf_getshdr(curr_section, &curr_section_hdr)) { + goto error; + } + + if (curr_section_hdr.sh_type != SHT_NOTE) { + continue; + } + + /* + * elf_getdata() translates the data to native byte order. + */ + note_data = elf_getdata(curr_section, NULL); + if (!note_data) { + goto error; + } + + /* Check if the note is of the build-id type. */ + is_build_id = is_build_id_note_section(note_data); + if (!is_build_id) { + continue; + } + + /* + * Compare the build id of the on-disk file and + * the build id recorded in the trace. + */ + is_matching = is_build_id_note_section_matching( + note_data, bin->build_id, bin->build_id_len); + if (!is_matching) { + break; + } + } +error: + return is_matching; +} + +BT_HIDDEN +int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id, + size_t build_id_len) +{ + if (!bin || !build_id) { + goto error; + } + + /* Set the build id. */ + bin->build_id = g_new0(uint8_t, build_id_len); + if (!bin->build_id) { + goto error; + } + + memcpy(bin->build_id, build_id, build_id_len); + bin->build_id_len = build_id_len; + + /* + * Check if the file found on the file system has the same build id + * that what was recorded in the trace. + */ + bin->file_build_id_matches = is_build_id_matching(bin); + if (!bin->file_build_id_matches) { + BT_LOGD_STR("Supplied Build ID does not match Build ID of the " + "binary or library found on the file system."); + goto error; + } + + /* + * Reset the is_elf_only flag in case it had been set + * previously, because we might find separate debug info using + * the new build id information. + */ + bin->is_elf_only = false; + + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bin_info_set_debug_link(struct bin_info *bin, const char *filename, + uint32_t crc) +{ + if (!bin || !filename) { + goto error; + } + + bin->dbg_link_filename = g_strdup(filename); + if (!bin->dbg_link_filename) { + goto error; + } + + bin->dbg_link_crc = crc; + + /* + * Reset the is_elf_only flag in case it had been set + * previously, because we might find separate debug info using + * the new build id information. + */ + bin->is_elf_only = false; + + return 0; + +error: + + return -1; +} + +/** + * Tries to read DWARF info from the location given by path, and + * attach it to the given bin_info instance if it exists. + * + * @param bin bin_info instance for which to set DWARF info + * @param path Presumed location of the DWARF info + * @returns 0 on success, negative value on failure + */ +static +int bin_info_set_dwarf_info_from_path(struct bin_info *bin, char *path) +{ + int ret = 0; + struct bt_fd_cache_handle *dwarf_handle = NULL; + struct bt_dwarf_cu *cu = NULL; + Dwarf *dwarf_info = NULL; + + if (!bin || !path) { + goto error; + } + + dwarf_handle = bt_fd_cache_get_handle(bin->fd_cache, path); + if (!dwarf_handle) { + goto error; + } + + dwarf_info = dwarf_begin(bt_fd_cache_handle_get_fd(dwarf_handle), + DWARF_C_READ); + if (!dwarf_info) { + goto error; + } + + /* + * Check if the dwarf info has any CU. If not, the + * executable's object file contains no DWARF info. + */ + cu = bt_dwarf_cu_create(dwarf_info); + if (!cu) { + goto error; + } + + ret = bt_dwarf_cu_next(cu); + if (ret) { + goto error; + } + + bin->dwarf_handle = dwarf_handle; + bin->dwarf_path = g_strdup(path); + if (!bin->dwarf_path) { + goto error; + } + bin->dwarf_info = dwarf_info; + free(cu); + + return 0; + +error: + bt_fd_cache_put_handle(bin->fd_cache, dwarf_handle); + dwarf_end(dwarf_info); + g_free(dwarf_info); + free(cu); + + return -1; +} + +/** + * Try to set the dwarf_info for a given bin_info instance via the + * build ID method. + * + * @param bin bin_info instance for which to retrieve the + * DWARF info via build ID + * @returns 0 on success (i.e. dwarf_info set), -1 on failure + */ +static +int bin_info_set_dwarf_info_build_id(struct bin_info *bin) +{ + int i = 0, ret = 0; + char *path = NULL, *build_id_prefix_dir = NULL, *build_id_file = NULL; + const char *dbg_dir = NULL; + size_t build_id_char_len, build_id_suffix_char_len, build_id_file_len; + + if (!bin || !bin->build_id) { + goto error; + } + + dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR; + + /* + * The prefix dir is the first byte of the build id, represented in + * lowercase hex as two characters per byte, +1 for '\0'. + */ + build_id_prefix_dir = g_new0(gchar, BUILD_ID_PREFIX_DIR_LEN + 1); + if (!build_id_prefix_dir) { + goto error; + } + g_snprintf(build_id_prefix_dir, BUILD_ID_PREFIX_DIR_LEN + 1, "%02x", bin->build_id[0]); + + /* + * The build id file is the remaining bytes of the build id, + * represented in lowercase hex, as two characters per byte. + */ + build_id_char_len = (2 * (bin->build_id_len - 1)); + + /* To which the build id suffix is added, +1 for '\0'. */ + build_id_suffix_char_len = strlen(BUILD_ID_SUFFIX) + 1; + + /* + * The resulting filename string is the concatenation of the + * hex build id and the suffix. + */ + build_id_file_len = build_id_char_len + build_id_suffix_char_len; + build_id_file = g_new0(gchar, build_id_file_len); + if (!build_id_file) { + goto error; + } + + /* + * For each byte, starting at offset 1, append two characters + * in lowercase hex. + */ + for (i = 1; i < bin->build_id_len; ++i) { + int path_idx = 2 * (i - 1); + + g_snprintf(&build_id_file[path_idx], 3, "%02x", bin->build_id[i]); + } + /* Append the suffix to the generated string, including the '\0'. */ + g_snprintf(&build_id_file[build_id_char_len], build_id_suffix_char_len, + BUILD_ID_SUFFIX); + + path = g_build_filename(dbg_dir, BUILD_ID_SUBDIR, build_id_prefix_dir, build_id_file, NULL); + if (!path) { + goto error; + } + + ret = bin_info_set_dwarf_info_from_path(bin, path); + if (ret) { + goto error; + } + + goto end; + +error: + ret = -1; +end: + g_free(build_id_prefix_dir); + g_free(build_id_file); + g_free(path); + + return ret; +} + +/** + * Tests whether the file located at path exists and has the expected + * checksum. + * + * This predicate is used when looking up separate debug info via the + * GNU debuglink method. The expected crc can be found .gnu_debuglink + * section in the original ELF file, along with the filename for the + * file containing the debug info. + * + * @param path Full path at which to look for the debug file + * @param crc Expected checksum for the debug file + * @returns 1 if the file exists and has the correct checksum, + * 0 otherwise + */ +static +int is_valid_debug_file(struct bin_info *bin, char *path, uint32_t crc) +{ + int ret = 0; + struct bt_fd_cache_handle *debug_handle = NULL; + uint32_t _crc = 0; + + if (!path) { + goto end; + } + + debug_handle = bt_fd_cache_get_handle(bin->fd_cache, path); + if (!debug_handle) { + goto end; + } + + ret = crc32(bt_fd_cache_handle_get_fd(debug_handle), &_crc); + if (ret) { + ret = 0; + goto end; + } + + ret = (crc == _crc); + +end: + bt_fd_cache_put_handle(bin->fd_cache, debug_handle); + return ret; +} + +/** + * Try to set the dwarf_info for a given bin_info instance via the + * debug-link method. + * + * @param bin bin_info instance for which to retrieve the + * DWARF info via debug link + * @returns 0 on success (i.e. dwarf_info set), -1 on failure + */ +static +int bin_info_set_dwarf_info_debug_link(struct bin_info *bin) +{ + int ret = 0; + const gchar *dbg_dir = NULL; + gchar *bin_dir = NULL, *path = NULL; + + if (!bin || !bin->dbg_link_filename) { + goto error; + } + + dbg_dir = bin->debug_info_dir ? bin->debug_info_dir : DEFAULT_DEBUG_DIR; + bin_dir = g_path_get_dirname(bin->elf_path); + + /* First look in the executable's dir */ + path = g_build_filename(bin_dir, bin->dbg_link_filename, NULL); + + if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { + goto found; + } + + /* If not found, look in .debug subdir */ + g_free(path); + path = g_build_filename(bin_dir, DEBUG_SUBDIR, bin->dbg_link_filename, NULL); + + if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { + goto found; + } + + /* Lastly, look under the global debug directory */ + g_free(path); + + path = g_build_filename(dbg_dir, bin_dir, bin->dbg_link_filename, NULL); + if (is_valid_debug_file(bin, path, bin->dbg_link_crc)) { + goto found; + } + +error: + ret = -1; +end: + g_free(bin_dir); + g_free(path); + + return ret; + +found: + ret = bin_info_set_dwarf_info_from_path(bin, path); + if (ret) { + goto error; + } + + goto end; +} + +/** + * Initialize the DWARF info for a given executable. + * + * @param bin bin_info instance + * @returns 0 on success, negative value on failure + */ +static +int bin_info_set_dwarf_info(struct bin_info *bin) +{ + int ret = 0; + + if (!bin) { + ret = -1; + goto end; + } + + /* First try to set the DWARF info from the ELF file */ + ret = bin_info_set_dwarf_info_from_path(bin, bin->elf_path); + if (!ret) { + goto end; + } + + /* + * If that fails, try to find separate debug info via build ID + * and debug link. + */ + ret = bin_info_set_dwarf_info_build_id(bin); + if (!ret) { + goto end; + } + + ret = bin_info_set_dwarf_info_debug_link(bin); + if (!ret) { + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +void source_location_destroy(struct source_location *src_loc) +{ + if (!src_loc) { + return; + } + + free(src_loc->filename); + g_free(src_loc); +} + +/** + * Append a string representation of an address offset to an existing + * string. + * + * On success, the out parameter `result` will contain the base string + * followed by the offset string of the form "+0x1234". On failure, + * `result` remains unchanged. + * + * @param base_str The string to which to append an offset string + * @param low_addr The lower virtual memory address, the base from + * which the offset is computed + * @param high_addr The higher virtual memory address + * @param result Out parameter, the base string followed by the + * offset string + * @returns 0 on success, -1 on failure + */ +static +int bin_info_append_offset_str(const char *base_str, uint64_t low_addr, + uint64_t high_addr, char **result) +{ + uint64_t offset; + char *_result = NULL; + + if (!base_str || !result) { + goto error; + } + + offset = high_addr - low_addr; + + _result = g_strdup_printf("%s+%#0" PRIx64, base_str, offset); + if (!_result) { + goto error; + } + *result = _result; + + return 0; + +error: + free(_result); + return -1; +} + +/** + * Try to find the symbol closest to an address within a given ELF + * section. + * + * Only function symbols are taken into account. The symbol's address + * must precede `addr`. A symbol with a closer address might exist + * after `addr` but is irrelevant because it cannot encompass `addr`. + * + * On success, if found, the out parameters `sym` and `shdr` are + * set. On failure or if none are found, they remain unchanged. + * + * @param scn ELF section in which to look for the address + * @param addr Virtual memory address for which to find the + * nearest function symbol + * @param sym Out parameter, the nearest function symbol + * @param shdr Out parameter, the section header for scn + * @returns 0 on success, -1 on failure + */ +static +int bin_info_get_nearest_symbol_from_section(Elf_Scn *scn, uint64_t addr, + GElf_Sym **sym, GElf_Shdr **shdr) +{ + int i; + size_t symbol_count; + Elf_Data *data = NULL; + GElf_Shdr *_shdr = NULL; + GElf_Sym *nearest_sym = NULL; + + if (!scn || !sym || !shdr) { + goto error; + } + + _shdr = g_new0(GElf_Shdr, 1); + if (!_shdr) { + goto error; + } + + _shdr = gelf_getshdr(scn, _shdr); + if (!_shdr) { + goto error; + } + + if (_shdr->sh_type != SHT_SYMTAB) { + /* + * We are only interested in symbol table (symtab) + * sections, skip this one. + */ + goto end; + } + + data = elf_getdata(scn, NULL); + if (!data) { + goto error; + } + + symbol_count = _shdr->sh_size / _shdr->sh_entsize; + + for (i = 0; i < symbol_count; ++i) { + GElf_Sym *cur_sym = NULL; + + cur_sym = g_new0(GElf_Sym, 1); + if (!cur_sym) { + goto error; + } + cur_sym = gelf_getsym(data, i, cur_sym); + if (!cur_sym) { + goto error; + } + if (GELF_ST_TYPE(cur_sym->st_info) != STT_FUNC) { + /* We're only interested in the functions. */ + g_free(cur_sym); + continue; + } + + if (cur_sym->st_value <= addr && + (!nearest_sym || + cur_sym->st_value > nearest_sym->st_value)) { + g_free(nearest_sym); + nearest_sym = cur_sym; + } else { + g_free(cur_sym); + } + } + +end: + if (nearest_sym) { + *sym = nearest_sym; + *shdr = _shdr; + } else { + g_free(_shdr); + } + + return 0; + +error: + g_free(nearest_sym); + g_free(_shdr); + return -1; +} + +/** + * Get the name of the function containing a given address within an + * executable using ELF symbols. + * + * The function name is in fact the name of the nearest ELF symbol, + * followed by the offset in bytes between the address and the symbol + * (in hex), separated by a '+' character. + * + * If found, the out parameter `func_name` is set on success. On failure, + * it remains unchanged. + * + * @param bin bin_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_elf_function_name(struct bin_info *bin, uint64_t addr, + char **func_name) +{ + /* + * TODO (possible optimisation): if an ELF has no symtab + * section, it has been stripped. Therefore, it would be wise + * to store a flag indicating the stripped status after the + * first iteration to prevent subsequent ones. + */ + int ret = 0; + Elf_Scn *scn = NULL; + GElf_Sym *sym = NULL; + GElf_Shdr *shdr = NULL; + char *sym_name = NULL; + + /* Set ELF file if it hasn't been accessed yet. */ + if (!bin->elf_file) { + ret = bin_info_set_elf_file(bin); + if (ret) { + /* Failed to set ELF file. */ + goto error; + } + } + + scn = elf_nextscn(bin->elf_file, scn); + if (!scn) { + goto error; + } + + while (scn && !sym) { + ret = bin_info_get_nearest_symbol_from_section( + scn, addr, &sym, &shdr); + if (ret) { + goto error; + } + + scn = elf_nextscn(bin->elf_file, scn); + } + + if (sym) { + sym_name = elf_strptr(bin->elf_file, shdr->sh_link, + sym->st_name); + if (!sym_name) { + goto error; + } + + ret = bin_info_append_offset_str(sym_name, sym->st_value, addr, + func_name); + if (ret) { + goto error; + } + } + + g_free(shdr); + g_free(sym); + return 0; + +error: + g_free(shdr); + g_free(sym); + return ret; +} + +/** + * Get the name of the function containing a given address within a + * given compile unit (CU). + * + * If found, the out parameter `func_name` is set on success. On + * failure, it remains unchanged. + * + * @param cu bt_dwarf_cu instance which may contain the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_cu_function_name(struct bt_dwarf_cu *cu, uint64_t addr, + char **func_name) +{ + int ret = 0; + bool found = false; + struct bt_dwarf_die *die = NULL; + + if (!cu || !func_name) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + while (bt_dwarf_die_next(die) == 0) { + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_subprogram) { + ret = bt_dwarf_die_contains_addr(die, addr, &found); + if (ret) { + goto error; + } + + if (found) { + break; + } + } + } + + if (found) { + uint64_t low_addr = 0; + char *die_name = NULL; + + ret = bt_dwarf_die_get_name(die, &die_name); + if (ret) { + goto error; + } + + ret = dwarf_lowpc(die->dwarf_die, &low_addr); + if (ret) { + free(die_name); + goto error; + } + + ret = bin_info_append_offset_str(die_name, low_addr, addr, + func_name); + free(die_name); + if (ret) { + goto error; + } + } + + bt_dwarf_die_destroy(die); + return 0; + +error: + bt_dwarf_die_destroy(die); + return -1; +} + +/** + * Get the name of the function containing a given address within an + * executable using DWARF debug info. + * + * If found, the out parameter `func_name` is set on success. On + * failure, it remains unchanged. + * + * @param bin bin_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_dwarf_function_name(struct bin_info *bin, uint64_t addr, + char **func_name) +{ + int ret = 0; + char *_func_name = NULL; + struct bt_dwarf_cu *cu = NULL; + + if (!bin || !func_name) { + goto error; + } + + cu = bt_dwarf_cu_create(bin->dwarf_info); + if (!cu) { + goto error; + } + + while (bt_dwarf_cu_next(cu) == 0) { + ret = bin_info_lookup_cu_function_name(cu, addr, &_func_name); + if (ret) { + goto error; + } + + if (_func_name) { + break; + } + } + + if (_func_name) { + *func_name = _func_name; + } else { + goto error; + } + + bt_dwarf_cu_destroy(cu); + return 0; + +error: + bt_dwarf_cu_destroy(cu); + return -1; +} + +BT_HIDDEN +int bin_info_lookup_function_name(struct bin_info *bin, + uint64_t addr, char **func_name) +{ + int ret = 0; + char *_func_name = NULL; + + if (!bin || !func_name) { + goto error; + } + + /* + * If the bin_info has a build id but it does not match the build id + * that was found on the file system, return an error. + */ + if (bin->build_id && !bin->file_build_id_matches) { + goto error; + } + + /* Set DWARF info if it hasn't been accessed yet. */ + if (!bin->dwarf_info && !bin->is_elf_only) { + ret = bin_info_set_dwarf_info(bin); + if (ret) { + BT_LOGD_STR("Failed to set bin dwarf info, falling " + "back to ELF lookup."); + /* Failed to set DWARF info, fallback to ELF. */ + bin->is_elf_only = true; + } + } + + if (!bin_info_has_address(bin, addr)) { + goto error; + } + + /* + * Addresses in ELF and DWARF are relative to base address for + * PIC, so make the address argument relative too if needed. + */ + if (bin->is_pic) { + addr -= bin->low_addr; + } + + if (bin->is_elf_only) { + ret = bin_info_lookup_elf_function_name(bin, addr, + &_func_name); + if (ret) { + BT_LOGD("Failed to lookup function name (ELF): " + "ret=%d", ret); + } + } else { + ret = bin_info_lookup_dwarf_function_name(bin, addr, + &_func_name); + if (ret) { + BT_LOGD("Failed to lookup function name (DWARF): " + "ret=%d", ret); + } + } + + *func_name = _func_name; + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc) +{ + gchar *_bin_loc = NULL; + + if (!bin || !bin_loc) { + goto error; + } + + /* + * If the bin_info has a build id but it does not match the build id + * that was found on the file system, return an error. + */ + if (bin->build_id && !bin->file_build_id_matches) { + goto error; + } + + if (bin->is_pic) { + addr -= bin->low_addr; + _bin_loc = g_strdup_printf("+%#0" PRIx64, addr); + } else { + _bin_loc = g_strdup_printf("@%#0" PRIx64, addr); + } + + if (!_bin_loc) { + goto error; + } + + *bin_loc = _bin_loc; + return 0; + +error: + return -1; +} + +/** + * Predicate used to determine whether the children of a given DIE + * contain a specific address. + * + * More specifically, the parameter `die` is expected to be a + * subprogram (function) DIE, and this predicate tells whether any + * subroutines are inlined within this function and would contain + * `addr`. + * + * On success, the out parameter `contains` is set with the boolean + * value indicating whether the DIE's range covers `addr`. On failure, + * it remains unchanged. + * + * Do note that this function advances the position of `die`. If the + * address is found within one of its children, `die` will be pointing + * to that child upon returning from the function, allowing to extract + * the information deemed necessary. + * + * @param die The parent DIE in whose children the address will be + * looked for + * @param addr The address for which to look for in the DIEs + * @param contains Out parameter, true if addr is contained, + * false if not + * @returns Returns 0 on success, -1 on failure + */ +static +int bin_info_child_die_has_address(struct bt_dwarf_die *die, uint64_t addr, bool *contains) +{ + int ret = 0; + bool _contains = false; + + if (!die) { + goto error; + } + + ret = bt_dwarf_die_child(die); + if (ret) { + goto error; + } + + do { + ret = bt_dwarf_die_contains_addr(die, addr, &_contains); + if (ret) { + goto error; + } + + if (_contains) { + /* + * The address is within the range of the current DIE + * or its children. + */ + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_inlined_subroutine) { + /* Found the tracepoint. */ + goto end; + } + + if (bt_dwarf_die_has_children(die)) { + /* + * Look for the address in the children DIEs. + */ + ret = bt_dwarf_die_child(die); + if (ret) { + goto error; + } + } + } + } while (bt_dwarf_die_next(die) == 0); + +end: + *contains = _contains; + return 0; + +error: + return -1; +} + +/** + * Lookup the source location for a given address within a CU, making + * the assumption that it is contained within an inline routine in a + * function. + * + * @param cu bt_dwarf_cu instance in which to look for the address + * @param addr The address for which to look for + * @param src_loc Out parameter, the source location (filename and + * line number) for the address + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_cu_src_loc_inl(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + int ret = 0; + bool found = false; + struct bt_dwarf_die *die = NULL; + struct source_location *_src_loc = NULL; + + if (!cu || !src_loc) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + while (bt_dwarf_die_next(die) == 0) { + int tag; + + ret = bt_dwarf_die_get_tag(die, &tag); + if (ret) { + goto error; + } + + if (tag == DW_TAG_subprogram) { + bool contains = false; + + ret = bt_dwarf_die_contains_addr(die, addr, &contains); + if (ret) { + goto error; + } + + if (contains) { + /* + * Try to find an inlined subroutine + * child of this DIE containing addr. + */ + ret = bin_info_child_die_has_address(die, addr, + &found); + if(ret) { + goto error; + } + + goto end; + } + } + } + +end: + if (found) { + char *filename = NULL; + uint64_t line_no; + + _src_loc = g_new0(struct source_location, 1); + if (!_src_loc) { + goto error; + } + + ret = bt_dwarf_die_get_call_file(die, &filename); + if (ret) { + goto error; + } + ret = bt_dwarf_die_get_call_line(die, &line_no); + if (ret) { + free(filename); + goto error; + } + + _src_loc->filename = filename; + _src_loc->line_no = line_no; + *src_loc = _src_loc; + } + + bt_dwarf_die_destroy(die); + return 0; + +error: + source_location_destroy(_src_loc); + bt_dwarf_die_destroy(die); + return -1; +} + +/** + * Lookup the source location for a given address within a CU, + * assuming that it is contained within an inlined function. + * + * A source location can be found regardless of inlining status for + * this method, but in the case of an inlined function, the returned + * source location will point not to the callsite but rather to the + * definition site of the inline function. + * + * @param cu bt_dwarf_cu instance in which to look for the address + * @param addr The address for which to look for + * @param src_loc Out parameter, the source location (filename and + * line number) for the address. Set only if the address + * is found and resolved successfully + * + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_cu_src_loc_no_inl(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + struct source_location *_src_loc = NULL; + struct bt_dwarf_die *die = NULL; + const char *filename = NULL; + Dwarf_Line *line = NULL; + Dwarf_Addr line_addr; + int ret = 0, line_no; + + if (!cu || !src_loc) { + goto error; + } + + die = bt_dwarf_die_create(cu); + if (!die) { + goto error; + } + + line = dwarf_getsrc_die(die->dwarf_die, addr); + if (!line) { + /* This is not an error. The caller needs to keep looking. */ + goto end; + } + + ret = dwarf_lineaddr(line, &line_addr); + if (ret) { + goto error; + } + + filename = dwarf_linesrc(line, NULL, NULL); + if (!filename) { + goto error; + } + + if (addr == line_addr) { + _src_loc = g_new0(struct source_location, 1); + if (!_src_loc) { + goto error; + } + + ret = dwarf_lineno(line, &line_no); + if (ret) { + goto error; + } + + _src_loc->line_no = line_no; + _src_loc->filename = g_strdup(filename); + } + + if (_src_loc) { + *src_loc = _src_loc; + } + + goto end; + +error: + source_location_destroy(_src_loc); + ret = -1; +end: + bt_dwarf_die_destroy(die); + return ret; +} + +/** + * Get the source location (file name and line number) for a given + * address within a compile unit (CU). + * + * On success, the out parameter `src_loc` is set if found. On + * failure, it remains unchanged. + * + * @param cu bt_dwarf_cu instance for the compile unit which + * may contain the address + * @param addr Virtual memory address for which to find the + * source location + * @param src_loc Out parameter, the source location + * @returns 0 on success, -1 on failure + */ +static +int bin_info_lookup_cu_src_loc(struct bt_dwarf_cu *cu, uint64_t addr, + struct source_location **src_loc) +{ + int ret = 0; + struct source_location *_src_loc = NULL; + + if (!cu || !src_loc) { + goto error; + } + + ret = bin_info_lookup_cu_src_loc_inl(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + goto end; + } + + ret = bin_info_lookup_cu_src_loc_no_inl(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + goto end; + } + +end: + if (_src_loc) { + *src_loc = _src_loc; + } + + return 0; + +error: + source_location_destroy(_src_loc); + return -1; +} + +BT_HIDDEN +int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr, + struct source_location **src_loc) +{ + struct bt_dwarf_cu *cu = NULL; + struct source_location *_src_loc = NULL; + + if (!bin || !src_loc) { + goto error; + } + + /* + * If the bin_info has a build id but it does not match the build id + * that was found on the file system, return an error. + */ + if (bin->build_id && !bin->file_build_id_matches) { + goto error; + } + + /* Set DWARF info if it hasn't been accessed yet. */ + if (!bin->dwarf_info && !bin->is_elf_only) { + if (bin_info_set_dwarf_info(bin)) { + /* Failed to set DWARF info. */ + bin->is_elf_only = true; + } + } + + if (bin->is_elf_only) { + /* We cannot lookup source location without DWARF info. */ + goto error; + } + + if (!bin_info_has_address(bin, addr)) { + goto error; + } + + /* + * Addresses in ELF and DWARF are relative to base address for + * PIC, so make the address argument relative too if needed. + */ + if (bin->is_pic) { + addr -= bin->low_addr; + } + + cu = bt_dwarf_cu_create(bin->dwarf_info); + if (!cu) { + goto error; + } + + while (bt_dwarf_cu_next(cu) == 0) { + int ret; + + ret = bin_info_lookup_cu_src_loc(cu, addr, &_src_loc); + if (ret) { + goto error; + } + + if (_src_loc) { + break; + } + } + + bt_dwarf_cu_destroy(cu); + if (_src_loc) { + *src_loc = _src_loc; + } + + return 0; + +error: + source_location_destroy(_src_loc); + bt_dwarf_cu_destroy(cu); + return -1; +} diff --git a/src/plugins/lttng-utils/debug-info/bin-info.h b/src/plugins/lttng-utils/debug-info/bin-info.h new file mode 100644 index 00000000..bf473690 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/bin-info.h @@ -0,0 +1,240 @@ +#ifndef _BABELTRACE_BIN_INFO_H +#define _BABELTRACE_BIN_INFO_H + +/* + * Babeltrace - Executable and Shared Object Debug Info Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "common/babeltrace.h" +#include "fd-cache/fd-cache.h" + +#define DEFAULT_DEBUG_DIR "/usr/lib/debug" +#define DEBUG_SUBDIR ".debug" +#define BUILD_ID_SUBDIR ".build-id" +#define BUILD_ID_SUFFIX ".debug" +#define BUILD_ID_PREFIX_DIR_LEN 2 + +struct bin_info { + /* Base virtual memory address. */ + uint64_t low_addr; + /* Upper bound of exec address space. */ + uint64_t high_addr; + /* Size of exec address space. */ + uint64_t memsz; + /* Paths to ELF and DWARF files. */ + gchar *elf_path; + gchar *dwarf_path; + /* libelf and libdw objects representing the files. */ + Elf *elf_file; + Dwarf *dwarf_info; + /* Optional build ID info. */ + uint8_t *build_id; + size_t build_id_len; + + /* Optional debug link info. */ + gchar *dbg_link_filename; + uint32_t dbg_link_crc; + /* fd cache handles to ELF and DWARF files. */ + struct bt_fd_cache_handle *elf_handle; + struct bt_fd_cache_handle *dwarf_handle; + /* Configuration. */ + gchar *debug_info_dir; + /* Denotes whether the executable is position independent code. */ + bool is_pic:1; + /* Denotes whether the build id in the trace matches to one on disk. */ + bool file_build_id_matches:1; + /* + * Denotes whether the executable only has ELF symbols and no + * DWARF info. + */ + bool is_elf_only:1; + /* Weak ref. Owned by the iterator. */ + struct bt_fd_cache *fd_cache; +}; + +struct source_location { + uint64_t line_no; + gchar *filename; +}; + +/** + * Initializes the bin_info framework. Call this before calling + * anything else. + * + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_init(void); + +/** + * Instantiate a structure representing an ELF executable, possibly + * with DWARF info, located at the given path. + * + * @param path Path to the ELF file + * @param low_addr Base address of the executable + * @param memsz In-memory size of the executable + * @param is_pic Whether the executable is position independent + * code (PIC) + * @param debug_info_dir Directory containing debug info or NULL. + * @param target_prefix Path to the root file system of the target + * or NULL. + * @returns Pointer to the new bin_info on success, + * NULL on failure. + */ +BT_HIDDEN +struct bin_info *bin_info_create(struct bt_fd_cache *fdc, const char *path, + uint64_t low_addr, uint64_t memsz, bool is_pic, + const char *debug_info_dir, const char *target_prefix); + +/** + * Destroy the given bin_info instance + * + * @param bin bin_info instance to destroy + */ +BT_HIDDEN +void bin_info_destroy(struct bin_info *bin); + +/** + * Sets the build ID information for a given bin_info instance. + * + * @param bin The bin_info instance for which to set + * the build ID + * @param build_id Array of bytes containing the actual ID + * @param build_id_len Length in bytes of the build_id + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_set_build_id(struct bin_info *bin, uint8_t *build_id, + size_t build_id_len); + +/** + * Sets the debug link information for a given bin_info instance. + * + * @param bin The bin_info instance for which to set + * the debug link + * @param filename Name of the separate debug info file + * @param crc Checksum for the debug info file + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_set_debug_link(struct bin_info *bin, const char *filename, + uint32_t crc); + +/** + * Returns whether or not the given bin info \p bin contains the + * address \p addr. + * + * @param bin bin_info instance + * @param addr Address to lookup + * @returns 1 if \p bin contains \p addr, 0 if it does not, + * -1 on failure + */ +static inline +int bin_info_has_address(struct bin_info *bin, uint64_t addr) +{ + if (!bin) { + return -1; + } + + return addr >= bin->low_addr && addr < bin->high_addr; +} + +/** + * Get the name of the function containing a given address within an + * executable. + * + * If no DWARF info is available, the function falls back to ELF + * symbols and the "function name" is in fact the name of the closest + * symbol, followed by the offset between the symbol and the address. + * + * On success, if found, the out parameter `func_name` is set. The ownership + * of `func_name` is passed to the caller. On failure, `func_name` remains + * unchanged. + * + * @param bin bin_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * function name + * @param func_name Out parameter, the function name. + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_lookup_function_name(struct bin_info *bin, uint64_t addr, + char **func_name); + +/** + * Get the source location (file name and line number) for a given + * address within an executable. + * + * If no DWARF info is available, the source location cannot be found + * and the function will return unsuccessfully. + * + * On success, if found, the out parameter `src_loc` is set. The ownership + * of `src_loc` is passed to the caller. On failure, `src_loc` remains + * unchanged. + * + * @param bin bin_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * source location + * @param src_loc Out parameter, the source location + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_lookup_source_location(struct bin_info *bin, uint64_t addr, + struct source_location **src_loc); +/** + * Get a string representing the location within the binary of a given + * address. + * + * In the case of a PIC binary, the location is relative (+0x1234). + * For a non-PIC binary, the location is absolute (@0x1234) + * + * On success, the out parameter `bin_loc` is set. The ownership is + * passed to the caller. On failure, `bin_loc` remains unchanged. + * + * @param bin bin_info instance for the executable containing + * the address + * @param addr Virtual memory address for which to find the + * binary location + * @param bin_loc Out parameter, the binary location + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bin_info_get_bin_loc(struct bin_info *bin, uint64_t addr, char **bin_loc); + +/** + * Destroy the given source_location instance + * + * @param src_loc source_location instance to destroy + */ +BT_HIDDEN +void source_location_destroy(struct source_location *src_loc); + +#endif /* _BABELTRACE_BIN_INFO_H */ diff --git a/src/plugins/lttng-utils/debug-info/crc32.c b/src/plugins/lttng-utils/debug-info/crc32.c new file mode 100644 index 00000000..e68c0434 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/crc32.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "crc32.h" + +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + +/* generated using the AUTODIN II polynomial + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + */ +static const uint32_t crctab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +int crc32(int fd, uint32_t *crc) +{ + int nr; + uint32_t _crc = ~0; + char buf[BUFSIZ], *p; + + if (fd < 0 || !crc) { + goto error; + } + + while ((nr = read(fd, buf, sizeof(buf))) > 0) { + for (p = buf; nr--; ++p) { + CRC(_crc, *p); + } + } + + if (nr < 0) { + goto error; + } + + *crc = ~_crc; + return 0; + +error: + return -1; +} diff --git a/src/plugins/lttng-utils/debug-info/crc32.h b/src/plugins/lttng-utils/debug-info/crc32.h new file mode 100644 index 00000000..73438192 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/crc32.h @@ -0,0 +1,55 @@ +#ifndef _BABELTRACE_CRC32_H +#define _BABELTRACE_CRC32_H + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include "common/babeltrace.h" + +/** + * Compute a 32-bit cyclic redundancy checksum for a given file. + * + * On success, the out parameter crc is set with the computed checksum + * value, + * + * @param fd File descriptor for the file for which to compute the CRC + * @param crc Out parameter, the computed checksum + * @returns 0 on success, -1 on failure. + */ +BT_HIDDEN +int crc32(int fd, uint32_t *crc); + +#endif /* _BABELTRACE_CRC32_H */ diff --git a/src/plugins/lttng-utils/debug-info/debug-info.c b/src/plugins/lttng-utils/debug-info/debug-info.c new file mode 100644 index 00000000..297be248 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/debug-info.c @@ -0,0 +1,2061 @@ +/* + * Babeltrace - Debug Information State Tracker + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015 Philippe Proulx + * Copyright (c) 2015 Antoine Busque + * Copyright (c) 2016 Jérémie Galarneau + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-CTF-LTTNG-UTILS-DEBUG-INFO-FLT" +#include "logging.h" + +#include +#include "plugins/plugins-common.h" + +#include "common/assert.h" +#include "common/common.h" +#include "fd-cache/fd-cache.h" + +#include "bin-info.h" +#include "debug-info.h" +#include "trace-ir-data-copy.h" +#include "trace-ir-mapping.h" +#include "trace-ir-metadata-copy.h" +#include "utils.h" + +#define DEFAULT_DEBUG_INFO_FIELD_NAME "debug_info" +#define LTTNG_UST_STATEDUMP_PREFIX "lttng_ust" +#define VPID_FIELD_NAME "vpid" +#define IP_FIELD_NAME "ip" +#define BADDR_FIELD_NAME "baddr" +#define CRC32_FIELD_NAME "crc" +#define BUILD_ID_FIELD_NAME "build_id" +#define FILENAME_FIELD_NAME "filename" +#define IS_PIC_FIELD_NAME "is_pic" +#define MEMSZ_FIELD_NAME "memsz" +#define PATH_FIELD_NAME "path" + +struct debug_info_component { + gchar *arg_debug_dir; + gchar *arg_debug_info_field_name; + gchar *arg_target_prefix; + bt_bool arg_full_path; +}; + +struct debug_info_msg_iter { + struct debug_info_component *debug_info_component; + bt_self_message_iterator *input_iterator; + bt_self_component *self_comp; + bt_self_component_port_input_message_iterator *msg_iter; + + struct trace_ir_maps *ir_maps; + /* in_trace -> debug_info_mapping. */ + GHashTable *debug_info_map; + + struct bt_fd_cache fd_cache; +}; + +struct debug_info_source { + /* Strings are owned by debug_info_source. */ + gchar *func; + /* + * Store the line number as a string so that the allocation and + * conversion to string is only done once. + */ + gchar *line_no; + gchar *src_path; + /* short_src_path points inside src_path, no need to free. */ + const gchar *short_src_path; + gchar *bin_path; + /* short_bin_path points inside bin_path, no need to free. */ + const gchar *short_bin_path; + /* + * Location within the binary. Either absolute (@0x1234) or + * relative (+0x4321). + */ + gchar *bin_loc; +}; + +struct proc_debug_info_sources { + /* + * Hash table: base address (pointer to uint64_t) to bin info; owned by + * proc_debug_info_sources. + */ + GHashTable *baddr_to_bin_info; + + /* + * Hash table: IP (pointer to uint64_t) to (struct debug_info_source *); + * owned by proc_debug_info_sources. + */ + GHashTable *ip_to_debug_info_src; +}; + +struct debug_info { + struct debug_info_component *comp; + const bt_trace *input_trace; + uint64_t destruction_listener_id; + + /* + * Hash table of VPIDs (pointer to int64_t) to + * (struct proc_debug_info_sources*); owned by debug_info. + */ + GHashTable *vpid_to_proc_dbg_info_src; + GQuark q_statedump_bin_info; + GQuark q_statedump_debug_link; + GQuark q_statedump_build_id; + GQuark q_statedump_start; + GQuark q_dl_open; + GQuark q_lib_load; + GQuark q_lib_unload; + struct bt_fd_cache *fd_cache; /* Weak ref. Owned by the iterator. */ +}; + +static +int debug_info_init(struct debug_info *info) +{ + info->q_statedump_bin_info = g_quark_from_string( + "lttng_ust_statedump:bin_info"); + info->q_statedump_debug_link = g_quark_from_string( + "lttng_ust_statedump:debug_link"); + info->q_statedump_build_id = g_quark_from_string( + "lttng_ust_statedump:build_id"); + info->q_statedump_start = g_quark_from_string( + "lttng_ust_statedump:start"); + info->q_dl_open = g_quark_from_string("lttng_ust_dl:dlopen"); + info->q_lib_load = g_quark_from_string("lttng_ust_lib:load"); + info->q_lib_unload = g_quark_from_string("lttng_ust_lib:unload"); + + return bin_info_init(); +} + +static +void debug_info_source_destroy(struct debug_info_source *debug_info_src) +{ + if (!debug_info_src) { + return; + } + + g_free(debug_info_src->func); + g_free(debug_info_src->line_no); + g_free(debug_info_src->src_path); + g_free(debug_info_src->bin_path); + g_free(debug_info_src->bin_loc); + g_free(debug_info_src); +} + +static +struct debug_info_source *debug_info_source_create_from_bin( + struct bin_info *bin, uint64_t ip) +{ + int ret; + struct debug_info_source *debug_info_src = NULL; + struct source_location *src_loc = NULL; + + debug_info_src = g_new0(struct debug_info_source, 1); + + if (!debug_info_src) { + goto end; + } + + /* Lookup function name */ + ret = bin_info_lookup_function_name(bin, ip, &debug_info_src->func); + if (ret) { + goto error; + } + + /* Can't retrieve src_loc from ELF, or could not find binary, skip. */ + if (!bin->is_elf_only || !debug_info_src->func) { + /* Lookup source location */ + ret = bin_info_lookup_source_location(bin, ip, &src_loc); + if (ret) { + BT_LOGD("Failed to lookup source location: ret=%d", ret); + } + } + + if (src_loc) { + debug_info_src->line_no = + g_strdup_printf("%"PRId64, src_loc->line_no); + if (!debug_info_src->line_no) { + BT_LOGD("Error occured when setting line_no field."); + goto error; + } + + if (src_loc->filename) { + debug_info_src->src_path = g_strdup(src_loc->filename); + if (!debug_info_src->src_path) { + goto error; + } + + debug_info_src->short_src_path = get_filename_from_path( + debug_info_src->src_path); + } + source_location_destroy(src_loc); + } + + if (bin->elf_path) { + debug_info_src->bin_path = g_strdup(bin->elf_path); + if (!debug_info_src->bin_path) { + goto error; + } + + debug_info_src->short_bin_path = get_filename_from_path( + debug_info_src->bin_path); + + ret = bin_info_get_bin_loc(bin, ip, &(debug_info_src->bin_loc)); + if (ret) { + goto error; + } + } + +end: + return debug_info_src; + +error: + debug_info_source_destroy(debug_info_src); + return NULL; +} + +static +void proc_debug_info_sources_destroy( + struct proc_debug_info_sources *proc_dbg_info_src) +{ + if (!proc_dbg_info_src) { + return; + } + + if (proc_dbg_info_src->baddr_to_bin_info) { + g_hash_table_destroy(proc_dbg_info_src->baddr_to_bin_info); + } + + if (proc_dbg_info_src->ip_to_debug_info_src) { + g_hash_table_destroy(proc_dbg_info_src->ip_to_debug_info_src); + } + + g_free(proc_dbg_info_src); +} + +static +struct proc_debug_info_sources *proc_debug_info_sources_create(void) +{ + struct proc_debug_info_sources *proc_dbg_info_src = NULL; + + proc_dbg_info_src = g_new0(struct proc_debug_info_sources, 1); + if (!proc_dbg_info_src) { + goto end; + } + + proc_dbg_info_src->baddr_to_bin_info = g_hash_table_new_full( + g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, + (GDestroyNotify) bin_info_destroy); + if (!proc_dbg_info_src->baddr_to_bin_info) { + goto error; + } + + proc_dbg_info_src->ip_to_debug_info_src = g_hash_table_new_full( + g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, + (GDestroyNotify) debug_info_source_destroy); + if (!proc_dbg_info_src->ip_to_debug_info_src) { + goto error; + } + +end: + return proc_dbg_info_src; + +error: + proc_debug_info_sources_destroy(proc_dbg_info_src); + return NULL; +} + +static +struct proc_debug_info_sources *proc_debug_info_sources_ht_get_entry( + GHashTable *ht, int64_t vpid) +{ + gpointer key = g_new0(int64_t, 1); + struct proc_debug_info_sources *proc_dbg_info_src = NULL; + + if (!key) { + goto end; + } + + *((int64_t *) key) = vpid; + + /* Exists? Return it */ + proc_dbg_info_src = g_hash_table_lookup(ht, key); + if (proc_dbg_info_src) { + goto end; + } + + /* Otherwise, create and return it */ + proc_dbg_info_src = proc_debug_info_sources_create(); + if (!proc_dbg_info_src) { + goto end; + } + + g_hash_table_insert(ht, key, proc_dbg_info_src); + /* Ownership passed to ht */ + key = NULL; +end: + g_free(key); + return proc_dbg_info_src; +} + +static inline +const bt_field *event_borrow_payload_field(const bt_event *event, + const char *field_name) +{ + const bt_field *event_payload, *field; + + event_payload = bt_event_borrow_payload_field_const(event); + BT_ASSERT(event_payload); + + field = bt_field_structure_borrow_member_field_by_name_const( + event_payload, field_name); + return field; +} + +static inline +const bt_field *event_borrow_common_context_field(const bt_event *event, + const char *field_name) +{ + const bt_field *event_common_ctx, *field = NULL; + + event_common_ctx = bt_event_borrow_common_context_field_const(event); + if (!event_common_ctx) { + goto end; + } + + field = bt_field_structure_borrow_member_field_by_name_const( + event_common_ctx, field_name); + +end: + return field; +} + +static inline +void event_get_common_context_signed_integer_field_value( + const bt_event *event, const char *field_name, int64_t *value) +{ + *value = bt_field_signed_integer_get_value( + event_borrow_common_context_field(event, field_name)); +} + +static inline +int event_get_payload_build_id_length(const bt_event *event, + const char *field_name, uint64_t *build_id_len) +{ + const bt_field *build_id_field; + const bt_field_class *build_id_field_class; + + build_id_field = event_borrow_payload_field(event, field_name); + build_id_field_class = bt_field_borrow_class_const(build_id_field); + + BT_ASSERT(bt_field_class_get_type(build_id_field_class) == + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); + BT_ASSERT(bt_field_class_get_type( + bt_field_class_array_borrow_element_field_class_const( + build_id_field_class)) == + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); + + *build_id_len = bt_field_array_get_length(build_id_field); + + return 0; +} + +static inline +int event_get_payload_build_id_value(const bt_event *event, + const char *field_name, uint8_t *build_id) +{ + const bt_field *curr_field, *build_id_field; + const bt_field_class *build_id_field_class; + uint64_t i, build_id_len; + int ret; + + ret = 0; + + build_id_field = event_borrow_payload_field(event, field_name); + build_id_field_class = bt_field_borrow_class_const(build_id_field); + + BT_ASSERT(bt_field_class_get_type(build_id_field_class) == + BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY); + BT_ASSERT(bt_field_class_get_type( + bt_field_class_array_borrow_element_field_class_const( + build_id_field_class)) == + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER); + + build_id_len = bt_field_array_get_length(build_id_field); + + for (i = 0; i < build_id_len; i++) { + curr_field = + bt_field_array_borrow_element_field_by_index_const( + build_id_field, i); + + build_id[i] = bt_field_unsigned_integer_get_value(curr_field); + } + + return ret; +} + +static +void event_get_payload_unsigned_integer_field_value(const bt_event *event, + const char *field_name, uint64_t *value) +{ + *value = bt_field_unsigned_integer_get_value( + event_borrow_payload_field(event, field_name)); +} + +static +void event_get_payload_string_field_value(const bt_event *event, + const char *field_name, const char **value) +{ + *value = bt_field_string_get_value( + event_borrow_payload_field(event, field_name)); +} + +static inline +bool event_has_payload_field(const bt_event *event, + const char *field_name) +{ + return event_borrow_payload_field(event, field_name) != NULL; +} + +static +struct debug_info_source *proc_debug_info_sources_get_entry( + struct proc_debug_info_sources *proc_dbg_info_src, uint64_t ip) +{ + struct debug_info_source *debug_info_src = NULL; + gpointer key = g_new0(uint64_t, 1); + GHashTableIter iter; + gpointer baddr, value; + + if (!key) { + goto end; + } + + *((uint64_t *) key) = ip; + + /* Look in IP to debug infos hash table first. */ + debug_info_src = g_hash_table_lookup( + proc_dbg_info_src->ip_to_debug_info_src, + key); + if (debug_info_src) { + goto end; + } + + /* Check in all bin_infos. */ + g_hash_table_iter_init(&iter, proc_dbg_info_src->baddr_to_bin_info); + + while (g_hash_table_iter_next(&iter, &baddr, &value)) + { + struct bin_info *bin = value; + + if (!bin_info_has_address(value, ip)) { + continue; + } + + /* + * Found; add it to cache. + * + * FIXME: this should be bounded in size (and implement + * a caching policy), and entries should be prunned when + * libraries are unmapped. + */ + debug_info_src = debug_info_source_create_from_bin(bin, ip); + if (debug_info_src) { + g_hash_table_insert( + proc_dbg_info_src->ip_to_debug_info_src, + key, debug_info_src); + /* Ownership passed to ht. */ + key = NULL; + } + break; + } + +end: + free(key); + return debug_info_src; +} + +static +struct debug_info_source *debug_info_query(struct debug_info *debug_info, + int64_t vpid, uint64_t ip) +{ + struct debug_info_source *dbg_info_src = NULL; + struct proc_debug_info_sources *proc_dbg_info_src; + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + dbg_info_src = proc_debug_info_sources_get_entry(proc_dbg_info_src, ip); + +end: + return dbg_info_src; +} + +static +struct debug_info *debug_info_create(struct debug_info_component *comp, + const bt_trace *trace, struct bt_fd_cache *fdc) +{ + int ret; + struct debug_info *debug_info; + + BT_ASSERT(comp); + BT_ASSERT(trace); + BT_ASSERT(fdc); + + debug_info = g_new0(struct debug_info, 1); + if (!debug_info) { + goto end; + } + + debug_info->vpid_to_proc_dbg_info_src = g_hash_table_new_full( + g_int64_hash, g_int64_equal, (GDestroyNotify) g_free, + (GDestroyNotify) proc_debug_info_sources_destroy); + if (!debug_info->vpid_to_proc_dbg_info_src) { + goto error; + } + + debug_info->comp = comp; + ret = debug_info_init(debug_info); + if (ret) { + goto error; + } + + debug_info->input_trace = trace; + debug_info->fd_cache = fdc; + +end: + return debug_info; +error: + g_free(debug_info); + return NULL; +} + +static +void debug_info_destroy(struct debug_info *debug_info) +{ + bt_trace_status status; + if (!debug_info) { + goto end; + } + + if (debug_info->vpid_to_proc_dbg_info_src) { + g_hash_table_destroy(debug_info->vpid_to_proc_dbg_info_src); + } + + status = bt_trace_remove_destruction_listener(debug_info->input_trace, + debug_info->destruction_listener_id); + if (status != BT_TRACE_STATUS_OK) { + BT_LOGD("Trace destruction listener removal failed."); + } + + g_free(debug_info); +end: + return; +} + +static +void handle_event_statedump_build_id(struct debug_info *debug_info, + const bt_event *event) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + uint64_t build_id_len, baddr; + uint8_t *build_id = NULL; + struct bin_info *bin; + int64_t vpid; + int ret = 0; + + event_get_common_context_signed_integer_field_value(event, + VPID_FIELD_NAME, &vpid); + event_get_payload_unsigned_integer_field_value(event, + BADDR_FIELD_NAME, &baddr); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, + (gpointer) &baddr); + if (!bin) { + /* + * The build_id event comes after the bin has been + * created. If it isn't found, just ignore this event. + */ + goto end; + } + ret = event_get_payload_build_id_length(event, BUILD_ID_FIELD_NAME, + &build_id_len); + + build_id = g_new0(uint8_t, build_id_len); + if (!build_id) { + goto end; + } + + ret = event_get_payload_build_id_value(event, BUILD_ID_FIELD_NAME, + build_id); + if (ret) { + goto end; + } + + ret = bin_info_set_build_id(bin, build_id, build_id_len); + if (ret) { + goto end; + } + + /* + * Reset the is_elf_only flag in case it had been set + * previously, because we might find separate debug info using + * the new build id information. + */ + bin->is_elf_only = false; + +end: + g_free(build_id); + return; +} + +static +void handle_event_statedump_debug_link(struct debug_info *debug_info, + const bt_event *event) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + struct bin_info *bin = NULL; + int64_t vpid; + uint64_t baddr; + const char *filename = NULL; + uint32_t crc32; + uint64_t crc_field_value; + + event_get_common_context_signed_integer_field_value(event, + VPID_FIELD_NAME, &vpid); + + event_get_payload_unsigned_integer_field_value(event, + BADDR_FIELD_NAME, &baddr); + + event_get_payload_unsigned_integer_field_value(event, + CRC32_FIELD_NAME, &crc_field_value); + + crc32 = (uint32_t) crc_field_value; + + event_get_payload_string_field_value(event, + FILENAME_FIELD_NAME, &filename); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, + (gpointer) &baddr); + if (!bin) { + /* + * The debug_link event comes after the bin has been + * created. If it isn't found, just ignore this event. + */ + goto end; + } + + bin_info_set_debug_link(bin, filename, crc32); + +end: + return; +} + +static +void handle_bin_info_event(struct debug_info *debug_info, + const bt_event *event, bool has_pic_field) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + struct bin_info *bin; + uint64_t baddr, memsz; + int64_t vpid; + const char *path; + gpointer key = NULL; + bool is_pic; + + event_get_payload_unsigned_integer_field_value(event, + MEMSZ_FIELD_NAME, &memsz); + if (memsz == 0) { + /* Ignore VDSO. */ + goto end; + } + + event_get_payload_unsigned_integer_field_value(event, + BADDR_FIELD_NAME, &baddr); + + /* + * This field is not produced by the dlopen event emitted before + * lttng-ust 2.9. + */ + if (!event_has_payload_field(event, PATH_FIELD_NAME)) { + goto end; + } + event_get_payload_string_field_value(event, PATH_FIELD_NAME, &path); + + if (has_pic_field) { + uint64_t is_pic_field_value; + + event_get_payload_unsigned_integer_field_value(event, + IS_PIC_FIELD_NAME, &is_pic_field_value); + is_pic = is_pic_field_value == 1; + } else { + /* + * dlopen has no is_pic field, because the shared + * object is always PIC. + */ + is_pic = true; + } + + event_get_common_context_signed_integer_field_value(event, + VPID_FIELD_NAME, &vpid); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + key = g_new0(uint64_t, 1); + if (!key) { + goto end; + } + + *((uint64_t *) key) = baddr; + + bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info, key); + if (bin) { + goto end; + } + + bin = bin_info_create(debug_info->fd_cache, path, baddr, memsz, is_pic, + debug_info->comp->arg_debug_dir, + debug_info->comp->arg_target_prefix); + if (!bin) { + goto end; + } + + g_hash_table_insert(proc_dbg_info_src->baddr_to_bin_info, key, bin); + /* Ownership passed to ht. */ + key = NULL; + +end: + g_free(key); + return; +} + +static inline +void handle_event_statedump_bin_info(struct debug_info *debug_info, + const bt_event *event) +{ + handle_bin_info_event(debug_info, event, true); +} + +static inline +void handle_event_lib_load(struct debug_info *debug_info, + const bt_event *event) +{ + handle_bin_info_event(debug_info, event, false); +} + +static +void handle_event_lib_unload(struct debug_info *debug_info, + const bt_event *event) +{ + gboolean ret; + struct proc_debug_info_sources *proc_dbg_info_src; + uint64_t baddr; + int64_t vpid; + + event_get_payload_unsigned_integer_field_value(event, BADDR_FIELD_NAME, + &baddr); + + event_get_common_context_signed_integer_field_value(event, + VPID_FIELD_NAME, &vpid); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + /* + * It's an unload event for a library for which no load event + * was previously received. + */ + goto end; + } + + ret = g_hash_table_remove(proc_dbg_info_src->baddr_to_bin_info, + (gpointer) &baddr); + BT_ASSERT(ret); +end: + return; +} + +static +void handle_event_statedump_start(struct debug_info *debug_info, + const bt_event *event) +{ + struct proc_debug_info_sources *proc_dbg_info_src; + int64_t vpid; + + event_get_common_context_signed_integer_field_value( + event, VPID_FIELD_NAME, &vpid); + + proc_dbg_info_src = proc_debug_info_sources_ht_get_entry( + debug_info->vpid_to_proc_dbg_info_src, vpid); + if (!proc_dbg_info_src) { + goto end; + } + + g_hash_table_remove_all(proc_dbg_info_src->baddr_to_bin_info); + g_hash_table_remove_all(proc_dbg_info_src->ip_to_debug_info_src); + +end: + return; +} + +void trace_debug_info_remove_func(const bt_trace *in_trace, void *data) +{ + struct debug_info_msg_iter *debug_it = data; + if (debug_it->debug_info_map) { + gboolean ret; + ret = g_hash_table_remove(debug_it->debug_info_map, + (gpointer) in_trace); + BT_ASSERT(ret); + } +} + +static +void handle_event_statedump(struct debug_info_msg_iter *debug_it, + const bt_event *event) +{ + const bt_event_class *event_class; + const char *event_name; + GQuark q_event_name; + const bt_trace *trace; + struct debug_info *debug_info; + + BT_ASSERT(debug_it); + BT_ASSERT(event); + + event_class = bt_event_borrow_class_const(event); + + event_name = bt_event_class_get_name(event_class); + + trace = bt_stream_borrow_trace_const( + bt_event_borrow_stream_const(event)); + + debug_info = g_hash_table_lookup(debug_it->debug_info_map, trace); + if (!debug_info) { + debug_info = debug_info_create(debug_it->debug_info_component, + trace, &debug_it->fd_cache); + g_hash_table_insert(debug_it->debug_info_map, (gpointer) trace, + debug_info); + bt_trace_add_destruction_listener(trace, + trace_debug_info_remove_func, debug_it, + &debug_info->destruction_listener_id); + } + + q_event_name = g_quark_try_string(event_name); + + if (q_event_name == debug_info->q_statedump_bin_info) { + /* State dump */ + handle_event_statedump_bin_info(debug_info, event); + } else if (q_event_name == debug_info->q_dl_open || + q_event_name == debug_info->q_lib_load) { + /* + * dl_open and lib_load events are both checked for since + * only dl_open was produced as of lttng-ust 2.8. + * + * lib_load, which is produced from lttng-ust 2.9+, is a lot + * more reliable since it will be emitted when other functions + * of the dlopen family are called (e.g. dlmopen) and when + * library are transitively loaded. + */ + handle_event_lib_load(debug_info, event); + } else if (q_event_name == debug_info->q_statedump_start) { + /* Start state dump */ + handle_event_statedump_start(debug_info, event); + } else if (q_event_name == debug_info->q_statedump_debug_link) { + /* Debug link info */ + handle_event_statedump_debug_link(debug_info, event); + } else if (q_event_name == debug_info->q_statedump_build_id) { + /* Build ID info */ + handle_event_statedump_build_id(debug_info, event); + } else if (q_event_name == debug_info-> q_lib_unload) { + handle_event_lib_unload(debug_info, event); + } + + return; +} + +static +void destroy_debug_info_comp(struct debug_info_component *debug_info) +{ + if (!debug_info) { + return; + } + + g_free(debug_info->arg_debug_dir); + g_free(debug_info->arg_debug_info_field_name); + g_free(debug_info->arg_target_prefix); + g_free(debug_info); +} + +static +void fill_debug_info_bin_field(struct debug_info_source *dbg_info_src, + bool full_path, bt_field *curr_field) +{ + bt_field_status status; + + BT_ASSERT(bt_field_get_class_type(curr_field) == + BT_FIELD_CLASS_TYPE_STRING); + + if (dbg_info_src) { + if (full_path) { + status = bt_field_string_set_value(curr_field, + dbg_info_src->bin_path); + } else { + status = bt_field_string_set_value(curr_field, + dbg_info_src->short_bin_path); + } + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set path component of \"bin\" " + "curr_field field's value: str-fc-addr=%p", + curr_field); + } + + status = bt_field_string_append(curr_field, dbg_info_src->bin_loc); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set bin location component of \"bin\" " + "curr_field field's value: str-fc-addr=%p", + curr_field); + } + } else { + status = bt_field_string_set_value(curr_field, ""); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"bin\" curr_field field's value: " + "str-fc-addr=%p", curr_field); + } + } +} + +static +void fill_debug_info_func_field(struct debug_info_source *dbg_info_src, + bt_field *curr_field) +{ + bt_field_status status; + + BT_ASSERT(bt_field_get_class_type(curr_field) == + BT_FIELD_CLASS_TYPE_STRING); + if (dbg_info_src && dbg_info_src->func) { + status = bt_field_string_set_value(curr_field, + dbg_info_src->func); + } else { + status = bt_field_string_set_value(curr_field, ""); + } + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"func\" curr_field field's value: " + "str-fc-addr=%p", curr_field); + } +} + +static +void fill_debug_info_src_field(struct debug_info_source *dbg_info_src, + bool full_path, bt_field *curr_field) +{ + bt_field_status status; + + BT_ASSERT(bt_field_get_class_type(curr_field) == + BT_FIELD_CLASS_TYPE_STRING); + + if (dbg_info_src && dbg_info_src->src_path) { + if (full_path) { + status = bt_field_string_set_value(curr_field, + dbg_info_src->src_path); + } else { + status = bt_field_string_set_value(curr_field, + dbg_info_src->short_src_path); + } + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set path component of \"src\" " + "curr_field field's value: str-fc-addr=%p", + curr_field); + } + + status = bt_field_string_append(curr_field, ":"); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set colon component of \"src\" " + "curr_field field's value: str-fc-addr=%p", + curr_field); + } + + status = bt_field_string_append(curr_field, dbg_info_src->line_no); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set line number component of \"src\" " + "curr_field field's value: str-fc-addr=%p", + curr_field); + } + } else { + status = bt_field_string_set_value(curr_field, ""); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"src\" curr_field field's value: " + "str-fc-addr=%p", curr_field); + } + } +} + +void fill_debug_info_field_empty(bt_field *debug_info_field) +{ + bt_field_status status; + bt_field *bin_field, *func_field, *src_field; + + BT_ASSERT(bt_field_get_class_type(debug_info_field) == + BT_FIELD_CLASS_TYPE_STRUCTURE); + + bin_field = bt_field_structure_borrow_member_field_by_name( + debug_info_field, "bin"); + func_field = bt_field_structure_borrow_member_field_by_name( + debug_info_field, "func"); + src_field = bt_field_structure_borrow_member_field_by_name( + debug_info_field, "src"); + + BT_ASSERT(bt_field_get_class_type(bin_field) == + BT_FIELD_CLASS_TYPE_STRING); + BT_ASSERT(bt_field_get_class_type(func_field) == + BT_FIELD_CLASS_TYPE_STRING); + BT_ASSERT(bt_field_get_class_type(src_field) == + BT_FIELD_CLASS_TYPE_STRING); + + status = bt_field_string_set_value(bin_field, ""); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"bin\" bin_field field's value: " + "str-fc-addr=%p", bin_field); + } + + status = bt_field_string_set_value(func_field, ""); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"func\" func_field field's value: " + "str-fc-addr=%p", func_field); + } + + status = bt_field_string_set_value(src_field, ""); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set \"src\" src_field field's value: " + "str-fc-addr=%p", src_field); + } +} +static +void fill_debug_info_field(struct debug_info *debug_info, int64_t vpid, + uint64_t ip, bt_field *debug_info_field) +{ + struct debug_info_source *dbg_info_src; + const bt_field_class *debug_info_fc; + + BT_ASSERT(bt_field_get_class_type(debug_info_field) == + BT_FIELD_CLASS_TYPE_STRUCTURE); + + debug_info_fc = bt_field_borrow_class_const(debug_info_field); + + BT_ASSERT(bt_field_class_structure_get_member_count(debug_info_fc) == 3); + + dbg_info_src = debug_info_query(debug_info, vpid, ip); + + fill_debug_info_bin_field(dbg_info_src, debug_info->comp->arg_full_path, + bt_field_structure_borrow_member_field_by_name( + debug_info_field, "bin")); + fill_debug_info_func_field(dbg_info_src, + bt_field_structure_borrow_member_field_by_name( + debug_info_field, "func")); + fill_debug_info_src_field(dbg_info_src, debug_info->comp->arg_full_path, + bt_field_structure_borrow_member_field_by_name( + debug_info_field, "src")); +} + +static +void fill_debug_info_event_if_needed(struct debug_info_msg_iter *debug_it, + const bt_event *in_event, bt_event *out_event) +{ + bt_field *out_common_ctx_field, *out_debug_info_field; + const bt_field *vpid_field, *ip_field, *in_common_ctx_field; + const bt_field_class *in_common_ctx_fc; + struct debug_info *debug_info; + uint64_t vpid; + int64_t ip; + gchar *debug_info_field_name = + debug_it->debug_info_component->arg_debug_info_field_name; + + in_common_ctx_field = bt_event_borrow_common_context_field_const( + in_event); + if (!in_common_ctx_field) { + /* + * There is no event common context so no need to add debug + * info field. + */ + goto end; + } + + in_common_ctx_fc = bt_field_borrow_class_const(in_common_ctx_field); + if (!is_event_common_ctx_dbg_info_compatible(in_common_ctx_fc, + debug_it->ir_maps->debug_info_field_class_name)) { + /* + * The input event common context does not have the necessary + * fields to resolve debug information. + */ + goto end; + } + + /* Borrow the debug-info field. */ + out_common_ctx_field = bt_event_borrow_common_context_field(out_event); + if (!out_common_ctx_field) { + goto end; + } + + out_debug_info_field = bt_field_structure_borrow_member_field_by_name( + out_common_ctx_field, debug_info_field_name); + + vpid_field = bt_field_structure_borrow_member_field_by_name_const( + out_common_ctx_field, VPID_FIELD_NAME); + ip_field = bt_field_structure_borrow_member_field_by_name_const( + out_common_ctx_field, IP_FIELD_NAME); + + vpid = bt_field_signed_integer_get_value(vpid_field); + ip = bt_field_unsigned_integer_get_value(ip_field); + + /* + * Borrow the debug_info structure needed for the source + * resolving. + */ + debug_info = g_hash_table_lookup(debug_it->debug_info_map, + bt_stream_borrow_trace_const( + bt_event_borrow_stream_const(in_event))); + + if (debug_info) { + /* + * Perform the debug-info resolving and set the event fields + * accordingly. + */ + fill_debug_info_field(debug_info, vpid, ip, out_debug_info_field); + } else { + BT_LOGD("No debug information for this trace. Setting debug " + "info fields to empty strings."); + fill_debug_info_field_empty(out_debug_info_field); + } +end: + return; +} + +static +void update_event_statedump_if_needed(struct debug_info_msg_iter *debug_it, + const bt_event *in_event) +{ + const bt_field *event_common_ctx; + const bt_field_class *event_common_ctx_fc; + const bt_event_class *in_event_class = bt_event_borrow_class_const(in_event); + + /* + * If the event is an lttng_ust_statedump event AND has the right event + * common context fields update the debug-info view for this process. + */ + event_common_ctx = bt_event_borrow_common_context_field_const(in_event); + if (!event_common_ctx) { + goto end; + } + + event_common_ctx_fc = bt_field_borrow_class_const(event_common_ctx); + if (is_event_common_ctx_dbg_info_compatible(event_common_ctx_fc, + debug_it->ir_maps->debug_info_field_class_name)) { + /* Checkout if it might be a one of lttng ust statedump events. */ + const char *in_event_name = bt_event_class_get_name(in_event_class); + if (strncmp(in_event_name, LTTNG_UST_STATEDUMP_PREFIX, + strlen(LTTNG_UST_STATEDUMP_PREFIX)) == 0) { + /* Handle statedump events. */ + handle_event_statedump(debug_it, in_event); + } + } +end: + return; +} + +static +bt_message *handle_event_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_clock_snapshot *cs; + const bt_clock_class *default_cc; + const bt_packet *in_packet; + bt_event_class *out_event_class; + bt_packet *out_packet; + bt_event *out_event; + + bt_message *out_message = NULL; + + /* Borrow the input event and its event class. */ + const bt_event *in_event = + bt_message_event_borrow_event_const(in_message); + const bt_event_class *in_event_class = + bt_event_borrow_class_const(in_event); + + update_event_statedump_if_needed(debug_it, in_event); + + out_event_class = trace_ir_mapping_borrow_mapped_event_class( + debug_it->ir_maps, in_event_class); + if (!out_event_class) { + out_event_class = trace_ir_mapping_create_new_mapped_event_class( + debug_it->ir_maps, in_event_class); + } + BT_ASSERT(out_event_class); + + /* Borrow the input and output packets. */ + in_packet = bt_event_borrow_packet_const(in_event); + out_packet = trace_ir_mapping_borrow_mapped_packet(debug_it->ir_maps, + in_packet); + + default_cc = bt_stream_class_borrow_default_clock_class_const( + bt_event_class_borrow_stream_class_const(in_event_class)); + if (default_cc) { + /* Borrow event clock snapshot. */ + cs = bt_message_event_borrow_default_clock_snapshot_const( + in_message); + + /* Create an output event message. */ + out_message = bt_message_event_create_with_default_clock_snapshot( + debug_it->input_iterator, + out_event_class, out_packet, + bt_clock_snapshot_get_value(cs)); + } else { + out_message = bt_message_event_create(debug_it->input_iterator, + out_event_class, out_packet); + } + + if (!out_message) { + BT_LOGE("Error creating output event message."); + goto error; + } + + out_event = bt_message_event_borrow_event(out_message); + + /* Copy the original fields to the output event. */ + copy_event_content(in_event, out_event); + + /* + * Try to set the debug-info fields based on debug information that is + * gathered so far. + */ + fill_debug_info_event_if_needed(debug_it, in_event, out_event); + +error: + return out_message; +} + +static +bt_message *handle_stream_begin_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_stream *in_stream; + bt_message *out_message; + bt_stream *out_stream; + + in_stream = bt_message_stream_beginning_borrow_stream_const(in_message); + BT_ASSERT(in_stream); + + /* Create a duplicated output stream. */ + out_stream = trace_ir_mapping_create_new_mapped_stream( + debug_it->ir_maps, in_stream); + if (!out_stream) { + out_message = NULL; + goto error; + } + + /* Create an output stream beginning message. */ + out_message = bt_message_stream_beginning_create( + debug_it->input_iterator, out_stream); + if (!out_message) { + BT_LOGE("Error creating output stream beginning message: " + "out-s-addr=%p", out_stream); + } +error: + return out_message; +} + +static +bt_message *handle_stream_end_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_stream *in_stream; + bt_message *out_message = NULL; + bt_stream *out_stream; + + in_stream = bt_message_stream_end_borrow_stream_const(in_message); + BT_ASSERT(in_stream); + + out_stream = trace_ir_mapping_borrow_mapped_stream( + debug_it->ir_maps, in_stream); + BT_ASSERT(out_stream); + + /* Create an output stream end message. */ + out_message = bt_message_stream_end_create(debug_it->input_iterator, + out_stream); + if (!out_message) { + BT_LOGE("Error creating output stream end message: out-s-addr=%p", + out_stream); + } + + /* Remove stream from trace mapping hashtable. */ + trace_ir_mapping_remove_mapped_stream(debug_it->ir_maps, in_stream); + + return out_message; +} + +static +bt_message *handle_packet_begin_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + bool has_default_clock_snapshot; + const bt_clock_snapshot *cs; + bt_message *out_message = NULL; + bt_packet *out_packet; + + const bt_packet *in_packet = + bt_message_packet_beginning_borrow_packet_const(in_message); + BT_ASSERT(in_packet); + + /* This packet should not be already mapped. */ + BT_ASSERT(!trace_ir_mapping_borrow_mapped_packet( + debug_it->ir_maps, in_packet)); + + out_packet = trace_ir_mapping_create_new_mapped_packet(debug_it->ir_maps, + in_packet); + + BT_ASSERT(out_packet); + + has_default_clock_snapshot = + bt_stream_class_packets_have_beginning_default_clock_snapshot( + bt_stream_borrow_class_const( + bt_packet_borrow_stream_const(in_packet))); + if (has_default_clock_snapshot) { + /* Borrow clock snapshot. */ + cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const( + in_message); + + /* Create an output packet beginning message. */ + out_message = bt_message_packet_beginning_create_with_default_clock_snapshot( + debug_it->input_iterator, out_packet, + bt_clock_snapshot_get_value(cs)); + } else { + out_message = bt_message_packet_beginning_create( + debug_it->input_iterator, out_packet); + } + if (!out_message) { + BT_LOGE("Error creating output packet beginning message: " + "out-p-addr=%p", out_packet); + } + + return out_message; +} + +static +bt_message *handle_packet_end_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + bool has_default_clock_snapshot; + const bt_clock_snapshot *cs; + const bt_packet *in_packet; + bt_message *out_message = NULL; + bt_packet *out_packet; + + in_packet = bt_message_packet_end_borrow_packet_const(in_message); + BT_ASSERT(in_packet); + + out_packet = trace_ir_mapping_borrow_mapped_packet(debug_it->ir_maps, in_packet); + BT_ASSERT(out_packet); + + has_default_clock_snapshot = + bt_stream_class_packets_have_end_default_clock_snapshot( + bt_stream_borrow_class_const( + bt_packet_borrow_stream_const(in_packet))); + if (has_default_clock_snapshot) { + /* Borrow clock snapshot. */ + cs = bt_message_packet_end_borrow_default_clock_snapshot_const( + in_message); + + /* Create an outpute packet end message. */ + out_message = bt_message_packet_end_create_with_default_clock_snapshot( + debug_it->input_iterator, out_packet, + bt_clock_snapshot_get_value(cs)); + } else { + out_message = bt_message_packet_end_create( + debug_it->input_iterator, out_packet); + } + + if (!out_message) { + BT_LOGE("Error creating output packet end message: " + "out-p-addr=%p", out_packet); + } + + /* Remove packet from data mapping hashtable. */ + trace_ir_mapping_remove_mapped_packet(debug_it->ir_maps, in_packet); + + return out_message; +} + +static +bt_message *handle_msg_iterator_inactivity(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + /* + * This message type can be forwarded directly because it does + * not refer to any objects in the trace class. + */ + bt_message_get_ref(in_message); + return (bt_message*) in_message; +} + +static +bt_message *handle_stream_act_begin_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_clock_snapshot *cs; + const bt_clock_class *default_cc; + bt_message *out_message = NULL; + bt_stream *out_stream; + uint64_t cs_value; + bt_message_stream_activity_clock_snapshot_state cs_state; + + const bt_stream *in_stream = + bt_message_stream_activity_beginning_borrow_stream_const( + in_message); + BT_ASSERT(in_stream); + + out_stream = trace_ir_mapping_borrow_mapped_stream(debug_it->ir_maps, + in_stream); + BT_ASSERT(out_stream); + + out_message = bt_message_stream_activity_beginning_create( + debug_it->input_iterator, out_stream); + if (!out_message) { + BT_LOGE("Error creating output stream activity beginning " + "message: out-s-addr=%p", out_stream); + goto error; + } + + default_cc = bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(in_stream)); + if (default_cc) { + /* Borrow clock snapshot. */ + cs_state = + bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + in_message, &cs); + + if (cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { + cs_value = bt_clock_snapshot_get_value(cs); + bt_message_stream_activity_beginning_set_default_clock_snapshot( + out_message, cs_value); + } else { + bt_message_stream_activity_beginning_set_default_clock_snapshot_state( + out_message, cs_state); + } + } + +error: + return out_message; +} + +static +bt_message *handle_stream_act_end_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_clock_snapshot *cs; + const bt_clock_class *default_cc; + const bt_stream *in_stream; + bt_message *out_message; + bt_stream *out_stream; + uint64_t cs_value; + bt_message_stream_activity_clock_snapshot_state cs_state; + + in_stream = bt_message_stream_activity_end_borrow_stream_const( + in_message); + BT_ASSERT(in_stream); + + out_stream = trace_ir_mapping_borrow_mapped_stream(debug_it->ir_maps, + in_stream); + BT_ASSERT(out_stream); + + out_message = bt_message_stream_activity_end_create( + debug_it->input_iterator, out_stream); + if (!out_message) { + BT_LOGE("Error creating output stream activity end message: " + "out-s-addr=%p", out_stream); + goto error; + } + + default_cc = bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(in_stream)); + + if (default_cc) { + cs_state = + bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + in_message, &cs); + + if (cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN ) { + cs_value = bt_clock_snapshot_get_value(cs); + bt_message_stream_activity_end_set_default_clock_snapshot( + out_message, cs_value); + } else { + bt_message_stream_activity_end_set_default_clock_snapshot_state( + out_message, cs_state); + } + } + +error: + return out_message; +} + +static +bt_message *handle_discarded_events_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_clock_snapshot *begin_cs, *end_cs; + const bt_stream *in_stream; + bool has_default_clock_snapshots; + uint64_t discarded_events, begin_cs_value, end_cs_value; + bt_property_availability prop_avail; + bt_message *out_message = NULL; + bt_stream *out_stream; + + in_stream = bt_message_discarded_events_borrow_stream_const( + in_message); + BT_ASSERT(in_stream); + + out_stream = trace_ir_mapping_borrow_mapped_stream( + debug_it->ir_maps, in_stream); + BT_ASSERT(out_stream); + + has_default_clock_snapshots = + bt_stream_class_discarded_events_have_default_clock_snapshots( + bt_stream_borrow_class_const(in_stream)); + if (has_default_clock_snapshots) { + begin_cs = + bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + in_message); + end_cs = + bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + in_message); + + begin_cs_value = bt_clock_snapshot_get_value(begin_cs); + end_cs_value = bt_clock_snapshot_get_value(end_cs); + + out_message = + bt_message_discarded_events_create_with_default_clock_snapshots( + debug_it->input_iterator, out_stream, + begin_cs_value, end_cs_value); + } else { + out_message = bt_message_discarded_events_create( + debug_it->input_iterator, out_stream); + } + if (!out_message) { + BT_LOGE("Error creating output discarded events message: " + "out-s-addr=%p", out_stream); + goto error; + } + + prop_avail = bt_message_discarded_events_get_count(in_message, + &discarded_events); + + if (prop_avail == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { + bt_message_discarded_events_set_count(out_message, + discarded_events); + } + +error: + return out_message; +} + +static +bt_message *handle_discarded_packets_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + const bt_clock_snapshot *begin_cs, *end_cs; + bool has_default_clock_snapshots; + const bt_stream *in_stream; + uint64_t discarded_packets, begin_cs_value, end_cs_value; + bt_property_availability prop_avail; + bt_message *out_message = NULL; + bt_stream *out_stream; + + in_stream = bt_message_discarded_packets_borrow_stream_const( + in_message); + BT_ASSERT(in_stream); + + out_stream = trace_ir_mapping_borrow_mapped_stream( + debug_it->ir_maps, in_stream); + BT_ASSERT(out_stream); + + has_default_clock_snapshots = + bt_stream_class_discarded_packets_have_default_clock_snapshots( + bt_stream_borrow_class_const(in_stream)); + if (has_default_clock_snapshots) { + begin_cs = + bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + in_message); + + end_cs = + bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + in_message); + + begin_cs_value = bt_clock_snapshot_get_value(begin_cs); + end_cs_value = bt_clock_snapshot_get_value(end_cs); + + out_message = bt_message_discarded_packets_create_with_default_clock_snapshots( + debug_it->input_iterator, out_stream, + begin_cs_value, end_cs_value); + } else { + out_message = bt_message_discarded_packets_create( + debug_it->input_iterator, out_stream); + } + if (!out_message) { + BT_LOGE("Error creating output discarded packet message: " + "out-s-addr=%p", out_stream); + goto error; + } + + prop_avail = bt_message_discarded_packets_get_count(in_message, + &discarded_packets); + if (prop_avail == BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { + bt_message_discarded_packets_set_count(out_message, + discarded_packets); + } + +error: + return out_message; +} + +static +const bt_message *handle_message(struct debug_info_msg_iter *debug_it, + const bt_message *in_message) +{ + bt_message *out_message = NULL; + + switch (bt_message_get_type(in_message)) { + case BT_MESSAGE_TYPE_EVENT: + out_message = handle_event_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + out_message = handle_packet_begin_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_PACKET_END: + out_message = handle_packet_end_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + out_message = handle_stream_begin_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_STREAM_END: + out_message = handle_stream_end_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + out_message = handle_msg_iterator_inactivity(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + out_message = handle_stream_act_begin_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + out_message = handle_stream_act_end_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + out_message = handle_discarded_events_message(debug_it, + in_message); + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + out_message = handle_discarded_packets_message(debug_it, + in_message); + break; + default: + abort(); + break; + } + + return out_message; +} + +static +int init_from_params(struct debug_info_component *debug_info_component, + const bt_value *params) +{ + const bt_value *value = NULL; + int ret = 0; + + BT_ASSERT(params); + + value = bt_value_map_borrow_entry_value_const(params, + "debug-info-field-name"); + if (value) { + debug_info_component->arg_debug_info_field_name = + g_strdup(bt_value_string_get(value)); + } else { + debug_info_component->arg_debug_info_field_name = + g_strdup(DEFAULT_DEBUG_INFO_FIELD_NAME); + } + + value = bt_value_map_borrow_entry_value_const(params, "debug-info-dir"); + if (value) { + debug_info_component->arg_debug_dir = + g_strdup(bt_value_string_get(value)); + } else { + debug_info_component->arg_debug_dir = NULL; + } + + + value = bt_value_map_borrow_entry_value_const(params, "target-prefix"); + if (value) { + debug_info_component->arg_target_prefix = + g_strdup(bt_value_string_get(value)); + } else { + debug_info_component->arg_target_prefix = NULL; + } + + value = bt_value_map_borrow_entry_value_const(params, "full-path"); + if (value) { + debug_info_component->arg_full_path = bt_value_bool_get(value); + } else { + debug_info_component->arg_full_path = BT_FALSE; + } + + return ret; +} + +BT_HIDDEN +bt_self_component_status debug_info_comp_init( + bt_self_component_filter *self_comp, + const bt_value *params, UNUSED_VAR void *init_method_data) +{ + int ret; + struct debug_info_component *debug_info_comp; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + + BT_LOGD("Initializing debug_info component: " + "comp-addr=%p, params-addr=%p", self_comp, params); + + debug_info_comp = g_new0(struct debug_info_component, 1); + if (!debug_info_comp) { + BT_LOGE_STR("Failed to allocate one debug_info component."); + goto error; + } + + bt_self_component_set_data( + bt_self_component_filter_as_self_component(self_comp), + debug_info_comp); + + status = bt_self_component_filter_add_input_port(self_comp, "in", + NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + status = bt_self_component_filter_add_output_port(self_comp, "out", + NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + ret = init_from_params(debug_info_comp, params); + if (ret) { + BT_LOGE("Cannot configure debug_info component: " + "debug_info-comp-addr=%p, params-addr=%p", + debug_info_comp, params); + goto error; + } + + goto end; + +error: + destroy_debug_info_comp(debug_info_comp); + bt_self_component_set_data( + bt_self_component_filter_as_self_component(self_comp), + NULL); + + if (status == BT_SELF_COMPONENT_STATUS_OK) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + } +end: + return status; +} + +BT_HIDDEN +void debug_info_comp_finalize(bt_self_component_filter *self_comp) +{ + struct debug_info_component *debug_info = + bt_self_component_get_data( + bt_self_component_filter_as_self_component( + self_comp)); + BT_LOGD("Finalizing debug_info self_component: comp-addr=%p", + self_comp); + + destroy_debug_info_comp(debug_info); +} + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + const bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_component_port_input_message_iterator *upstream_iterator = NULL; + bt_message_iterator_status upstream_iterator_ret_status; + struct debug_info_msg_iter *debug_info_msg_iter; + struct debug_info_component *debug_info = NULL; + bt_self_message_iterator_status status; + bt_self_component *self_comp = NULL; + bt_message_array_const input_msgs; + const bt_message *out_message; + uint64_t curr_msg_idx, i; + + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + self_comp = bt_self_message_iterator_borrow_component(self_msg_iter); + BT_ASSERT(self_comp); + + debug_info = bt_self_component_get_data(self_comp); + BT_ASSERT(debug_info); + + debug_info_msg_iter = bt_self_message_iterator_get_data(self_msg_iter); + BT_ASSERT(debug_info_msg_iter); + + upstream_iterator = debug_info_msg_iter->msg_iter; + BT_ASSERT(upstream_iterator); + + upstream_iterator_ret_status = + bt_self_component_port_input_message_iterator_next( + upstream_iterator, &input_msgs, count); + if (upstream_iterator_ret_status != BT_MESSAGE_ITERATOR_STATUS_OK) { + /* + * No messages were returned. Not necessarily an error. Convert + * the upstream message iterator status to a self status. + */ + status = bt_common_message_iterator_status_to_self( + upstream_iterator_ret_status); + goto end; + } + + /* + * There should never be more received messages than the capacity we + * provided. + */ + BT_ASSERT(*count <= capacity); + + for (curr_msg_idx = 0; curr_msg_idx < *count; curr_msg_idx++) { + out_message = handle_message(debug_info_msg_iter, + input_msgs[curr_msg_idx]); + if (!out_message) { + goto handle_msg_error; + } + + msgs[curr_msg_idx] = out_message; + /* + * Drop our reference of the input message as we are done with + * it and created a output copy. + */ + bt_message_put_ref(input_msgs[curr_msg_idx]); + } + + goto end; + +handle_msg_error: + /* + * Drop references of all the output messages created before the + * failure. + */ + for (i = 0; i < curr_msg_idx; i++) { + bt_message_put_ref(msgs[i]); + } + + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; +end: + return status; +} + +static +void debug_info_msg_iter_destroy(struct debug_info_msg_iter *debug_info_msg_iter) +{ + if (!debug_info_msg_iter) { + goto end; + } + + if (debug_info_msg_iter->msg_iter) { + bt_self_component_port_input_message_iterator_put_ref( + debug_info_msg_iter->msg_iter); + } + + if (debug_info_msg_iter->ir_maps) { + trace_ir_maps_destroy(debug_info_msg_iter->ir_maps); + } + + if (debug_info_msg_iter->debug_info_map) { + g_hash_table_destroy(debug_info_msg_iter->debug_info_map); + } + + bt_fd_cache_fini(&debug_info_msg_iter->fd_cache); + g_free(debug_info_msg_iter); + +end: + return; +} + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *self_port) +{ + bt_self_message_iterator_status status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + struct bt_self_component_port_input *input_port = NULL; + bt_self_component_port_input_message_iterator *upstream_iterator = NULL; + struct debug_info_msg_iter *debug_info_msg_iter = NULL; + gchar *debug_info_field_name; + int ret; + + /* Borrow the upstream input port. */ + input_port = bt_self_component_filter_borrow_input_port_by_name( + self_comp, "in"); + if (!input_port) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto error; + } + + debug_info_msg_iter = g_new0(struct debug_info_msg_iter, 1); + if (!debug_info_msg_iter) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + /* Create an iterator on the upstream component. */ + upstream_iterator = bt_self_component_port_input_message_iterator_create( + input_port); + if (!upstream_iterator) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( + debug_info_msg_iter->msg_iter, upstream_iterator); + + /* Create hashtable that will contain debug info mapping. */ + debug_info_msg_iter->debug_info_map = g_hash_table_new_full( + g_direct_hash, g_direct_equal, (GDestroyNotify) NULL, + (GDestroyNotify) debug_info_destroy); + if (!debug_info_msg_iter->debug_info_map) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + debug_info_msg_iter->self_comp = + bt_self_component_filter_as_self_component(self_comp); + + debug_info_msg_iter->debug_info_component = bt_self_component_get_data( + bt_self_component_filter_as_self_component( + self_comp)); + + debug_info_field_name = + debug_info_msg_iter->debug_info_component->arg_debug_info_field_name; + + debug_info_msg_iter->ir_maps = trace_ir_maps_create( + bt_self_component_filter_as_self_component(self_comp), + debug_info_field_name); + if (!debug_info_msg_iter->ir_maps) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + ret = bt_fd_cache_init(&debug_info_msg_iter->fd_cache); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto error; + } + + bt_self_message_iterator_set_data(self_msg_iter, debug_info_msg_iter); + debug_info_msg_iter->input_iterator = self_msg_iter; + + goto end; + +error: + debug_info_msg_iter_destroy(debug_info_msg_iter); +end: + return status; +} + +BT_HIDDEN +bt_bool debug_info_msg_iter_can_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct debug_info_msg_iter *debug_info_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + BT_ASSERT(debug_info_msg_iter); + + return bt_self_component_port_input_message_iterator_can_seek_beginning( + debug_info_msg_iter->msg_iter); +} + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct debug_info_msg_iter *debug_info_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(debug_info_msg_iter); + + /* Ask the upstream component to seek to the beginning. */ + status = bt_self_component_port_input_message_iterator_seek_beginning( + debug_info_msg_iter->msg_iter); + if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + /* Clear this iterator data. */ + trace_ir_maps_clear(debug_info_msg_iter->ir_maps); + g_hash_table_remove_all(debug_info_msg_iter->debug_info_map); +end: + return bt_common_message_iterator_status_to_self(status); +} + +BT_HIDDEN +void debug_info_msg_iter_finalize(bt_self_message_iterator *it) +{ + struct debug_info_msg_iter *debug_info_msg_iter; + + debug_info_msg_iter = bt_self_message_iterator_get_data(it); + BT_ASSERT(debug_info_msg_iter); + + debug_info_msg_iter_destroy(debug_info_msg_iter); +} diff --git a/src/plugins/lttng-utils/debug-info/debug-info.h b/src/plugins/lttng-utils/debug-info/debug-info.h new file mode 100644 index 00000000..ef99f718 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/debug-info.h @@ -0,0 +1,68 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_H + +/* + * Babeltrace - Debug information Plugin + * + * Copyright (c) 2015-2019 EfficiOS Inc. + * Copyright (c) 2015 Antoine Busque + * Copyright (c) 2019 Francis Deslauriers francis.deslauriers@efficios.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" + +#define VPID_FIELD_NAME "vpid" +#define IP_FIELD_NAME "ip" + +BT_HIDDEN +bt_self_component_status debug_info_comp_init( + bt_self_component_filter *self_comp, + const bt_value *params, void *init_method_data); + +BT_HIDDEN +void debug_info_comp_finalize(bt_self_component_filter *self_comp); + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *self_port); + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + const bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +BT_HIDDEN +bt_bool debug_info_msg_iter_can_seek_beginning( + bt_self_message_iterator *message_iterator); + +BT_HIDDEN +bt_self_message_iterator_status debug_info_msg_iter_seek_beginning( + bt_self_message_iterator *message_iterator); + +BT_HIDDEN +void debug_info_msg_iter_finalize(bt_self_message_iterator *it); + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_H */ diff --git a/src/plugins/lttng-utils/debug-info/dwarf.c b/src/plugins/lttng-utils/debug-info/dwarf.c new file mode 100644 index 00000000..534d2883 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/dwarf.c @@ -0,0 +1,375 @@ +/* + * dwarf.c + * + * Babeltrace - DWARF Information Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "dwarf.h" + +BT_HIDDEN +struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info) +{ + struct bt_dwarf_cu *cu; + + if (!dwarf_info) { + goto error; + } + + cu = g_new0(struct bt_dwarf_cu, 1); + if (!cu) { + goto error; + } + cu->dwarf_info = dwarf_info; + return cu; + +error: + return NULL; +} + +BT_HIDDEN +void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu) +{ + g_free(cu); +} + +BT_HIDDEN +int bt_dwarf_cu_next(struct bt_dwarf_cu *cu) +{ + int ret; + Dwarf_Off next_offset; + size_t cu_header_size; + + if (!cu) { + ret = -1; + goto end; + } + + ret = dwarf_nextcu(cu->dwarf_info, cu->next_offset, &next_offset, + &cu_header_size, NULL, NULL, NULL); + if (ret) { + /* ret is -1 on error, 1 if no next CU. */ + goto end; + } + + cu->offset = cu->next_offset; + cu->next_offset = next_offset; + cu->header_size = cu_header_size; + +end: + return ret; +} + +BT_HIDDEN +struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu) +{ + Dwarf_Die *dwarf_die = NULL; + struct bt_dwarf_die *die = NULL; + + if (!cu) { + goto error; + } + + dwarf_die = g_new0(Dwarf_Die, 1); + if (!dwarf_die) { + goto error; + } + + dwarf_die = dwarf_offdie(cu->dwarf_info, cu->offset + cu->header_size, + dwarf_die); + if (!dwarf_die) { + goto error; + } + + die = g_new0(struct bt_dwarf_die, 1); + if (!die) { + goto error; + } + + die->cu = cu; + die->dwarf_die = dwarf_die; + die->depth = 0; + + return die; + +error: + g_free(dwarf_die); + g_free(die); + return NULL; +} + +BT_HIDDEN +void bt_dwarf_die_destroy(struct bt_dwarf_die *die) +{ + if (!die) { + return; + } + + g_free(die->dwarf_die); + g_free(die); +} + +BT_HIDDEN +int bt_dwarf_die_has_children(struct bt_dwarf_die *die) +{ + return dwarf_haschildren(die->dwarf_die); +} + +BT_HIDDEN +int bt_dwarf_die_child(struct bt_dwarf_die *die) +{ + int ret; + Dwarf_Die *child_die = NULL; + + if (!die) { + ret = -1; + goto error; + } + + child_die = g_new0(Dwarf_Die, 1); + if (!child_die) { + ret = -1; + goto error; + } + + ret = dwarf_child(die->dwarf_die, child_die); + if (ret) { + /* ret is -1 on error, 1 if no child DIE. */ + goto error; + } + + g_free(die->dwarf_die); + die->dwarf_die = child_die; + die->depth++; + return 0; + +error: + g_free(child_die); + return ret; +} + +BT_HIDDEN +int bt_dwarf_die_next(struct bt_dwarf_die *die) +{ + int ret; + Dwarf_Die *next_die = NULL; + + if (!die) { + ret = -1; + goto error; + } + + next_die = g_new0(Dwarf_Die, 1); + if (!next_die) { + ret = -1; + goto error; + } + + if (die->depth == 0) { + ret = dwarf_child(die->dwarf_die, next_die); + if (ret) { + /* ret is -1 on error, 1 if no child DIE. */ + goto error; + } + + die->depth = 1; + } else { + ret = dwarf_siblingof(die->dwarf_die, next_die); + if (ret) { + /* ret is -1 on error, 1 if we reached end of + * DIEs at this depth. */ + goto error; + } + } + + g_free(die->dwarf_die); + die->dwarf_die = next_die; + return 0; + +error: + g_free(next_die); + return ret; +} + +BT_HIDDEN +int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag) +{ + int _tag; + + if (!die || !tag) { + goto error; + } + + _tag = dwarf_tag(die->dwarf_die); + if (_tag == DW_TAG_invalid) { + goto error; + } + + *tag = _tag; + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name) +{ + const char *_name; + + if (!die || !name) { + goto error; + } + + _name = dwarf_diename(die->dwarf_die); + if (!_name) { + goto error; + } + + *name = g_strdup(_name); + if (!*name) { + goto error; + } + + return 0; + +error: + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename) +{ + int ret; + Dwarf_Sword file_no; + const char *_filename = NULL; + Dwarf_Files *src_files = NULL; + Dwarf_Attribute *file_attr = NULL; + struct bt_dwarf_die *cu_die = NULL; + + if (!die || !filename) { + goto error; + } + + file_attr = g_new0(Dwarf_Attribute, 1); + if (!file_attr) { + goto error; + } + + file_attr = dwarf_attr(die->dwarf_die, DW_AT_call_file, file_attr); + if (!file_attr) { + goto error; + } + + ret = dwarf_formsdata(file_attr, &file_no); + if (ret) { + goto error; + } + + cu_die = bt_dwarf_die_create(die->cu); + if (!cu_die) { + goto error; + } + + ret = dwarf_getsrcfiles(cu_die->dwarf_die, &src_files, NULL); + if (ret) { + goto error; + } + + _filename = dwarf_filesrc(src_files, file_no, NULL, NULL); + if (!_filename) { + goto error; + } + + *filename = g_strdup(_filename); + + bt_dwarf_die_destroy(cu_die); + g_free(file_attr); + + return 0; + +error: + bt_dwarf_die_destroy(cu_die); + g_free(file_attr); + + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, + uint64_t *line_no) +{ + int ret = 0; + Dwarf_Attribute *line_attr = NULL; + uint64_t _line_no; + + if (!die || !line_no) { + goto error; + } + + line_attr = g_new0(Dwarf_Attribute, 1); + if (!line_attr) { + goto error; + } + + line_attr = dwarf_attr(die->dwarf_die, DW_AT_call_line, line_attr); + if (!line_attr) { + goto error; + } + + ret = dwarf_formudata(line_attr, &_line_no); + if (ret) { + goto error; + } + + *line_no = _line_no; + g_free(line_attr); + + return 0; + +error: + g_free(line_attr); + + return -1; +} + +BT_HIDDEN +int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, + bool *contains) +{ + int ret; + + ret = dwarf_haspc(die->dwarf_die, addr); + if (ret == -1) { + goto error; + } + + *contains = (ret == 1); + + return 0; + +error: + return -1; +} diff --git a/src/plugins/lttng-utils/debug-info/dwarf.h b/src/plugins/lttng-utils/debug-info/dwarf.h new file mode 100644 index 00000000..4ab527b0 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/dwarf.h @@ -0,0 +1,235 @@ +#ifndef _BABELTRACE_DWARF_H +#define _BABELTRACE_DWARF_H + +/* + * Babeltrace - DWARF Information Reader + * + * Copyright 2015 Antoine Busque + * + * Author: Antoine Busque + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "common/babeltrace.h" + +/* + * bt_dwarf is a wrapper over libdw providing a nicer, higher-level + * interface, to access basic debug information. + */ + +/* + * This structure corresponds to a single compilation unit (CU) for a + * given set of debug information (Dwarf type). + */ +struct bt_dwarf_cu { + Dwarf *dwarf_info; + /* Offset in bytes in the DWARF file to current CU header. */ + Dwarf_Off offset; + /* Offset in bytes in the DWARF file to next CU header. */ + Dwarf_Off next_offset; + /* Size in bytes of CU header */ + size_t header_size; +}; + +/* + * This structure represents a single debug information entry (DIE), + * within a compilation unit (CU). + */ +struct bt_dwarf_die { + struct bt_dwarf_cu *cu; + Dwarf_Die *dwarf_die; + /* + * A depth of 0 represents a root DIE, located in the DWARF + * layout on the same level as its corresponding CU entry. Its + * children DIEs will have a depth of 1, and so forth. + */ + unsigned int depth; +}; + +/** + * Instantiate a structure to access compile units (CU) from a given + * `dwarf_info`. + * + * @param dwarf_info Dwarf instance + * @returns Pointer to the new bt_dwarf_cu on success, + * NULL on failure. + */ +BT_HIDDEN +struct bt_dwarf_cu *bt_dwarf_cu_create(Dwarf *dwarf_info); + +/** + * Destroy the given bt_dwarf_cu instance. + * + * @param cu bt_dwarf_cu instance + */ +BT_HIDDEN +void bt_dwarf_cu_destroy(struct bt_dwarf_cu *cu); + +/** + * Advance the compile unit `cu` to the next one. + * + * On success, `cu`'s offset is set to that of the current compile + * unit in the executable. On failure, `cu` remains unchanged. + * + * @param cu bt_dwarf_cu instance + * @returns 0 on success, 1 if no next CU is available, + * -1 on failure + */ +BT_HIDDEN +int bt_dwarf_cu_next(struct bt_dwarf_cu *cu); + +/** + * Instantiate a structure to access debug information entries (DIE) + * for the given compile unit `cu`. + * + * @param cu bt_dwarf_cu instance + * @returns Pointer to the new bt_dwarf_die on success, + * NULL on failure. + */ +BT_HIDDEN +struct bt_dwarf_die *bt_dwarf_die_create(struct bt_dwarf_cu *cu); + +/** + * Destroy the given bt_dwarf_die instance. + * + * @param die bt_dwarf_die instance + */ +BT_HIDDEN +void bt_dwarf_die_destroy(struct bt_dwarf_die *die); + +/** + * Indicates if the debug information entry `die` has children DIEs. + * + * @param die bt_dwarf_die instance + * @returns 0 if the die no child, 1 otherwise + */ +BT_HIDDEN +int bt_dwarf_die_has_children(struct bt_dwarf_die *die); + +/** + * Advance the debug information entry `die` to its first child, if + * any. + * + * @param die bt_dwarf_die instance + * @returns 0 on success, 1 if no child DIE is available, + * -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_child(struct bt_dwarf_die *die); + +/** + * Advance the debug information entry `die` to the next one. + * + * The next DIE is considered to be its sibling on the same level. The + * only exception is when the depth of the given DIE is 0, i.e. a + * newly created bt_dwarf_die, in which case next returns the first + * DIE at depth 1. + * + * The reason for staying at a depth of 1 is that this is where all + * the function DIEs (those with a tag value of DW_TAG_subprogram) are + * located, from which more specific child DIEs can then be accessed + * if needed via bt_dwarf_die_child. + * + * @param die bt_dwarf_die instance + * @returns 0 on success, 1 if no other siblings are available, -1 on + * failure + */ +BT_HIDDEN +int bt_dwarf_die_next(struct bt_dwarf_die *die); + +/** + * Get a DIE's tag. + * + * On success, the `tag` out parameter is set to the `die`'s tag's + * value. It remains unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param tag Out parameter, the DIE's tag value + * @returns 0 on success, -1 on failure. + */ +BT_HIDDEN +int bt_dwarf_die_get_tag(struct bt_dwarf_die *die, int *tag); + +/** + * Get a DIE's name. + * + * On success, the `name` out parameter is set to the DIE's name. It + * remains unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param name Out parameter, the DIE's name + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_name(struct bt_dwarf_die *die, char **name); + +/** + * Get the full path to the DIE's callsite file. + * + * Only applies to DW_TAG_inlined_subroutine entries. The out + * parameter `filename` is set on success, unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param filename Out parameter, the filename for the subroutine's + * callsite + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_call_file(struct bt_dwarf_die *die, char **filename); + +/** + * Get line number for the DIE's callsite. + * + * Only applies to DW_TAG_inlined_subroutine entries. The out + * parameter `line_no` is set on success, unchanged on failure. + * + * @param die bt_dwarf_die instance + * @param line_no Out parameter, the line number for the + * subroutine's callsite + * @returns 0 on success, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_get_call_line(struct bt_dwarf_die *die, + uint64_t *line_no); + +/** + * Verifies whether a given DIE contains the virtual memory address + * `addr`. + * + * On success, the out parameter `contains` is set with the boolean + * value indicating whether the DIE's range covers `addr`. On failure, + * it remains unchanged. + * + * @param die bt_dwarf_die instance + * @param addr The memory address to verify + * @param contains Out parameter, true if addr is contained, + * false if not + * @returns 0 on succes, -1 on failure + */ +BT_HIDDEN +int bt_dwarf_die_contains_addr(struct bt_dwarf_die *die, uint64_t addr, + bool *contains); + +#endif /* _BABELTRACE_DWARF_H */ diff --git a/src/plugins/lttng-utils/debug-info/logging.c b/src/plugins/lttng-utils/debug-info/logging.c new file mode 100644 index 00000000..a872d571 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_lttng_utils_debug_info_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_lttng_utils_debug_info_log_level, + "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL"); diff --git a/src/plugins/lttng-utils/debug-info/logging.h b/src/plugins/lttng-utils/debug-info/logging.h new file mode 100644 index 00000000..fa298d54 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H +#define PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_lttng_utils_debug_info_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_lttng_utils_debug_info_log_level); + +#endif /* PLUGINS_LTTNG_UTILS_DEBUG_INFO_LOGGING_H */ diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.c b/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.c new file mode 100644 index 00000000..c677770d --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.c @@ -0,0 +1,287 @@ +/* + * Babeltrace - Trace IR data object copy + * + * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-DATA-COPY" +#include "logging.h" + +#include +#include + +#include "common/assert.h" + +#include "trace-ir-data-copy.h" + +BT_HIDDEN +void copy_trace_content(const bt_trace *in_trace, bt_trace *out_trace) +{ + bt_trace_status status; + const char *trace_name; + + BT_LOGD("Copying content of trace: in-t-addr=%p, out-t-addr=%p", + in_trace, out_trace); + + trace_name = bt_trace_get_name(in_trace); + /* Copy the trace name. */ + if (trace_name) { + status = bt_trace_set_name(out_trace, trace_name); + if (status != BT_TRACE_STATUS_OK) { + BT_LOGE("Cannot set trace's name: trace-addr=%p, name=\"%s\"", + out_trace, trace_name); + goto end; + } + } + + BT_LOGD("Copied content of trace: in-t-addr=%p, out-t-addr=%p", + in_trace, out_trace); +end: + return; +} + +BT_HIDDEN +void copy_stream_content(const bt_stream *in_stream, bt_stream *out_stream) +{ + const char *stream_name; + bt_stream_status status; + + BT_LOGD("Copying content of stream: in-s-addr=%p, out-s-addr=%p", + in_stream, out_stream); + + stream_name = bt_stream_get_name(in_stream); + if (stream_name) { + status = bt_stream_set_name(out_stream, stream_name); + if (status != BT_STREAM_STATUS_OK) { + BT_LOGE("Cannot set stream's name: stream-addr=%p, " + "name=%s", out_stream, stream_name); + goto end; + } + } + + BT_LOGD("Copied content of stream: in-s-addr=%p, out-s-addr=%p", + in_stream, out_stream); +end: + return; +} + +BT_HIDDEN +void copy_packet_content(const bt_packet *in_packet, bt_packet *out_packet) +{ + const bt_field *in_context_field; + bt_field *out_context_field; + + BT_LOGD("Copying content of packet: in-p-addr=%p, out-p-addr=%p", + in_packet, out_packet); + + /* Copy context field. */ + in_context_field = bt_packet_borrow_context_field_const(in_packet); + if (in_context_field) { + out_context_field = bt_packet_borrow_context_field(out_packet); + BT_ASSERT(out_context_field); + copy_field_content(in_context_field, out_context_field); + } + + BT_LOGD("Copied content of packet: in-p-addr=%p, out-p-addr=%p", + in_packet, out_packet); + return; +} + +BT_HIDDEN +void copy_event_content(const bt_event *in_event, bt_event *out_event) +{ + const bt_field *in_common_ctx_field, *in_specific_ctx_field, + *in_payload_field; + bt_field *out_common_ctx_field, *out_specific_ctx_field, + *out_payload_field; + + BT_LOGD("Copying content of event: in-e-addr=%p, out-e-addr=%p", + in_event, out_event); + in_common_ctx_field = + bt_event_borrow_common_context_field_const(in_event); + if (in_common_ctx_field) { + out_common_ctx_field = + bt_event_borrow_common_context_field(out_event); + BT_ASSERT(out_common_ctx_field); + copy_field_content(in_common_ctx_field, + out_common_ctx_field); + } + + in_specific_ctx_field = + bt_event_borrow_specific_context_field_const(in_event); + if (in_specific_ctx_field) { + out_specific_ctx_field = + bt_event_borrow_specific_context_field(out_event); + BT_ASSERT(out_specific_ctx_field); + copy_field_content(in_specific_ctx_field, + out_specific_ctx_field); + } + + in_payload_field = bt_event_borrow_payload_field_const(in_event); + if (in_payload_field) { + out_payload_field = bt_event_borrow_payload_field(out_event); + BT_ASSERT(out_payload_field); + copy_field_content(in_payload_field, + out_payload_field); + } + + BT_LOGD("Copied content of event: in-e-addr=%p, out-e-addr=%p", + in_event, out_event); +} + +BT_HIDDEN +void copy_field_content(const bt_field *in_field, bt_field *out_field) +{ + bt_field_class_type in_fc_type, out_fc_type; + + in_fc_type = bt_field_get_class_type(in_field); + out_fc_type = bt_field_get_class_type(out_field); + BT_ASSERT(in_fc_type == out_fc_type); + + BT_LOGD("Copying content of field: in-f-addr=%p, out-f-addr=%p", + in_field, out_field); + switch (in_fc_type) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + bt_field_unsigned_integer_set_value(out_field, + bt_field_unsigned_integer_get_value(in_field)); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + bt_field_signed_integer_set_value(out_field, + bt_field_signed_integer_get_value(in_field)); + break; + case BT_FIELD_CLASS_TYPE_REAL: + bt_field_real_set_value(out_field, + bt_field_real_get_value(in_field)); + break; + case BT_FIELD_CLASS_TYPE_STRING: + { + const char *str = bt_field_string_get_value(in_field); + bt_field_status status = bt_field_string_set_value(out_field, str); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set string field's value: " + "str-field-addr=%p, str=%s" PRId64, + out_field, str); + } + break; + } + case BT_FIELD_CLASS_TYPE_STRUCTURE: + { + uint64_t i, nb_member_struct; + const bt_field *in_member_field; + bt_field *out_member_field; + const bt_field_class *in_field_class; + const char *in_member_name; + + in_field_class = bt_field_borrow_class_const(in_field); + nb_member_struct = bt_field_class_structure_get_member_count( + in_field_class); + + /* + * Iterate over the fields by names in the input field to avoid + * problem if the struct fields are not in the same order after + * the debug-info was added. + */ + for (i = 0; i < nb_member_struct; i++) { + const bt_field_class_structure_member *member = + bt_field_class_structure_borrow_member_by_index_const( + in_field_class, i); + + in_member_name = + bt_field_class_structure_member_get_name( + member); + in_member_field = + bt_field_structure_borrow_member_field_by_name_const( + in_field, in_member_name); + out_member_field = + bt_field_structure_borrow_member_field_by_name( + out_field, in_member_name); + + copy_field_content(in_member_field, + out_member_field); + } + break; + } + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + /* fall through */ + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + { + const bt_field *in_element_field; + bt_field *out_element_field; + uint64_t i, array_len; + bt_field_status status; + + array_len = bt_field_array_get_length(in_field); + + if (in_fc_type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY) { + status = bt_field_dynamic_array_set_length(out_field, + array_len); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot set dynamic array field's " + "length field: field-addr=%p, " + "length=%" PRIu64, out_field, array_len); + } + } + + for (i = 0; i < array_len; i++) { + in_element_field = + bt_field_array_borrow_element_field_by_index_const( + in_field, i); + out_element_field = + bt_field_array_borrow_element_field_by_index( + out_field, i); + copy_field_content(in_element_field, out_element_field); + } + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + { + bt_field_status status; + uint64_t in_selected_option_idx; + const bt_field *in_option_field; + bt_field *out_option_field; + + in_selected_option_idx = + bt_field_variant_get_selected_option_field_index( + in_field); + status = bt_field_variant_select_option_field(out_field, + in_selected_option_idx); + if (status != BT_FIELD_STATUS_OK) { + BT_LOGE("Cannot select variant field's option field: " + "var-field-addr=%p, opt-index=%" PRId64, + out_field, in_selected_option_idx); + } + + in_option_field = bt_field_variant_borrow_selected_option_field_const(in_field); + out_option_field = bt_field_variant_borrow_selected_option_field(out_field); + + copy_field_content(in_option_field, out_option_field); + + break; + } + default: + abort(); + } + BT_LOGD("Copied content of field: in-f-addr=%p, out-f-addr=%p", + in_field, out_field); +} diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.h b/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.h new file mode 100644 index 00000000..b9259769 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-data-copy.h @@ -0,0 +1,44 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H + +/* + * Babeltrace - Trace IR data object copy + * + * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "trace-ir-mapping.h" + +BT_HIDDEN +void copy_trace_content(const bt_trace *in_trace, bt_trace *out_trace); +BT_HIDDEN +void copy_stream_content(const bt_stream *in_stream, bt_stream *out_stream); +BT_HIDDEN +void copy_packet_content(const bt_packet *in_packet, bt_packet *out_packet); +BT_HIDDEN +void copy_event_content(const bt_event *in_event, bt_event *out_event); +BT_HIDDEN +void copy_field_content(const bt_field *in_field, bt_field *out_field); + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_DATA_COPY_H */ diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-mapping.c b/src/plugins/lttng-utils/debug-info/trace-ir-mapping.c new file mode 100644 index 00000000..67b24f84 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-mapping.c @@ -0,0 +1,683 @@ +/* + * Babeltrace - Mapping of IR metadata and data object between input and output + * trace + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-MAPPING" +#include "logging.h" + +#include + +#include "common/assert.h" +#include +/* For bt_property_availability */ +#include + +#include "debug-info.h" +#include "trace-ir-data-copy.h" +#include "trace-ir-mapping.h" +#include "trace-ir-metadata-copy.h" + +static +bt_trace_class *create_new_mapped_trace_class(struct trace_ir_maps *ir_maps, + const bt_trace_class *in_trace_class) +{ + int ret; + bt_trace_class *out_trace_class; + + BT_LOGD("Creating new mapped trace class: in-tc-addr=%p", in_trace_class); + + BT_ASSERT(ir_maps); + BT_ASSERT(in_trace_class); + + /* Create the ouput trace class. */ + out_trace_class = bt_trace_class_create(ir_maps->self_comp); + if (!out_trace_class) { + BT_LOGE_STR("Error create output trace class"); + goto end; + } + + /* If not, create a new one and add it to the mapping. */ + ret = copy_trace_class_content(in_trace_class, out_trace_class); + if (ret) { + BT_LOGE_STR("Error copy content to output trace class"); + out_trace_class = NULL; + goto end; + } + + BT_LOGD("Created new mapped trace class: in-tc-addr=%p, out-tc-addr=%p", + in_trace_class, out_trace_class); + +end: + return out_trace_class; +} + +static +bt_trace *create_new_mapped_trace(struct trace_ir_maps *ir_maps, + const bt_trace *in_trace) +{ + bt_trace *out_trace; + const bt_trace_class *in_trace_class; + struct trace_ir_metadata_maps *metadata_maps; + + BT_LOGD("Creating new mapped trace: in-t-addr=%p", in_trace); + BT_ASSERT(ir_maps); + BT_ASSERT(in_trace); + + in_trace_class = bt_trace_borrow_class_const(in_trace); + metadata_maps = borrow_metadata_maps_from_input_trace_class(ir_maps, + in_trace_class); + + if (!metadata_maps->output_trace_class) { + metadata_maps->output_trace_class = + create_new_mapped_trace_class(ir_maps, in_trace_class); + if (!metadata_maps->output_trace_class) { + out_trace = NULL; + goto end; + } + } + + out_trace = bt_trace_create(metadata_maps->output_trace_class); + if (!out_trace) { + BT_LOGE_STR("Error create output trace"); + goto end; + } + + /* If not, create a new one and add it to the mapping. */ + copy_trace_content(in_trace, out_trace); + + BT_LOGD("Created new mapped trace: in-t-addr=%p, out-t-addr=%p", + in_trace, out_trace); +end: + return out_trace; +} + +static +bt_stream_class *borrow_mapped_stream_class(struct trace_ir_metadata_maps *md_maps, + const bt_stream_class *in_stream_class) +{ + BT_ASSERT(md_maps); + BT_ASSERT(in_stream_class); + + return g_hash_table_lookup(md_maps->stream_class_map, + (gpointer) in_stream_class); +} + +static +bt_stream_class *create_new_mapped_stream_class(struct trace_ir_maps *ir_maps, + const bt_stream_class *in_stream_class) +{ + int ret; + bt_stream_class *out_stream_class; + struct trace_ir_metadata_maps *md_maps; + + BT_LOGD("Creating new mapped stream class: in-sc-addr=%p", + in_stream_class); + + md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, + in_stream_class); + + BT_ASSERT(md_maps); + BT_ASSERT(in_stream_class); + BT_ASSERT(!borrow_mapped_stream_class(md_maps, in_stream_class)); + + /* Create an out_stream_class. */ + out_stream_class = bt_stream_class_create_with_id( + md_maps->output_trace_class, + bt_stream_class_get_id(in_stream_class)); + if (!out_stream_class) { + BT_LOGE_STR("Error create output stream class"); + goto end; + } + + /* If not, create a new one and add it to the mapping. */ + ret = copy_stream_class_content(ir_maps, in_stream_class, + out_stream_class); + if (ret) { + BT_LOGE_STR("Error copy content to output stream class"); + out_stream_class = NULL; + goto end; + } + + g_hash_table_insert(md_maps->stream_class_map, + (gpointer) in_stream_class, out_stream_class); + + BT_LOGD("Created new mapped stream class: in-sc-addr=%p, out-sc-addr=%p", + in_stream_class, out_stream_class); + +end: + return out_stream_class; +} + +static +bt_stream *borrow_mapped_stream(struct trace_ir_data_maps *d_maps, + const bt_stream *in_stream) +{ + BT_ASSERT(d_maps); + BT_ASSERT(in_stream); + + return g_hash_table_lookup(d_maps->stream_map, (gpointer) in_stream); +} + +BT_HIDDEN +bt_stream *trace_ir_mapping_create_new_mapped_stream( + struct trace_ir_maps *ir_maps, + const bt_stream *in_stream) +{ + struct trace_ir_data_maps *d_maps; + struct trace_ir_metadata_maps *md_maps; + const bt_stream_class *in_stream_class; + const bt_trace *in_trace; + bt_stream_class *out_stream_class; + bt_stream *out_stream = NULL; + + BT_LOGD("Creating new mapped stream: in-s-addr=%p", in_stream); + + BT_ASSERT(ir_maps); + BT_ASSERT(in_stream); + + in_trace = bt_stream_borrow_trace_const(in_stream); + + d_maps = borrow_data_maps_from_input_trace(ir_maps, in_trace); + if (!d_maps->output_trace) { + d_maps->output_trace = create_new_mapped_trace(ir_maps, in_trace); + if (!d_maps->output_trace) { + goto end; + } + } + + BT_ASSERT(d_maps->output_trace); + BT_ASSERT(!borrow_mapped_stream(d_maps, in_stream)); + + in_stream_class = bt_stream_borrow_class_const(in_stream); + md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, in_stream_class); + out_stream_class = borrow_mapped_stream_class(md_maps, in_stream_class); + if (!out_stream_class) { + out_stream_class = create_new_mapped_stream_class(ir_maps, + in_stream_class); + if (!out_stream_class) { + goto end; + } + } + BT_ASSERT(out_stream_class); + + out_stream = bt_stream_create_with_id(out_stream_class, + d_maps->output_trace, bt_stream_get_id(in_stream)); + if (!out_stream) { + BT_LOGE_STR("Error creating output stream"); + goto end; + } + /* + * Release our ref since the trace object will be managing the life + * time of the stream objects. + */ + + copy_stream_content(in_stream, out_stream); + + g_hash_table_insert(d_maps->stream_map, (gpointer) in_stream, + out_stream); + + BT_LOGD("Created new mapped stream: in-s-addr=%p, out-s-addr=%p", + in_stream, out_stream); + +end: + return out_stream; +} + +BT_HIDDEN +bt_stream *trace_ir_mapping_borrow_mapped_stream(struct trace_ir_maps *ir_maps, + const bt_stream *in_stream) +{ + BT_ASSERT(ir_maps); + BT_ASSERT(in_stream); + struct trace_ir_data_maps *d_maps; + + d_maps = borrow_data_maps_from_input_stream(ir_maps, in_stream); + /* Return the mapped stream. */ + return borrow_mapped_stream(d_maps, in_stream); +} + +static inline +bt_event_class *borrow_mapped_event_class(struct trace_ir_metadata_maps *md_maps, + const bt_event_class *in_event_class) +{ + return g_hash_table_lookup(md_maps->event_class_map, + (gpointer) in_event_class); +} + +BT_HIDDEN +bt_event_class *trace_ir_mapping_create_new_mapped_event_class( + struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class) +{ + bt_event_class *out_event_class; + const bt_trace_class *in_trace_class; + const bt_stream_class *in_stream_class; + bt_stream_class *out_stream_class; + struct trace_ir_metadata_maps *md_maps; + int ret; + + BT_LOGD("Creating new mapped event class: in-ec-addr=%p", + in_event_class); + + BT_ASSERT(ir_maps); + BT_ASSERT(in_event_class); + + in_trace_class = bt_stream_class_borrow_trace_class_const( + bt_event_class_borrow_stream_class_const( + in_event_class)); + + md_maps = borrow_metadata_maps_from_input_trace_class(ir_maps, in_trace_class); + + BT_ASSERT(!borrow_mapped_event_class(md_maps, in_event_class)); + + in_stream_class = + bt_event_class_borrow_stream_class_const(in_event_class); + BT_ASSERT(in_stream_class); + + /* Get the right output stream class to add the new event class to. */ + out_stream_class = borrow_mapped_stream_class(md_maps, in_stream_class); + BT_ASSERT(out_stream_class); + + /* Create an output event class. */ + out_event_class = bt_event_class_create_with_id(out_stream_class, + bt_event_class_get_id(in_event_class)); + if (!out_event_class) { + BT_LOGE_STR("Error creating output event class"); + goto end; + } + + /* If not, create a new one and add it to the mapping. */ + ret = copy_event_class_content(ir_maps, in_event_class, + out_event_class); + if (ret) { + BT_LOGE_STR("Error copy content to output event class"); + out_event_class = NULL; + goto end; + } + + g_hash_table_insert(md_maps->event_class_map, + (gpointer) in_event_class, out_event_class); + + BT_LOGD("Created new mapped event class: in-ec-addr=%p, out-ec-addr=%p", + in_event_class, out_event_class); + +end: + return out_event_class; +} + +BT_HIDDEN +bt_event_class *trace_ir_mapping_borrow_mapped_event_class( + struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class) +{ + struct trace_ir_metadata_maps *md_maps; + + BT_ASSERT(ir_maps); + BT_ASSERT(in_event_class); + + md_maps = borrow_metadata_maps_from_input_event_class(ir_maps, in_event_class); + + /* Return the mapped event_class. */ + return borrow_mapped_event_class(md_maps, in_event_class); +} + +static inline +bt_packet *borrow_mapped_packet(struct trace_ir_data_maps *d_maps, + const bt_packet *in_packet) +{ + BT_ASSERT(d_maps); + BT_ASSERT(in_packet); + + return g_hash_table_lookup(d_maps->packet_map, + (gpointer) in_packet); +} + +BT_HIDDEN +bt_packet *trace_ir_mapping_create_new_mapped_packet( + struct trace_ir_maps *ir_maps, + const bt_packet *in_packet) +{ + struct trace_ir_data_maps *d_maps; + const bt_trace *in_trace; + const bt_stream *in_stream; + bt_packet *out_packet; + bt_stream *out_stream; + + BT_LOGD("Creating new mapped packet: in-p-addr=%p", in_packet); + + in_stream = bt_packet_borrow_stream_const(in_packet); + in_trace = bt_stream_borrow_trace_const(in_stream); + d_maps = borrow_data_maps_from_input_trace(ir_maps, in_trace); + + /* There should never be a mapped packet. */ + BT_ASSERT(!borrow_mapped_packet(d_maps, in_packet)); + + BT_ASSERT(in_stream); + + /* Get output stream corresponding to this input stream. */ + out_stream = borrow_mapped_stream(d_maps, in_stream); + BT_ASSERT(out_stream); + + /* Create the output packet. */ + out_packet = bt_packet_create(out_stream); + if (!out_packet) { + BT_LOGE_STR("Error create output packet"); + goto end; + } + + /* + * Release our ref since the stream object will be managing the life + * time of the packet objects. + */ + copy_packet_content(in_packet, out_packet); + + g_hash_table_insert(d_maps->packet_map, + (gpointer) in_packet, out_packet); + + BT_LOGD("Created new mapped packet: in-p-addr=%p, out-p-addr=%p", + in_packet, out_packet); + +end: + return out_packet; +} + +BT_HIDDEN +bt_packet *trace_ir_mapping_borrow_mapped_packet(struct trace_ir_maps *ir_maps, + const bt_packet *in_packet) +{ + struct trace_ir_data_maps *d_maps; + BT_ASSERT(ir_maps); + BT_ASSERT(in_packet); + + d_maps = borrow_data_maps_from_input_packet(ir_maps, in_packet); + + return borrow_mapped_packet(d_maps, in_packet); +} + +BT_HIDDEN +void trace_ir_mapping_remove_mapped_packet(struct trace_ir_maps *ir_maps, + const bt_packet *in_packet) +{ + gboolean ret; + + struct trace_ir_data_maps *d_maps; + BT_ASSERT(ir_maps); + BT_ASSERT(in_packet); + + d_maps = borrow_data_maps_from_input_packet(ir_maps, in_packet); + + ret = g_hash_table_remove(d_maps->packet_map, in_packet); + + BT_ASSERT(ret); +} + +BT_HIDDEN +void trace_ir_mapping_remove_mapped_stream(struct trace_ir_maps *ir_maps, + const bt_stream *in_stream) +{ + gboolean ret; + struct trace_ir_data_maps *d_maps; + + BT_ASSERT(ir_maps); + BT_ASSERT(in_stream); + + d_maps = borrow_data_maps_from_input_stream(ir_maps, in_stream); + + ret = g_hash_table_remove(d_maps->stream_map, in_stream); + + BT_ASSERT(ret); +} + +static +void trace_ir_metadata_maps_remove_func(const bt_trace_class *in_trace_class, + void *data) +{ + struct trace_ir_maps *maps = (struct trace_ir_maps *) data; + if (maps->metadata_maps) { + gboolean ret; + ret = g_hash_table_remove(maps->metadata_maps, + (gpointer) in_trace_class); + BT_ASSERT(ret); + } +} + +static +void trace_ir_data_maps_remove_func(const bt_trace *in_trace, void *data) +{ + struct trace_ir_maps *maps = (struct trace_ir_maps *) data; + if (maps->data_maps) { + gboolean ret; + ret = g_hash_table_remove(maps->data_maps, (gpointer) in_trace); + BT_ASSERT(ret); + } +} + +struct trace_ir_data_maps *trace_ir_data_maps_create(struct trace_ir_maps *ir_maps, + const bt_trace *in_trace) +{ + struct trace_ir_data_maps *d_maps = + g_new0(struct trace_ir_data_maps, 1); + if (!d_maps) { + BT_LOGE_STR("Error allocating trace_ir_maps"); + goto error; + } + + d_maps->input_trace = in_trace; + + /* Create the hashtables used to map data objects. */ + d_maps->stream_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL,(GDestroyNotify) bt_stream_put_ref); + d_maps->packet_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL,(GDestroyNotify) bt_packet_put_ref); + + bt_trace_add_destruction_listener(in_trace, trace_ir_data_maps_remove_func, + ir_maps, &d_maps->destruction_listener_id); +error: + return d_maps; +} + +struct trace_ir_metadata_maps *trace_ir_metadata_maps_create( + struct trace_ir_maps *ir_maps, + const bt_trace_class *in_trace_class) +{ + struct trace_ir_metadata_maps *md_maps = + g_new0(struct trace_ir_metadata_maps, 1); + if (!md_maps) { + BT_LOGE_STR("Error allocating trace_ir_maps"); + goto error; + } + + md_maps->input_trace_class = in_trace_class; + /* + * Create the field class resolving context. This is needed to keep + * track of the field class already copied in order to do the field + * path resolution correctly. + */ + md_maps->fc_resolving_ctx = + g_new0(struct field_class_resolving_context, 1); + if (!md_maps->fc_resolving_ctx) { + BT_LOGE_STR("Error allocating field_class_resolving_context"); + goto error; + } + + /* Create the hashtables used to map metadata objects. */ + md_maps->stream_class_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_stream_class_put_ref); + md_maps->event_class_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_event_class_put_ref); + md_maps->field_class_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_field_class_put_ref); + md_maps->clock_class_map = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_clock_class_put_ref); + + bt_trace_class_add_destruction_listener(in_trace_class, + trace_ir_metadata_maps_remove_func, + ir_maps, &md_maps->destruction_listener_id); +error: + return md_maps; +} + +BT_HIDDEN +void trace_ir_data_maps_destroy(struct trace_ir_data_maps *maps) +{ + bt_trace_status status; + if (!maps) { + return; + } + + if (maps->packet_map) { + g_hash_table_destroy(maps->packet_map); + } + + if (maps->stream_map) { + g_hash_table_destroy(maps->stream_map); + } + + if (maps->output_trace) { + bt_trace_put_ref(maps->output_trace); + } + + status = bt_trace_remove_destruction_listener(maps->input_trace, + maps->destruction_listener_id); + if (status != BT_TRACE_STATUS_OK) { + BT_LOGD("Trace destruction listener removal failed."); + } + + g_free(maps); +} + +BT_HIDDEN +void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *maps) +{ + bt_trace_class_status status; + if (!maps) { + return; + } + + if (maps->stream_class_map) { + g_hash_table_destroy(maps->stream_class_map); + } + + if (maps->event_class_map) { + g_hash_table_destroy(maps->event_class_map); + } + + if (maps->field_class_map) { + g_hash_table_destroy(maps->field_class_map); + } + + if (maps->clock_class_map) { + g_hash_table_destroy(maps->clock_class_map); + } + + if (maps->fc_resolving_ctx) { + g_free(maps->fc_resolving_ctx); + } + + if (maps->output_trace_class) { + bt_trace_class_put_ref(maps->output_trace_class); + } + + status = bt_trace_class_remove_destruction_listener(maps->input_trace_class, + maps->destruction_listener_id); + if (status != BT_TRACE_CLASS_STATUS_OK) { + BT_LOGD("Trace destruction listener removal failed."); + } + + g_free(maps); +} + +void trace_ir_maps_clear(struct trace_ir_maps *maps) +{ + if (maps->data_maps) { + g_hash_table_remove_all(maps->data_maps); + } + + if (maps->metadata_maps) { + g_hash_table_remove_all(maps->metadata_maps); + } +} + +BT_HIDDEN +void trace_ir_maps_destroy(struct trace_ir_maps *maps) +{ + if (!maps) { + return; + } + + if (maps->debug_info_field_class_name) { + g_free(maps->debug_info_field_class_name); + } + + if (maps->data_maps) { + g_hash_table_destroy(maps->data_maps); + maps->data_maps = NULL; + } + + if (maps->metadata_maps) { + g_hash_table_destroy(maps->metadata_maps); + maps->metadata_maps = NULL; + } + + g_free(maps); +} + +BT_HIDDEN +struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp, + const char *debug_info_field_name) +{ + struct trace_ir_maps *trace_ir_maps = + g_new0(struct trace_ir_maps, 1); + if (!trace_ir_maps) { + BT_LOGE_STR("Error allocating trace_ir_maps"); + goto error; + } + + /* Copy debug info field name received from the user. */ + trace_ir_maps->debug_info_field_class_name = + g_strdup(debug_info_field_name); + if (!trace_ir_maps->debug_info_field_class_name) { + BT_LOGE_STR("Cannot copy debug info field name"); + goto error; + } + + trace_ir_maps->self_comp = self_comp; + + trace_ir_maps->data_maps = g_hash_table_new_full(g_direct_hash, + g_direct_equal, (GDestroyNotify) NULL, + (GDestroyNotify) trace_ir_data_maps_destroy); + + trace_ir_maps->metadata_maps = g_hash_table_new_full(g_direct_hash, + g_direct_equal, (GDestroyNotify) NULL, + (GDestroyNotify) trace_ir_metadata_maps_destroy); + + goto end; +error: + trace_ir_maps_destroy(trace_ir_maps); + trace_ir_maps = NULL; +end: + return trace_ir_maps; +} diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-mapping.h b/src/plugins/lttng-utils/debug-info/trace-ir-mapping.h new file mode 100644 index 00000000..9691785b --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-mapping.h @@ -0,0 +1,274 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H +/* + * Copyright 2019 Francis Deslauriers francis.deslauriers@efficios.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "common/assert.h" +#include + +#include "debug-info.h" + +/* Used to resolve field paths for dynamic arrays and variant field classes. */ +struct field_class_resolving_context { + /* Weak reference. Owned by input stream class. */ + const bt_field_class *packet_context; + /* Weak reference. Owned by input stream class. */ + const bt_field_class *event_common_context; + /* Weak reference. Owned by input event class. */ + const bt_field_class *event_specific_context; + /* Weak reference. Owned by input event class. */ + const bt_field_class *event_payload; +}; + +struct trace_ir_metadata_maps { + const bt_trace_class *input_trace_class; + bt_trace_class *output_trace_class; + + /* + * Map between input stream class and its corresponding output stream + * class. + * input stream class: weak reference. Owned by an upstream + * component. + * output stream class: owned by this structure. + */ + GHashTable *stream_class_map; + + /* + * Map between input event class and its corresponding output event + * class. + * input event class: weak reference. Owned by an upstream component. + * output event class: owned by this structure. + */ + GHashTable *event_class_map; + + /* + * Map between input field class and its corresponding output field + * class. + * input field class: weak reference. Owned by an upstream component. + * output field class: owned by this structure. + */ + GHashTable *field_class_map; + + /* + * Map between input clock class and its corresponding output clock + * class. + * input clock class: weak reference. Owned by an upstream component. + * output clock class: owned by this structure. + */ + GHashTable *clock_class_map; + + struct field_class_resolving_context *fc_resolving_ctx; + + uint64_t destruction_listener_id; +}; + +struct trace_ir_data_maps { + const bt_trace *input_trace; + bt_trace *output_trace; + + /* + * Map between input stream its corresponding output stream. + * input stream: weak reference. Owned by an upstream component. + * output stream: owned by this structure. + */ + GHashTable *stream_map; + + /* + * Map between input packet its corresponding output packet. + * input packet: weak reference. Owned by an upstream packet component. + * output packet: owned by this structure. + */ + GHashTable *packet_map; + + uint64_t destruction_listener_id; +}; + +struct trace_ir_maps { + /* + * input trace -> trace_ir_data_maps. + * input trace: weak reference. Owned by an upstream component. + * trace_ir_data_maps: Owned by this structure. + */ + GHashTable *data_maps; + + /* + * input trace class -> trace_ir_metadata_maps. + * input trace class: weak reference. Owned by an upstream component. + * trace_ir_metadata_maps: Owned by this structure. + */ + GHashTable *metadata_maps; + + char *debug_info_field_class_name; + + bt_self_component *self_comp; +}; + +BT_HIDDEN +struct trace_ir_maps *trace_ir_maps_create(bt_self_component *self_comp, + const char *debug_info_field_name); + +BT_HIDDEN +void trace_ir_maps_clear(struct trace_ir_maps *maps); + +BT_HIDDEN +void trace_ir_maps_destroy(struct trace_ir_maps *maps); + +BT_HIDDEN +struct trace_ir_data_maps *trace_ir_data_maps_create( + struct trace_ir_maps *ir_maps, + const bt_trace *in_trace); + +BT_HIDDEN +void trace_ir_data_maps_destroy(struct trace_ir_data_maps *d_maps); + +BT_HIDDEN +struct trace_ir_metadata_maps *trace_ir_metadata_maps_create( + struct trace_ir_maps *ir_maps, + const bt_trace_class *in_trace_class); + +BT_HIDDEN +void trace_ir_metadata_maps_destroy(struct trace_ir_metadata_maps *md_maps); + +BT_HIDDEN +bt_stream *trace_ir_mapping_create_new_mapped_stream( + struct trace_ir_maps *ir_maps, + const bt_stream *in_stream); + +BT_HIDDEN +bt_stream *trace_ir_mapping_borrow_mapped_stream( + struct trace_ir_maps *ir_maps, + const bt_stream *in_stream); + +BT_HIDDEN +void trace_ir_mapping_remove_mapped_stream( + struct trace_ir_maps *ir_maps, + const bt_stream *in_stream); + +BT_HIDDEN +bt_event_class *trace_ir_mapping_create_new_mapped_event_class( + struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class); + +BT_HIDDEN +bt_event_class *trace_ir_mapping_borrow_mapped_event_class( + struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class); + +BT_HIDDEN +bt_packet *trace_ir_mapping_create_new_mapped_packet( + struct trace_ir_maps *ir_maps, + const bt_packet *in_packet); + +BT_HIDDEN +bt_packet *trace_ir_mapping_borrow_mapped_packet( + struct trace_ir_maps *ir_maps, + const bt_packet *in_packet); + +BT_HIDDEN +void trace_ir_mapping_remove_mapped_packet( + struct trace_ir_maps *ir_maps, + const bt_packet *in_packet); + +static inline +struct trace_ir_data_maps *borrow_data_maps_from_input_trace( + struct trace_ir_maps *ir_maps, const bt_trace *in_trace) +{ + BT_ASSERT(ir_maps); + BT_ASSERT(in_trace); + + struct trace_ir_data_maps *d_maps = + g_hash_table_lookup(ir_maps->data_maps, (gpointer) in_trace); + if (!d_maps) { + d_maps = trace_ir_data_maps_create(ir_maps, in_trace); + g_hash_table_insert(ir_maps->data_maps, (gpointer) in_trace, d_maps); + } + + return d_maps; +} + +static inline +struct trace_ir_data_maps *borrow_data_maps_from_input_stream( + struct trace_ir_maps *ir_maps, const bt_stream *in_stream) +{ + BT_ASSERT(ir_maps); + BT_ASSERT(in_stream); + + return borrow_data_maps_from_input_trace(ir_maps, + bt_stream_borrow_trace_const(in_stream)); +} + +static inline +struct trace_ir_data_maps *borrow_data_maps_from_input_packet( + struct trace_ir_maps *ir_maps, const bt_packet *in_packet) +{ + BT_ASSERT(ir_maps); + BT_ASSERT(in_packet); + + return borrow_data_maps_from_input_stream(ir_maps, + bt_packet_borrow_stream_const(in_packet)); +} + +static inline +struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_trace_class( + struct trace_ir_maps *ir_maps, + const bt_trace_class *in_trace_class) +{ + BT_ASSERT(ir_maps); + BT_ASSERT(in_trace_class); + + struct trace_ir_metadata_maps *md_maps = + g_hash_table_lookup(ir_maps->metadata_maps, + (gpointer) in_trace_class); + if (!md_maps) { + md_maps = trace_ir_metadata_maps_create(ir_maps, in_trace_class); + g_hash_table_insert(ir_maps->metadata_maps, + (gpointer) in_trace_class, md_maps); + } + + return md_maps; +} + +static inline +struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_stream_class( + struct trace_ir_maps *ir_maps, + const bt_stream_class *in_stream_class) { + + BT_ASSERT(in_stream_class); + + return borrow_metadata_maps_from_input_trace_class(ir_maps, + bt_stream_class_borrow_trace_class_const(in_stream_class)); +} + +static inline +struct trace_ir_metadata_maps *borrow_metadata_maps_from_input_event_class( + struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class) { + + BT_ASSERT(in_event_class); + + return borrow_metadata_maps_from_input_stream_class(ir_maps, + bt_event_class_borrow_stream_class_const(in_event_class)); +} + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_IR_MAPPING_H */ diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c new file mode 100644 index 00000000..92bc3de8 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.c @@ -0,0 +1,634 @@ +/* + * Babeltrace - Trace IR metadata object copy + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-METADATA-COPY" +#include "logging.h" + +#include +#include + +#include "common/assert.h" + +#include "trace-ir-metadata-copy.h" +#include "trace-ir-metadata-field-class-copy.h" +#include "utils.h" + +BT_HIDDEN +int copy_trace_class_content(const bt_trace_class *in_trace_class, + bt_trace_class *out_trace_class) +{ + int ret = 0; + uint64_t i, env_field_count; + const char *in_trace_class_name; + + BT_LOGD("Copying content of trace class: in-tc-addr=%p, out-tc-addr=%p", + in_trace_class, out_trace_class); + + /* Use the same stream class ids as in the origin trace class. */ + bt_trace_class_set_assigns_automatic_stream_class_id(out_trace_class, + BT_FALSE); + + in_trace_class_name = bt_trace_class_get_name(in_trace_class); + if (in_trace_class_name) { + bt_trace_class_set_name(out_trace_class, in_trace_class_name); + } + + /* + * Do not copy the trace class UUID as it may be modified and should no + * longer have the same UUID. + */ + + /* + * Go over all the entries in the environment section of the trace class + * and copy the content to the new trace class. + */ + env_field_count = bt_trace_class_get_environment_entry_count(in_trace_class); + for (i = 0; i < env_field_count; i++) { + const char *value_name; + const bt_value *value = NULL; + bt_trace_class_status trace_class_status; + + bt_trace_class_borrow_environment_entry_by_index_const( + in_trace_class, i, &value_name, &value); + + BT_LOGD("Copying trace class environnement entry: " + "index=%" PRId64 ", value-addr=%p, value-name=%s", + i, value, value_name); + + BT_ASSERT(value_name); + BT_ASSERT(value); + + if (bt_value_is_signed_integer(value)) { + trace_class_status = + bt_trace_class_set_environment_entry_integer( + out_trace_class, value_name, + bt_value_signed_integer_get( + value)); + } else if (bt_value_is_string(value)) { + trace_class_status = + bt_trace_class_set_environment_entry_string( + out_trace_class, value_name, + bt_value_string_get(value)); + } else { + abort(); + } + + if (trace_class_status != BT_TRACE_CLASS_STATUS_OK) { + ret = -1; + goto error; + } + } + + BT_LOGD("Copied content of trace class: in-tc-addr=%p, out-tc-addr=%p", + in_trace_class, out_trace_class); +error: + return ret; +} + +static +int copy_clock_class_content(const bt_clock_class *in_clock_class, + bt_clock_class *out_clock_class) +{ + bt_clock_class_status status; + const char *clock_class_name, *clock_class_description; + int64_t seconds; + uint64_t cycles; + bt_uuid in_uuid; + int ret = 0; + + BT_LOGD("Copying content of clock class: in-cc-addr=%p, out-cc-addr=%p", + in_clock_class, out_clock_class); + + clock_class_name = bt_clock_class_get_name(in_clock_class); + + if (clock_class_name) { + status = bt_clock_class_set_name(out_clock_class, clock_class_name); + if (status != BT_CLOCK_CLASS_STATUS_OK) { + BT_LOGE("Error setting clock class' name cc-addr=%p, name=%p", + out_clock_class, clock_class_name); + out_clock_class = NULL; + ret = -1; + goto error; + } + } + + clock_class_description = bt_clock_class_get_description(in_clock_class); + + if (clock_class_description) { + status = bt_clock_class_set_description(out_clock_class, + clock_class_description); + if (status != BT_CLOCK_CLASS_STATUS_OK) { + BT_LOGE("Error setting clock class' description cc-addr=%p, " + "name=%p", out_clock_class, clock_class_description); + out_clock_class = NULL; + ret = -1; + goto error; + } + } + + in_uuid = bt_clock_class_get_uuid(in_clock_class); + if (in_uuid) { + bt_clock_class_set_uuid(out_clock_class, in_uuid); + } + + bt_clock_class_set_frequency(out_clock_class, + bt_clock_class_get_frequency(in_clock_class)); + bt_clock_class_set_precision(out_clock_class, + bt_clock_class_get_precision(in_clock_class)); + bt_clock_class_get_offset(in_clock_class, &seconds, &cycles); + bt_clock_class_set_offset(out_clock_class, seconds, cycles); + bt_clock_class_set_origin_is_unix_epoch(out_clock_class, + bt_clock_class_origin_is_unix_epoch(in_clock_class)); + + BT_LOGD("Copied content of clock class: in-cc-addr=%p, out-cc-addr=%p", + in_clock_class, out_clock_class); + +error: + return ret; +} + +static +bt_clock_class *borrow_mapped_clock_class( + struct trace_ir_metadata_maps *md_maps, + const bt_clock_class *in_clock_class) +{ + BT_ASSERT(md_maps); + BT_ASSERT(in_clock_class); + + return g_hash_table_lookup(md_maps->clock_class_map, + (gpointer) in_clock_class); +} + +static +bt_clock_class *create_new_mapped_clock_class( + bt_self_component *self_comp, + struct trace_ir_metadata_maps *md_maps, + const bt_clock_class *in_clock_class) +{ + bt_clock_class *out_clock_class; + int ret; + + BT_LOGD("Creating new mapped clock class: in-cc-addr=%p", + in_clock_class); + + BT_ASSERT(md_maps); + BT_ASSERT(in_clock_class); + + BT_ASSERT(!borrow_mapped_clock_class(md_maps, in_clock_class)); + + out_clock_class = bt_clock_class_create(self_comp); + if (!out_clock_class) { + BT_LOGE_STR("Cannot create clock class"); + goto end; + } + /* If not, create a new one and add it to the mapping. */ + ret = copy_clock_class_content(in_clock_class, out_clock_class); + if (ret) { + BT_LOGE_STR("Cannot copy clock class"); + goto end; + } + + g_hash_table_insert(md_maps->clock_class_map, + (gpointer) in_clock_class, out_clock_class); + + BT_LOGD("Created new mapped clock class: in-cc-addr=%p, out-cc-addr=%p", + in_clock_class, out_clock_class); +end: + return out_clock_class; +} + +BT_HIDDEN +int copy_stream_class_content(struct trace_ir_maps *ir_maps, + const bt_stream_class *in_stream_class, + bt_stream_class *out_stream_class) +{ + struct trace_ir_metadata_maps *md_maps; + const bt_clock_class *in_clock_class; + bt_clock_class *out_clock_class; + const bt_field_class *in_packet_context_fc, *in_common_context_fc; + bt_field_class *out_packet_context_fc, *out_common_context_fc; + bt_stream_class_status status; + const char *in_name; + int ret = 0; + + BT_LOGD("Copying content of stream class: in-sc-addr=%p, out-sc-addr=%p", + in_stream_class, out_stream_class); + + md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, in_stream_class); + in_clock_class = bt_stream_class_borrow_default_clock_class_const( + in_stream_class); + + if (in_clock_class) { + /* Copy the clock class. */ + out_clock_class = + borrow_mapped_clock_class(md_maps, in_clock_class); + if (!out_clock_class) { + out_clock_class = create_new_mapped_clock_class( + ir_maps->self_comp, md_maps, + in_clock_class); + } + bt_stream_class_set_default_clock_class(out_stream_class, + out_clock_class); + + } + + bt_stream_class_set_packets_have_beginning_default_clock_snapshot( + out_stream_class, + bt_stream_class_packets_have_beginning_default_clock_snapshot( + in_stream_class)); + bt_stream_class_set_packets_have_end_default_clock_snapshot( + out_stream_class, + bt_stream_class_packets_have_end_default_clock_snapshot( + in_stream_class)); + bt_stream_class_set_supports_discarded_events( + out_stream_class, + bt_stream_class_supports_discarded_events(in_stream_class), + bt_stream_class_discarded_events_have_default_clock_snapshots( + in_stream_class)); + bt_stream_class_set_supports_discarded_packets( + out_stream_class, + bt_stream_class_supports_discarded_packets(in_stream_class), + bt_stream_class_discarded_packets_have_default_clock_snapshots( + in_stream_class)); + + in_name = bt_stream_class_get_name(in_stream_class); + if (in_name) { + status = bt_stream_class_set_name(out_stream_class, in_name); + if (status != BT_STREAM_CLASS_STATUS_OK) { + BT_LOGE("Error set stream class name: out-sc-addr=%p, " + "name=%s", out_stream_class, in_name); + ret = -1; + goto error; + } + } + + bt_stream_class_set_assigns_automatic_stream_id(out_stream_class, + BT_FALSE); + bt_stream_class_set_assigns_automatic_event_class_id(out_stream_class, + BT_FALSE); + + /* + * Add the input packet context field class to the context to + * resolution in the further steps. + */ + in_packet_context_fc = + bt_stream_class_borrow_packet_context_field_class_const( + in_stream_class); + md_maps->fc_resolving_ctx->packet_context = + in_packet_context_fc; + + if (in_packet_context_fc) { + /* Copy packet context. */ + out_packet_context_fc = create_field_class_copy( + md_maps, in_packet_context_fc); + + ret = copy_field_class_content(md_maps, + in_packet_context_fc, out_packet_context_fc); + if (ret) { + ret = -1; + goto error; + } + + status = bt_stream_class_set_packet_context_field_class( + out_stream_class, out_packet_context_fc); + if (status != BT_STREAM_CLASS_STATUS_OK) { + BT_LOGE("Error setting stream class' packet context " + "field class: sc-addr=%p, packet-fc-addr=%p", + out_stream_class, out_packet_context_fc); + ret = -1; + goto error; + } + } + + /* + * Add the input common context field class to the context to + * resolution in the further steps. + */ + in_common_context_fc = + bt_stream_class_borrow_event_common_context_field_class_const( + in_stream_class); + md_maps->fc_resolving_ctx->event_common_context = + in_common_context_fc; + + if (in_common_context_fc) { + /* Copy common context. */ + /* TODO: I find it a bit awkward to have this special function + * here to add the debug-info field class. I would like to + * abstract that.*/ + out_common_context_fc = create_field_class_copy( + md_maps, in_common_context_fc); + + ret = copy_event_common_context_field_class_content( + md_maps, ir_maps->debug_info_field_class_name, + in_common_context_fc, out_common_context_fc); + if (ret) { + goto error; + } + + status = bt_stream_class_set_event_common_context_field_class( + out_stream_class, out_common_context_fc); + if (status != BT_STREAM_CLASS_STATUS_OK) { + BT_LOGE("Error setting stream class' packet context " + "field class: sc-addr=%p, packet-fc-addr=%p", + out_stream_class, out_common_context_fc); + ret = -1; + goto error; + } + } + + /* Set packet snapshot boolean fields. */ + BT_LOGD("Copied content of stream class: in-sc-addr=%p, out-sc-addr=%p", + in_stream_class, out_stream_class); +error: + return ret; +} + +BT_HIDDEN +int copy_event_class_content(struct trace_ir_maps *ir_maps, + const bt_event_class *in_event_class, + bt_event_class *out_event_class) +{ + struct trace_ir_metadata_maps *md_maps; + const char *in_event_class_name, *in_emf_uri; + bt_property_availability prop_avail; + bt_event_class_log_level log_level; + bt_event_class_status status; + bt_field_class *out_specific_context_fc, *out_payload_fc; + const bt_field_class *in_event_specific_context, *in_event_payload; + int ret = 0; + + BT_LOGD("Copying content of event class: in-ec-addr=%p, out-ec-addr=%p", + in_event_class, out_event_class); + + /* Copy event class name. */ + in_event_class_name = bt_event_class_get_name(in_event_class); + if (in_event_class_name) { + status = bt_event_class_set_name(out_event_class, in_event_class_name); + if (status != BT_EVENT_CLASS_STATUS_OK) { + BT_LOGE("Error setting event class' name: ec-addr=%p, " + "name=%s", out_event_class, in_event_class_name); + ret = -1; + goto error; + } + } + + /* Copy event class loglevel. */ + prop_avail = bt_event_class_get_log_level(in_event_class, &log_level); + if (prop_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { + bt_event_class_set_log_level(out_event_class, + log_level); + } + + /* Copy event class emf uri. */ + in_emf_uri = bt_event_class_get_emf_uri(in_event_class); + if (in_emf_uri) { + status = bt_event_class_set_emf_uri(out_event_class, in_emf_uri); + if (status != BT_EVENT_CLASS_STATUS_OK) { + BT_LOGE("Error setting event class' emf uri: ec-addr=%p, " + "emf uri=%s", out_event_class, in_emf_uri); + ret = -1; + goto error; + } + } + + md_maps = borrow_metadata_maps_from_input_event_class(ir_maps, in_event_class); + /* + * Add the input event class' specific ctx to te + * context. + */ + in_event_specific_context = + bt_event_class_borrow_specific_context_field_class_const( + in_event_class); + + md_maps->fc_resolving_ctx->event_specific_context = + in_event_specific_context; + + if (in_event_specific_context) { + /* Copy the specific context of this event class. */ + out_specific_context_fc = create_field_class_copy(md_maps, + in_event_specific_context); + + ret = copy_field_class_content(md_maps, + in_event_specific_context, out_specific_context_fc); + if (ret) { + goto error; + } + /* + * Add the output specific context to the output event + * class. + */ + status = bt_event_class_set_specific_context_field_class( + out_event_class, out_specific_context_fc); + if (status != BT_EVENT_CLASS_STATUS_OK) { + BT_LOGE("Error setting event class' specific context " + "field class: ec-addr=%p, ctx-fc-addr=%p", + out_event_class, out_specific_context_fc); + ret = -1; + goto error; + } + } + + /* + * Add the input event class' payload field class to + * the context. + */ + in_event_payload = bt_event_class_borrow_payload_field_class_const( + in_event_class); + + md_maps->fc_resolving_ctx->event_payload = in_event_payload; + + if (in_event_payload) { + /* Copy the payload of this event class. */ + out_payload_fc = create_field_class_copy(md_maps, + in_event_payload); + ret = copy_field_class_content(md_maps, + in_event_payload, out_payload_fc); + if (ret) { + goto error; + } + + /* Add the output payload to the output event class. */ + status = bt_event_class_set_payload_field_class( + out_event_class, out_payload_fc); + if (status != BT_EVENT_CLASS_STATUS_OK) { + BT_LOGE("Error setting event class' payload " + "field class: ec-addr=%p, payload-fc-addr=%p", + out_event_class, out_payload_fc); + ret = -1; + goto error; + } + } + + BT_LOGD("Copied content of event class: in-ec-addr=%p, out-ec-addr=%p", + in_event_class, out_event_class); +error: + return ret; +} + +BT_HIDDEN +int copy_event_common_context_field_class_content( + struct trace_ir_metadata_maps *md_maps, + const char *debug_info_fc_name, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + bt_field_class_status status; + bt_field_class *debug_field_class = NULL, *bin_field_class = NULL, + *func_field_class = NULL, *src_field_class = NULL; + int ret = 0; + + BT_LOGD("Copying content of event common context field class: " + "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class); + + /* Copy the content of the input common context. */ + ret = copy_field_class_content(md_maps, in_field_class, out_field_class); + if (ret) { + goto error; + } + + /* + * If this event common context has the necessary fields to compute the + * debug information append the debug-info field class to the event + * common context. + */ + if (is_event_common_ctx_dbg_info_compatible(in_field_class, debug_info_fc_name)) { + /* + * The struct field and 3 sub-fields are not stored in the + * field class map because they don't have input equivalent. + * We need to put our reference each of these field classes + * once they are added to their respective containing field + * classes. + */ + debug_field_class = bt_field_class_structure_create( + md_maps->output_trace_class); + if (!debug_field_class) { + BT_LOGE_STR("Failed to create debug_info structure."); + ret = -1; + goto error; + } + + bin_field_class = bt_field_class_string_create( + md_maps->output_trace_class); + if (!bin_field_class) { + BT_LOGE_STR("Failed to create string for field=bin."); + ret = -1; + goto error; + } + + func_field_class = bt_field_class_string_create( + md_maps->output_trace_class); + if (!func_field_class) { + BT_LOGE_STR("Failed to create string for field=func."); + ret = -1; + goto error; + } + + src_field_class = bt_field_class_string_create( + md_maps->output_trace_class); + if (!src_field_class) { + BT_LOGE_STR("Failed to create string for field=src."); + ret = -1; + goto error; + } + + status = bt_field_class_structure_append_member( + debug_field_class, "bin", bin_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add a field to debug_info " + "struct: field=bin."); + ret = -1; + goto error; + } + BT_FIELD_CLASS_PUT_REF_AND_RESET(bin_field_class); + + status = bt_field_class_structure_append_member( + debug_field_class, "func", func_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add a field to debug_info " + "struct: field=func."); + ret = -1; + goto error; + } + BT_FIELD_CLASS_PUT_REF_AND_RESET(func_field_class); + + status = bt_field_class_structure_append_member( + debug_field_class, "src", src_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add a field to debug_info " + "struct: field=src."); + ret = -1; + goto error; + } + BT_FIELD_CLASS_PUT_REF_AND_RESET(src_field_class); + + /*Add the filled debug-info field class to the common context. */ + status = bt_field_class_structure_append_member(out_field_class, + debug_info_fc_name, + debug_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add debug_info field to " + "event common context."); + ret = -1; + goto error; + } + BT_FIELD_CLASS_PUT_REF_AND_RESET(debug_field_class); + } + BT_LOGD("Copied content of event common context field class: " + "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class); + goto end; + +error: + if (debug_field_class) { + bt_field_class_put_ref(debug_field_class); + } + if (bin_field_class) { + bt_field_class_put_ref(bin_field_class); + } + if (func_field_class) { + bt_field_class_put_ref(func_field_class); + } + if (src_field_class) { + bt_field_class_put_ref(src_field_class); + } +end: + return ret; +} + +BT_HIDDEN +bt_field_class *create_field_class_copy(struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class) +{ + return create_field_class_copy_internal(md_maps, in_field_class); +} + +BT_HIDDEN +int copy_field_class_content(struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + return copy_field_class_content_internal(md_maps, in_field_class, + out_field_class); +} diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h new file mode 100644 index 00000000..9330546d --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-copy.h @@ -0,0 +1,64 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H + +/* + * Babeltrace - Trace IR metadata object copy + * + * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "trace-ir-mapping.h" + +BT_HIDDEN +int copy_trace_class_content(const bt_trace_class *in_trace_class, + bt_trace_class *out_trace_class); + +BT_HIDDEN +int copy_stream_class_content(struct trace_ir_maps *trace_ir_maps, + const bt_stream_class *in_stream_class, + bt_stream_class *out_stream_class); + +BT_HIDDEN +int copy_event_class_content(struct trace_ir_maps *trace_ir_maps, + const bt_event_class *in_event_class, + bt_event_class *out_event_class); + +BT_HIDDEN +int copy_field_class_content(struct trace_ir_metadata_maps *trace_ir_metadata_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class); + +BT_HIDDEN +int copy_event_common_context_field_class_content( + struct trace_ir_metadata_maps *trace_ir_metadata_maps, + const char *debug_info_field_class_name, + const bt_field_class *in_field_class, + bt_field_class *out_field_class); + +BT_HIDDEN +bt_field_class *create_field_class_copy( + struct trace_ir_metadata_maps *trace_ir_metadata_maps, + const bt_field_class *in_field_class); + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_TRACE_METADATA_COPY_H */ diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c new file mode 100644 index 00000000..70242d66 --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.c @@ -0,0 +1,765 @@ +/* + * Babeltrace - Trace IR field copy + * + * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2018 Philippe Proulx + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-LTTNG-UTILS-DEBUG-INFO-TRACE-IR-METADATA-FC-COPY" +#include "logging.h" + +#include "common/assert.h" +#include "common/common.h" +#include "compat/compiler.h" +#include + +#include "trace-ir-metadata-copy.h" +#include "trace-ir-metadata-field-class-copy.h" + +/* + * This fonction walks througth the nested structures field class to resolve a + * field path object. A field path is made of indexes inside possibly nested + * structures ultimately leading to a field class. + */ +static +const bt_field_class *walk_field_path(const bt_field_path *fp, + const bt_field_class *fc) +{ + uint64_t i, fp_item_count; + const bt_field_class *curr_fc; + + BT_ASSERT(bt_field_class_get_type(fc) == BT_FIELD_CLASS_TYPE_STRUCTURE); + BT_LOGD("Walking field path on field class: fp-addr=%p, fc-addr=%p", + fp, fc); + + fp_item_count = bt_field_path_get_item_count(fp); + curr_fc = fc; + for (i = 0; i < fp_item_count; i++) { + bt_field_class_type fc_type = bt_field_class_get_type(curr_fc); + const bt_field_path_item *fp_item = + bt_field_path_borrow_item_by_index_const(fp, i); + + switch (fc_type) { + case BT_FIELD_CLASS_TYPE_STRUCTURE: + { + const bt_field_class_structure_member *member; + + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + member = bt_field_class_structure_borrow_member_by_index_const( + curr_fc, + bt_field_path_item_index_get_index(fp_item)); + curr_fc = bt_field_class_structure_member_borrow_field_class_const( + member); + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + { + const bt_field_class_variant_option *option; + + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_INDEX); + option = bt_field_class_variant_borrow_option_by_index_const( + curr_fc, + bt_field_path_item_index_get_index(fp_item)); + curr_fc = bt_field_class_variant_option_borrow_field_class_const( + option); + break; + } + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + BT_ASSERT(bt_field_path_item_get_type(fp_item) == + BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT); + curr_fc = bt_field_class_array_borrow_element_field_class_const( + curr_fc); + break; + } + default: + abort(); + } + } + + return curr_fc; +} + +static +const bt_field_class *resolve_field_path_to_field_class(const bt_field_path *fp, + struct trace_ir_metadata_maps *md_maps) +{ + struct field_class_resolving_context *fc_resolving_ctx; + const bt_field_class *fc; + bt_scope fp_scope; + + BT_LOGD("Resolving field path: fp-addr=%p", fp); + + fc_resolving_ctx = md_maps->fc_resolving_ctx; + fp_scope = bt_field_path_get_root_scope(fp); + + switch (fp_scope) { + case BT_SCOPE_PACKET_CONTEXT: + fc = walk_field_path(fp, fc_resolving_ctx->packet_context); + break; + case BT_SCOPE_EVENT_COMMON_CONTEXT: + fc = walk_field_path(fp, fc_resolving_ctx->event_common_context); + break; + case BT_SCOPE_EVENT_SPECIFIC_CONTEXT: + fc = walk_field_path(fp, fc_resolving_ctx->event_specific_context); + break; + case BT_SCOPE_EVENT_PAYLOAD: + fc = walk_field_path(fp, fc_resolving_ctx->event_payload); + break; + default: + abort(); + } + + return fc; +} + +static inline +void field_class_integer_set_props(const bt_field_class *input_fc, + bt_field_class *output_fc) +{ + bt_field_class_integer_set_preferred_display_base(output_fc, + bt_field_class_integer_get_preferred_display_base(input_fc)); + bt_field_class_integer_set_field_value_range(output_fc, + bt_field_class_integer_get_field_value_range(input_fc)); +} + +static inline +int field_class_unsigned_integer_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + BT_LOGD("Copying content of unsigned integer field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + field_class_integer_set_props(in_field_class, out_field_class); + + BT_LOGD("Copied content of unsigned integer field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + return 0; +} + +static inline +int field_class_signed_integer_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + BT_LOGD("Copying content of signed integer field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + field_class_integer_set_props(in_field_class, out_field_class); + + BT_LOGD("Copied content of signed integer field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + return 0; +} + +BT_HIDDEN +int field_class_unsigned_enumeration_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + uint64_t i, enum_mapping_count; + int ret = 0; + + BT_LOGD("Copying content of unsigned enumeration field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + /* Copy properties of the inner integer. */ + field_class_integer_set_props(in_field_class, out_field_class); + + /* Copy all enumeration entries. */ + enum_mapping_count = bt_field_class_enumeration_get_mapping_count(in_field_class); + for (i = 0; i < enum_mapping_count; i++) { + const char *label; + const bt_field_class_unsigned_enumeration_mapping *u_mapping; + const bt_field_class_enumeration_mapping *mapping; + uint64_t range_index, range_count; + + /* Get the ranges and the range count. */ + u_mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const( + in_field_class, i); + mapping = bt_field_class_unsigned_enumeration_mapping_as_mapping_const( + u_mapping); + range_count = + bt_field_class_enumeration_mapping_get_range_count( + mapping); + label = bt_field_class_enumeration_mapping_get_label( + mapping); + + /* + * Iterate over all the ranges to add them to copied field + * class. + */ + for (range_index = 0; range_index < range_count; range_index++) { + uint64_t lower, upper; + bt_field_class_status status; + bt_field_class_unsigned_enumeration_mapping_get_range_by_index( + u_mapping, range_index, &lower, &upper); + + BT_LOGD("Copying range in enumeration field class: " + "label=%s, lower=%"PRId64", upper=%"PRId64, + label, lower, upper); + + /* Add the label and its range to the copy field class. */ + status = bt_field_class_unsigned_enumeration_map_range( + out_field_class, label, lower, upper); + + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add range to unsigned " + "enumeration."); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_field_class); + ret = -1; + goto error; + } + } + } + + BT_LOGD("Copied content of unsigned enumeration field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + +error: + return ret; +} + +static inline +int field_class_signed_enumeration_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + uint64_t i, enum_mapping_count; + int ret = 0; + + BT_LOGD("Copying content of signed enumeration field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + /* Copy properties of the inner integer. */ + field_class_integer_set_props(in_field_class, out_field_class); + + /* Copy all enumeration entries. */ + enum_mapping_count = + bt_field_class_enumeration_get_mapping_count(in_field_class); + for (i = 0; i < enum_mapping_count; i++) { + const char *label; + const bt_field_class_signed_enumeration_mapping *i_mapping; + const bt_field_class_enumeration_mapping *mapping; + uint64_t range_index, range_count; + + /* Get the ranges and the range count. */ + i_mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const( + in_field_class, i); + mapping = bt_field_class_signed_enumeration_mapping_as_mapping_const( + i_mapping); + range_count = + bt_field_class_enumeration_mapping_get_range_count( + mapping); + label = bt_field_class_enumeration_mapping_get_label( + mapping); + + /* + * Iterate over all the ranges to add them to copied field + * class. + */ + for (range_index = 0; range_index < range_count; range_index++) { + int64_t lower, upper; + bt_field_class_status status; + bt_field_class_signed_enumeration_mapping_get_range_by_index( + i_mapping, range_index, &lower, &upper); + + BT_LOGD("Copying range in enumeration field class: " + "label=%s, lower=%"PRId64", upper=%"PRId64, + label, lower, upper); + + /* Add the label and its range to the copy field class. */ + status = bt_field_class_signed_enumeration_map_range( + out_field_class, label, lower, upper); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Failed to add range to signed " + "enumeration."); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_field_class); + ret = -1; + goto error; + } + } + } + + BT_LOGD("Copied content of signed enumeration field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + +error: + return ret; +} + +static inline +int field_class_real_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + BT_LOGD("Copying content of real field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + bt_field_class_real_set_is_single_precision(out_field_class, + bt_field_class_real_is_single_precision(in_field_class)); + + BT_LOGD("Copied content real field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + + return 0; +} + +static inline +int field_class_structure_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + uint64_t i, struct_member_count; + bt_field_class_status status; + int ret = 0; + + BT_LOGD("Copying content of structure field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + /* Get the number of member in that struct. */ + struct_member_count = + bt_field_class_structure_get_member_count(in_field_class); + + /* Iterate over all the members of the struct. */ + for (i = 0; i < struct_member_count; i++) { + const bt_field_class_structure_member *member; + const char *member_name; + const bt_field_class *member_fc; + bt_field_class *out_member_field_class; + + member = bt_field_class_structure_borrow_member_by_index_const( + in_field_class, i); + member_fc = bt_field_class_structure_member_borrow_field_class_const( + member); + member_name = bt_field_class_structure_member_get_name(member); + BT_LOGD("Copying structure field class's field: " + "index=%" PRId64 ", " + "member-fc-addr=%p, field-name=\"%s\"", + i, member_fc, member_name); + + out_member_field_class = create_field_class_copy(md_maps, + member_fc); + if (!out_member_field_class) { + BT_LOGE("Cannot copy structure field class's field: " + "index=%" PRId64 ", " + "field-fc-addr=%p, field-name=\"%s\"", + i, member_fc, member_name); + ret = -1; + goto error; + } + ret = copy_field_class_content(md_maps, member_fc, + out_member_field_class); + if (ret) { + goto error; + } + + status = bt_field_class_structure_append_member(out_field_class, + member_name, out_member_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE("Cannot append structure field class's field: " + "index=%" PRId64 ", " + "field-fc-addr=%p, field-name=\"%s\"", + i, member_fc, member_name); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_member_field_class); + ret = -1; + goto error; + } + } + + BT_LOGD("Copied structure field class: original-fc-addr=%p, copy-fc-addr=%p", + in_field_class, out_field_class); + +error: + return ret; +} + +static inline +int field_class_variant_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + bt_field_class *out_tag_field_class = NULL; + uint64_t i, variant_option_count; + const bt_field_path *tag_fp; + const bt_field_class *tag_fc; + int ret = 0; + + BT_LOGD("Copying content of variant field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + tag_fp = bt_field_class_variant_borrow_selector_field_path_const( + in_field_class); + if (tag_fp) { + tag_fc = resolve_field_path_to_field_class(tag_fp, + md_maps); + + out_tag_field_class = g_hash_table_lookup( + md_maps->field_class_map, tag_fc); + if (!out_tag_field_class) { + BT_LOGE_STR("Cannot find the tag field class."); + ret = -1; + goto error; + } + bt_field_class_variant_set_selector_field_class(out_field_class, + out_tag_field_class); + } + + variant_option_count = + bt_field_class_variant_get_option_count(in_field_class); + for (i = 0; i < variant_option_count; i++) { + const bt_field_class *option_fc; + const char *option_name; + bt_field_class *out_option_field_class; + bt_field_class_status status; + const bt_field_class_variant_option *option; + + option = bt_field_class_variant_borrow_option_by_index_const( + in_field_class, i); + option_fc = bt_field_class_variant_option_borrow_field_class_const( + option); + option_name = bt_field_class_variant_option_get_name(option); + out_option_field_class = create_field_class_copy_internal( + md_maps, option_fc); + if (!out_option_field_class) { + BT_LOGE_STR("Cannot copy field class."); + ret = -1; + goto error; + } + ret = copy_field_class_content_internal(md_maps, option_fc, + out_option_field_class); + if (ret) { + BT_LOGE_STR("Error copying content of option variant " + "field class'"); + goto error; + } + + status = bt_field_class_variant_append_option( + out_field_class, option_name, + out_option_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Cannot append option to variant field class'"); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_tag_field_class); + ret = -1; + goto error; + } + } + + BT_LOGD("Copied content of variant field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + +error: + return ret; +} + +static inline +int field_class_static_array_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + BT_LOGD("Copying content of static array field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + /* + * There is no content to copy. Keep this function call anyway for + * logging purposes. + */ + BT_LOGD("Copied content of static array field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + + return 0; +} + +static inline +int field_class_dynamic_array_copy( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + const bt_field_class *len_fc; + const bt_field_path *len_fp; + bt_field_class_status status; + bt_field_class *out_len_field_class; + int ret = 0; + + BT_LOGD("Copying content of dynamic array field class: " + "in-fc-addr=%p, out-fc-addr=%p", + in_field_class, out_field_class); + + len_fp = bt_field_class_dynamic_array_borrow_length_field_path_const( + in_field_class); + + if (len_fp) { + BT_LOGD("Copying dynamic array length field class using " + "field path: in-len-fp=%p", len_fp); + len_fc = resolve_field_path_to_field_class( + len_fp, md_maps); + out_len_field_class = g_hash_table_lookup( + md_maps->field_class_map, len_fc); + if (!out_len_field_class) { + BT_LOGE_STR("Cannot find the output matching length" + "field class."); + ret = -1; + goto error; + } + + status = bt_field_class_dynamic_array_set_length_field_class( + out_field_class, out_len_field_class); + if (status != BT_FIELD_CLASS_STATUS_OK) { + BT_LOGE_STR("Cannot set dynamic array field class' " + "length field class."); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_len_field_class); + ret = -1; + goto error; + } + } + + BT_LOGD("Copied dynamic array field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + +error: + return ret; +} + +static inline +int field_class_string_copy(struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + BT_LOGD("Copying content of string field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + /* + * There is no content to copy. Keep this function call anyway for + * logging purposes. + */ + BT_LOGD("Copied content of string field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + + return 0; +} + +static +bt_field_class *copy_field_class_array_element(struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_elem_fc) +{ + int ret; + bt_field_class *out_elem_fc = + create_field_class_copy_internal(md_maps, in_elem_fc); + if (!out_elem_fc) { + BT_LOGE("Error creating output elem field class " + "from input elem field class for static array: " + "in-fc-addr=%p", in_elem_fc); + goto error; + } + + ret = copy_field_class_content_internal(md_maps, in_elem_fc, out_elem_fc); + if (ret) { + BT_LOGE("Error creating output elem field class " + "from input elem field class for static array: " + "in-fc-addr=%p", in_elem_fc); + BT_FIELD_CLASS_PUT_REF_AND_RESET(out_elem_fc); + goto error; + } + +error: + return out_elem_fc; +} + +BT_HIDDEN +bt_field_class *create_field_class_copy_internal(struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class) +{ + bt_field_class *out_field_class = NULL; + + BT_LOGD("Creating bare field class based on field class: in-fc-addr=%p", + in_field_class); + + switch(bt_field_class_get_type(in_field_class)) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + out_field_class = bt_field_class_unsigned_integer_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + out_field_class = bt_field_class_signed_integer_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + out_field_class = bt_field_class_unsigned_enumeration_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + out_field_class = bt_field_class_signed_enumeration_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_REAL: + out_field_class = bt_field_class_real_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_STRING: + out_field_class = bt_field_class_string_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_STRUCTURE: + out_field_class = bt_field_class_structure_create( + md_maps->output_trace_class); + break; + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + { + const bt_field_class *in_elem_fc = + bt_field_class_array_borrow_element_field_class_const( + in_field_class); + uint64_t array_len = + bt_field_class_static_array_get_length(in_field_class); + + bt_field_class *out_elem_fc = copy_field_class_array_element( + md_maps, in_elem_fc); + if (!out_elem_fc) { + out_field_class = NULL; + goto error; + } + + out_field_class = bt_field_class_static_array_create( + md_maps->output_trace_class, + out_elem_fc, array_len); + break; + } + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + { + const bt_field_class *in_elem_fc = + bt_field_class_array_borrow_element_field_class_const( + in_field_class); + + bt_field_class *out_elem_fc = copy_field_class_array_element( + md_maps, in_elem_fc); + if (!out_elem_fc) { + out_field_class = NULL; + goto error; + } + + out_field_class = bt_field_class_dynamic_array_create( + md_maps->output_trace_class, + out_elem_fc); + break; + } + case BT_FIELD_CLASS_TYPE_VARIANT: + out_field_class = bt_field_class_variant_create( + md_maps->output_trace_class); + break; + default: + abort(); + } + + /* + * Add mapping from in_field_class to out_field_class. This simplifies + * the resolution of field paths in variant and dynamic array field + * classes. + */ + g_hash_table_insert(md_maps->field_class_map, + (gpointer) in_field_class, out_field_class); + +error: + if(out_field_class){ + BT_LOGD("Created bare field class based on field class: in-fc-addr=%p, " + "out-fc-addr=%p", in_field_class, out_field_class); + } else { + BT_LOGE("Error creating output field class from input field " + "class: in-fc-addr=%p", in_field_class); + } + + return out_field_class; +} + +BT_HIDDEN +int copy_field_class_content_internal( + struct trace_ir_metadata_maps *md_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class) +{ + int ret = 0; + switch(bt_field_class_get_type(in_field_class)) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + ret = field_class_unsigned_integer_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + ret = field_class_signed_integer_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + ret = field_class_unsigned_enumeration_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + ret = field_class_signed_enumeration_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_REAL: + ret = field_class_real_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_STRING: + ret = field_class_string_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_STRUCTURE: + ret = field_class_structure_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + ret = field_class_static_array_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + ret = field_class_dynamic_array_copy(md_maps, + in_field_class, out_field_class); + break; + case BT_FIELD_CLASS_TYPE_VARIANT: + ret = field_class_variant_copy(md_maps, + in_field_class, out_field_class); + break; + default: + abort(); + } + + return ret; +} diff --git a/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h new file mode 100644 index 00000000..3bd5b80f --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/trace-ir-metadata-field-class-copy.h @@ -0,0 +1,42 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H + +/* + * Babeltrace - Trace IR metadata field class copy + * + * Copyright (c) 2015-2019 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "trace-ir-mapping.h" + +BT_HIDDEN +int copy_field_class_content_internal(struct trace_ir_metadata_maps *trace_ir_metadata_maps, + const bt_field_class *in_field_class, + bt_field_class *out_field_class); + +BT_HIDDEN +bt_field_class *create_field_class_copy_internal( + struct trace_ir_metadata_maps *trace_ir_metadata_maps, + const bt_field_class *in_field_class); + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_FIELD_CLASS_COPY_H */ diff --git a/src/plugins/lttng-utils/debug-info/utils.c b/src/plugins/lttng-utils/debug-info/utils.c new file mode 100644 index 00000000..8f1bddea --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/utils.c @@ -0,0 +1,113 @@ +/* + * Babeltrace - Debug info utilities + * + * Copyright (c) 2016 Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "utils.h" + +BT_HIDDEN +const char *get_filename_from_path(const char *path) +{ + size_t i = strlen(path); + + if (i == 0) { + goto end; + } + + if (path[i - 1] == '/') { + /* + * Path ends with a trailing slash, no filename to return. + * Return the original path. + */ + goto end; + } + + while (i-- > 0) { + if (path[i] == '/') { + path = &path[i + 1]; + goto end; + } + } +end: + return path; +} + +BT_HIDDEN +bt_bool is_event_common_ctx_dbg_info_compatible(const bt_field_class *in_field_class, + const char *debug_info_field_class_name) +{ + const bt_field_class_structure_member *member; + const bt_field_class *ip_fc, *vpid_fc; + bt_bool match = BT_FALSE; + + /* + * If the debug info field is already present in the event common + * context. Do not try to add it. + */ + member = + bt_field_class_structure_borrow_member_by_name_const( + in_field_class, debug_info_field_class_name); + if (member) { + goto end; + } + + /* + * Verify that the ip and vpid field are present and of the right field + * class. + */ + member = bt_field_class_structure_borrow_member_by_name_const( + in_field_class, IP_FIELD_NAME); + if (!member) { + goto end; + } + + ip_fc = bt_field_class_structure_member_borrow_field_class_const( + member); + if (bt_field_class_get_type(ip_fc) != + BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER) { + match = BT_FALSE; + goto end; + } + + member = bt_field_class_structure_borrow_member_by_name_const( + in_field_class, VPID_FIELD_NAME); + if (!member) { + goto end; + } + + vpid_fc = bt_field_class_structure_member_borrow_field_class_const( + member); + + if (bt_field_class_get_type(vpid_fc) != + BT_FIELD_CLASS_TYPE_SIGNED_INTEGER) { + goto end; + } + + if (bt_field_class_integer_get_field_value_range(vpid_fc) != 32) { + goto end; + } + + match = BT_TRUE; + +end: + return match; +} diff --git a/src/plugins/lttng-utils/debug-info/utils.h b/src/plugins/lttng-utils/debug-info/utils.h new file mode 100644 index 00000000..393f34eb --- /dev/null +++ b/src/plugins/lttng-utils/debug-info/utils.h @@ -0,0 +1,44 @@ +#ifndef BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H +#define BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H +/* + * Babeltrace - Debug Info Utilities + * + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common/babeltrace.h" +#include "trace-ir-mapping.h" + +/* + * Return the location of a path's file (the last element of the path). + * Returns the original path on error. + */ +BT_HIDDEN +const char *get_filename_from_path(const char *path); + +BT_HIDDEN +bt_bool is_event_common_ctx_dbg_info_compatible( + const bt_field_class *in_field_class, + const char *debug_info_field_class_name); + +#endif /* BABELTRACE_PLUGIN_DEBUG_INFO_UTILS_H */ diff --git a/src/plugins/lttng-utils/plugin.c b/src/plugins/lttng-utils/plugin.c new file mode 100644 index 00000000..8d50b0e8 --- /dev/null +++ b/src/plugins/lttng-utils/plugin.c @@ -0,0 +1,57 @@ +/* + * plugin.c + * + * Babeltrace Debug Info Plug-in + * + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "debug-info/debug-info.h" + +#ifndef BT_BUILT_IN_PLUGINS +BT_PLUGIN_MODULE(); +#endif + +/* Initialize plug-in entry points. */ +BT_PLUGIN_WITH_ID(lttng_utils, "lttng-utils"); +BT_PLUGIN_DESCRIPTION_WITH_ID(lttng_utils, "LTTng utilities"); +BT_PLUGIN_AUTHOR_WITH_ID(lttng_utils, "Julien Desfossez"); +BT_PLUGIN_LICENSE_WITH_ID(lttng_utils, "MIT"); + +BT_PLUGIN_FILTER_COMPONENT_CLASS_WITH_ID(lttng_utils, debug_info, "debug-info", + debug_info_msg_iter_next); +BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION_WITH_ID(lttng_utils, debug_info, + "Augment compatible events with debugging information."); +BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD_WITH_ID(lttng_utils, + debug_info, debug_info_comp_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(lttng_utils, + debug_info, debug_info_comp_finalize); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD_WITH_ID( + lttng_utils, debug_info, debug_info_msg_iter_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD_WITH_ID( + lttng_utils, debug_info, debug_info_msg_iter_seek_beginning); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD_WITH_ID( + lttng_utils, debug_info, debug_info_msg_iter_can_seek_beginning); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD_WITH_ID( + lttng_utils, debug_info, debug_info_msg_iter_finalize); diff --git a/src/plugins/plugins-common.h b/src/plugins/plugins-common.h new file mode 100644 index 00000000..c7377bf8 --- /dev/null +++ b/src/plugins/plugins-common.h @@ -0,0 +1,32 @@ +#ifndef PLUGINS_COMMON_H +#define PLUGINS_COMMON_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * UNUSED_VAR: tag a variable or parameter as explicitly unused so that + * the compiler does not warn. + */ +#define UNUSED_VAR __attribute__((unused)) + +#endif /* PLUGINS_COMMON_H */ diff --git a/src/plugins/text/Makefile.am b/src/plugins/text/Makefile.am new file mode 100644 index 00000000..f7179107 --- /dev/null +++ b/src/plugins/text/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = pretty dmesg + +plugindir = "$(PLUGINSDIR)" +plugin_LTLIBRARIES = babeltrace-plugin-text.la + +babeltrace_plugin_text_la_SOURCES = plugin.c +babeltrace_plugin_text_la_LDFLAGS = \ + $(LT_NO_UNDEFINED) \ + -avoid-version -module + +babeltrace_plugin_text_la_LIBADD = \ + pretty/libbabeltrace2-plugin-text-pretty-cc.la \ + dmesg/libbabeltrace2-plugin-text-dmesg-cc.la + +if !ENABLE_BUILT_IN_PLUGINS +babeltrace_plugin_text_la_LIBADD += \ + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/compat/libcompat.la +endif diff --git a/src/plugins/text/dmesg/Makefile.am b/src/plugins/text/dmesg/Makefile.am new file mode 100644 index 00000000..6f168b38 --- /dev/null +++ b/src/plugins/text/dmesg/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-text-dmesg-cc.la + +# ctf-text plugin +libbabeltrace2_plugin_text_dmesg_cc_la_SOURCES = \ + dmesg.c \ + dmesg.h \ + logging.h \ + logging.c diff --git a/src/plugins/text/dmesg/dmesg.c b/src/plugins/text/dmesg/dmesg.c new file mode 100644 index 00000000..7ed01cdc --- /dev/null +++ b/src/plugins/text/dmesg/dmesg.c @@ -0,0 +1,927 @@ +/* + * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-TEXT-DMESG-SRC" +#include "logging.h" + +#include +#include +#include +#include +#include "common/assert.h" +#include "common/common.h" +#include +#include "lib/value.h" +#include "compat/utc.h" +#include "compat/stdio.h" +#include + +#define NSEC_PER_USEC 1000UL +#define NSEC_PER_MSEC 1000000UL +#define NSEC_PER_SEC 1000000000ULL +#define USEC_PER_SEC 1000000UL + +struct dmesg_component; + +struct dmesg_msg_iter { + struct dmesg_component *dmesg_comp; + bt_self_message_iterator *pc_msg_iter; /* Weak */ + char *linebuf; + size_t linebuf_len; + FILE *fp; + bt_message *tmp_event_msg; + uint64_t last_clock_value; + + enum { + STATE_EMIT_STREAM_BEGINNING, + STATE_EMIT_STREAM_ACTIVITY_BEGINNING, + STATE_EMIT_PACKET_BEGINNING, + STATE_EMIT_EVENT, + STATE_EMIT_PACKET_END, + STATE_EMIT_STREAM_ACTIVITY_END, + STATE_EMIT_STREAM_END, + STATE_DONE, + } state; +}; + +struct dmesg_component { + struct { + GString *path; + bt_bool read_from_stdin; + bt_bool no_timestamp; + } params; + + bt_self_component_source *self_comp; + bt_trace_class *trace_class; + bt_stream_class *stream_class; + bt_event_class *event_class; + bt_trace *trace; + bt_stream *stream; + bt_packet *packet; + bt_clock_class *clock_class; +}; + +static +bt_field_class *create_event_payload_fc(bt_trace_class *trace_class) +{ + bt_field_class *root_fc = NULL; + bt_field_class *fc = NULL; + int ret; + + root_fc = bt_field_class_structure_create(trace_class); + if (!root_fc) { + BT_LOGE_STR("Cannot create an empty structure field class object."); + goto error; + } + + fc = bt_field_class_string_create(trace_class); + if (!fc) { + BT_LOGE_STR("Cannot create a string field class object."); + goto error; + } + + ret = bt_field_class_structure_append_member(root_fc, + "str", fc); + if (ret) { + BT_LOGE("Cannot add `str` member to structure field class: " + "ret=%d", ret); + goto error; + } + + goto end; + +error: + BT_FIELD_CLASS_PUT_REF_AND_RESET(root_fc); + +end: + bt_field_class_put_ref(fc); + return root_fc; +} + +static +int create_meta(struct dmesg_component *dmesg_comp, bool has_ts) +{ + bt_field_class *fc = NULL; + int ret = 0; + + dmesg_comp->trace_class = bt_trace_class_create( + bt_self_component_source_as_self_component( + dmesg_comp->self_comp)); + if (!dmesg_comp->trace_class) { + BT_LOGE_STR("Cannot create an empty trace class object."); + goto error; + } + + dmesg_comp->stream_class = bt_stream_class_create( + dmesg_comp->trace_class); + if (!dmesg_comp->stream_class) { + BT_LOGE_STR("Cannot create a stream class object."); + goto error; + } + + if (has_ts) { + dmesg_comp->clock_class = bt_clock_class_create( + bt_self_component_source_as_self_component( + dmesg_comp->self_comp)); + if (!dmesg_comp->clock_class) { + BT_LOGE_STR("Cannot create clock class."); + goto error; + } + + /* + * The `dmesg` timestamp's origin is not the Unix epoch, + * it's the boot time. + */ + bt_clock_class_set_origin_is_unix_epoch(dmesg_comp->clock_class, + BT_FALSE); + + ret = bt_stream_class_set_default_clock_class( + dmesg_comp->stream_class, dmesg_comp->clock_class); + if (ret) { + BT_LOGE_STR("Cannot set stream class's default clock class."); + goto error; + } + + bt_stream_class_set_packets_have_beginning_default_clock_snapshot( + dmesg_comp->stream_class, BT_TRUE); + bt_stream_class_set_packets_have_end_default_clock_snapshot( + dmesg_comp->stream_class, BT_TRUE); + } + + dmesg_comp->event_class = bt_event_class_create( + dmesg_comp->stream_class); + if (!dmesg_comp->event_class) { + BT_LOGE_STR("Cannot create an event class object."); + goto error; + } + + ret = bt_event_class_set_name(dmesg_comp->event_class, "string"); + if (ret) { + BT_LOGE_STR("Cannot set event class's name."); + goto error; + } + + fc = create_event_payload_fc(dmesg_comp->trace_class); + if (!fc) { + BT_LOGE_STR("Cannot create event payload field class."); + goto error; + } + + ret = bt_event_class_set_payload_field_class(dmesg_comp->event_class, fc); + if (ret) { + BT_LOGE_STR("Cannot set event class's event payload field class."); + goto error; + } + + goto end; + +error: + ret = -1; + +end: + bt_field_class_put_ref(fc); + return ret; +} + +static +int handle_params(struct dmesg_component *dmesg_comp, + const bt_value *params) +{ + const bt_value *no_timestamp = NULL; + const bt_value *path = NULL; + const char *path_str; + int ret = 0; + + no_timestamp = bt_value_map_borrow_entry_value_const(params, + "no-extract-timestamp"); + if (no_timestamp) { + if (!bt_value_is_bool(no_timestamp)) { + BT_LOGE("Expecting a boolean value for the `no-extract-timestamp` parameter: " + "type=%s", + bt_common_value_type_string( + bt_value_get_type(no_timestamp))); + goto error; + } + + dmesg_comp->params.no_timestamp = + bt_value_bool_get(no_timestamp); + } + + path = bt_value_map_borrow_entry_value_const(params, "path"); + if (path) { + if (dmesg_comp->params.read_from_stdin) { + BT_LOGE_STR("Cannot specify both `read-from-stdin` and `path` parameters."); + goto error; + } + + if (!bt_value_is_string(path)) { + BT_LOGE("Expecting a string value for the `path` parameter: " + "type=%s", + bt_common_value_type_string( + bt_value_get_type(path))); + goto error; + } + + path_str = bt_value_string_get(path); + g_string_assign(dmesg_comp->params.path, path_str); + } else { + dmesg_comp->params.read_from_stdin = true; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int create_packet_and_stream_and_trace(struct dmesg_component *dmesg_comp) +{ + int ret = 0; + const char *trace_name = NULL; + gchar *basename = NULL; + + dmesg_comp->trace = bt_trace_create(dmesg_comp->trace_class); + if (!dmesg_comp->trace) { + BT_LOGE_STR("Cannot create trace object."); + goto error; + } + + if (dmesg_comp->params.read_from_stdin) { + trace_name = "STDIN"; + } else { + basename = g_path_get_basename(dmesg_comp->params.path->str); + BT_ASSERT(basename); + + if (strcmp(basename, G_DIR_SEPARATOR_S) != 0 && + strcmp(basename, ".") != 0) { + trace_name = basename; + } + } + + if (trace_name) { + ret = bt_trace_set_name(dmesg_comp->trace, trace_name); + if (ret) { + BT_LOGE("Cannot set trace's name: name=\"%s\"", + trace_name); + goto error; + } + } + + dmesg_comp->stream = bt_stream_create(dmesg_comp->stream_class, + dmesg_comp->trace); + if (!dmesg_comp->stream) { + BT_LOGE_STR("Cannot create stream object."); + goto error; + } + + dmesg_comp->packet = bt_packet_create(dmesg_comp->stream); + if (!dmesg_comp->packet) { + BT_LOGE_STR("Cannot create packet object."); + goto error; + } + + goto end; + +error: + ret = -1; + +end: + if (basename) { + g_free(basename); + } + + return ret; +} + +static +int try_create_meta_stream_packet(struct dmesg_component *dmesg_comp, + bool has_ts) +{ + int ret = 0; + + if (dmesg_comp->trace) { + /* Already created */ + goto end; + } + + ret = create_meta(dmesg_comp, has_ts); + if (ret) { + BT_LOGE("Cannot create metadata objects: dmesg-comp-addr=%p", + dmesg_comp); + goto error; + } + + ret = create_packet_and_stream_and_trace(dmesg_comp); + if (ret) { + BT_LOGE("Cannot create packet and stream objects: " + "dmesg-comp-addr=%p", dmesg_comp); + goto error; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +void destroy_dmesg_component(struct dmesg_component *dmesg_comp) +{ + if (!dmesg_comp) { + return; + } + + if (dmesg_comp->params.path) { + g_string_free(dmesg_comp->params.path, TRUE); + } + + bt_packet_put_ref(dmesg_comp->packet); + bt_trace_put_ref(dmesg_comp->trace); + bt_stream_class_put_ref(dmesg_comp->stream_class); + bt_event_class_put_ref(dmesg_comp->event_class); + bt_stream_put_ref(dmesg_comp->stream); + bt_clock_class_put_ref(dmesg_comp->clock_class); + bt_trace_class_put_ref(dmesg_comp->trace_class); + g_free(dmesg_comp); +} + +static +bt_self_component_status create_port( + bt_self_component_source *self_comp) +{ + return bt_self_component_source_add_output_port(self_comp, + "out", NULL, NULL); +} + +BT_HIDDEN +bt_self_component_status dmesg_init( + bt_self_component_source *self_comp, + bt_value *params, void *init_method_data) +{ + int ret = 0; + struct dmesg_component *dmesg_comp = g_new0(struct dmesg_component, 1); + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + + if (!dmesg_comp) { + BT_LOGE_STR("Failed to allocate one dmesg component structure."); + goto error; + } + + dmesg_comp->self_comp = self_comp; + dmesg_comp->params.path = g_string_new(NULL); + if (!dmesg_comp->params.path) { + BT_LOGE_STR("Failed to allocate a GString."); + goto error; + } + + ret = handle_params(dmesg_comp, params); + if (ret) { + BT_LOGE("Invalid parameters: comp-addr=%p", self_comp); + goto error; + } + + if (!dmesg_comp->params.read_from_stdin && + !g_file_test(dmesg_comp->params.path->str, + G_FILE_TEST_IS_REGULAR)) { + BT_LOGE("Input path is not a regular file: " + "comp-addr=%p, path=\"%s\"", self_comp, + dmesg_comp->params.path->str); + goto error; + } + + status = create_port(self_comp); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + bt_self_component_set_data( + bt_self_component_source_as_self_component(self_comp), + dmesg_comp); + goto end; + +error: + destroy_dmesg_component(dmesg_comp); + bt_self_component_set_data( + bt_self_component_source_as_self_component(self_comp), + NULL); + + if (status >= 0) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + } + +end: + return status; +} + +BT_HIDDEN +void dmesg_finalize(bt_self_component_source *self_comp) +{ + destroy_dmesg_component(bt_self_component_get_data( + bt_self_component_source_as_self_component(self_comp))); +} + +static +bt_message *create_init_event_msg_from_line( + struct dmesg_msg_iter *msg_iter, + const char *line, const char **new_start) +{ + bt_event *event; + bt_message *msg = NULL; + bool has_timestamp = false; + unsigned long sec, usec, msec; + unsigned int year, mon, mday, hour, min; + uint64_t ts = 0; + int ret = 0; + struct dmesg_component *dmesg_comp = msg_iter->dmesg_comp; + + *new_start = line; + + if (dmesg_comp->params.no_timestamp) { + goto skip_ts; + } + + /* Extract time from input line */ + if (sscanf(line, "[%lu.%lu] ", &sec, &usec) == 2) { + ts = (uint64_t) sec * USEC_PER_SEC + (uint64_t) usec; + + /* + * The clock class we use has a 1 GHz frequency: convert + * from µs to ns. + */ + ts *= NSEC_PER_USEC; + has_timestamp = true; + } else if (sscanf(line, "[%u-%u-%u %u:%u:%lu.%lu] ", + &year, &mon, &mday, &hour, &min, + &sec, &msec) == 7) { + time_t ep_sec; + struct tm ti; + + memset(&ti, 0, sizeof(ti)); + ti.tm_year = year - 1900; /* From 1900 */ + ti.tm_mon = mon - 1; /* 0 to 11 */ + ti.tm_mday = mday; + ti.tm_hour = hour; + ti.tm_min = min; + ti.tm_sec = sec; + + ep_sec = bt_timegm(&ti); + if (ep_sec != (time_t) -1) { + ts = (uint64_t) ep_sec * NSEC_PER_SEC + + (uint64_t) msec * NSEC_PER_MSEC; + } + + has_timestamp = true; + } + + if (has_timestamp) { + /* Set new start for the message portion of the line */ + *new_start = strchr(line, ']'); + BT_ASSERT(*new_start); + (*new_start)++; + + if ((*new_start)[0] == ' ') { + (*new_start)++; + } + } + +skip_ts: + /* + * At this point, we know if the stream class's event header + * field class should have a timestamp or not, so we can lazily + * create the metadata, stream, and packet objects. + */ + ret = try_create_meta_stream_packet(dmesg_comp, has_timestamp); + if (ret) { + /* try_create_meta_stream_packet() logs errors */ + goto error; + } + + if (dmesg_comp->clock_class) { + msg = bt_message_event_create_with_default_clock_snapshot( + msg_iter->pc_msg_iter, + dmesg_comp->event_class, dmesg_comp->packet, ts); + msg_iter->last_clock_value = ts; + } else { + msg = bt_message_event_create(msg_iter->pc_msg_iter, + dmesg_comp->event_class, dmesg_comp->packet); + } + + if (!msg) { + BT_LOGE_STR("Cannot create event message."); + goto error; + } + + event = bt_message_event_borrow_event(msg); + BT_ASSERT(event); + goto end; + +error: + BT_MESSAGE_PUT_REF_AND_RESET(msg); + +end: + return msg; +} + +static +int fill_event_payload_from_line(const char *line, + bt_event *event) +{ + bt_field *ep_field = NULL; + bt_field *str_field = NULL; + size_t len; + int ret; + + ep_field = bt_event_borrow_payload_field(event); + BT_ASSERT(ep_field); + str_field = bt_field_structure_borrow_member_field_by_index( + ep_field, 0); + if (!str_field) { + BT_LOGE_STR("Cannot borrow `timestamp` field from event payload structure field."); + goto error; + } + + len = strlen(line); + if (line[len - 1] == '\n') { + /* Do not include the newline character in the payload */ + len--; + } + + ret = bt_field_string_clear(str_field); + if (ret) { + BT_LOGE_STR("Cannot clear string field object."); + goto error; + } + + ret = bt_field_string_append_with_length(str_field, line, len); + if (ret) { + BT_LOGE("Cannot append value to string field object: " + "len=%zu", len); + goto error; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +bt_message *create_msg_from_line( + struct dmesg_msg_iter *dmesg_msg_iter, const char *line) +{ + bt_event *event = NULL; + bt_message *msg = NULL; + const char *new_start; + int ret; + + msg = create_init_event_msg_from_line(dmesg_msg_iter, + line, &new_start); + if (!msg) { + BT_LOGE_STR("Cannot create and initialize event message from line."); + goto error; + } + + event = bt_message_event_borrow_event(msg); + BT_ASSERT(event); + ret = fill_event_payload_from_line(new_start, event); + if (ret) { + BT_LOGE("Cannot fill event payload field from line: " + "ret=%d", ret); + goto error; + } + + goto end; + +error: + BT_MESSAGE_PUT_REF_AND_RESET(msg); + +end: + return msg; +} + +static +void destroy_dmesg_msg_iter(struct dmesg_msg_iter *dmesg_msg_iter) +{ + if (!dmesg_msg_iter) { + return; + } + + if (dmesg_msg_iter->fp && dmesg_msg_iter->fp != stdin) { + if (fclose(dmesg_msg_iter->fp)) { + BT_LOGE_ERRNO("Cannot close input file", "."); + } + } + + bt_message_put_ref(dmesg_msg_iter->tmp_event_msg); + free(dmesg_msg_iter->linebuf); + g_free(dmesg_msg_iter); +} + + + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_source *self_comp, + bt_self_component_port_output *self_port) +{ + struct dmesg_component *dmesg_comp; + struct dmesg_msg_iter *dmesg_msg_iter = + g_new0(struct dmesg_msg_iter, 1); + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + if (!dmesg_msg_iter) { + BT_LOGE_STR("Failed to allocate on dmesg message iterator structure."); + goto error; + } + + dmesg_comp = bt_self_component_get_data( + bt_self_component_source_as_self_component(self_comp)); + BT_ASSERT(dmesg_comp); + dmesg_msg_iter->dmesg_comp = dmesg_comp; + dmesg_msg_iter->pc_msg_iter = self_msg_iter; + + if (dmesg_comp->params.read_from_stdin) { + dmesg_msg_iter->fp = stdin; + } else { + dmesg_msg_iter->fp = fopen(dmesg_comp->params.path->str, "r"); + if (!dmesg_msg_iter->fp) { + BT_LOGE_ERRNO("Cannot open input file in read mode", ": path=\"%s\"", + dmesg_comp->params.path->str); + goto error; + } + } + + bt_self_message_iterator_set_data(self_msg_iter, + dmesg_msg_iter); + goto end; + +error: + destroy_dmesg_msg_iter(dmesg_msg_iter); + bt_self_message_iterator_set_data(self_msg_iter, NULL); + if (status >= 0) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } + +end: + return status; +} + +BT_HIDDEN +void dmesg_msg_iter_finalize( + bt_self_message_iterator *priv_msg_iter) +{ + destroy_dmesg_msg_iter(bt_self_message_iterator_get_data( + priv_msg_iter)); +} + +static +bt_self_message_iterator_status dmesg_msg_iter_next_one( + struct dmesg_msg_iter *dmesg_msg_iter, + bt_message **msg) +{ + ssize_t len; + struct dmesg_component *dmesg_comp; + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(dmesg_msg_iter); + dmesg_comp = dmesg_msg_iter->dmesg_comp; + BT_ASSERT(dmesg_comp); + + if (dmesg_msg_iter->state == STATE_DONE) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + goto end; + } + + if (dmesg_msg_iter->tmp_event_msg || + dmesg_msg_iter->state == STATE_EMIT_PACKET_END || + dmesg_msg_iter->state == STATE_EMIT_STREAM_ACTIVITY_END || + dmesg_msg_iter->state == STATE_EMIT_STREAM_END) { + goto handle_state; + } + + while (true) { + const char *ch; + bool only_spaces = true; + + len = bt_getline(&dmesg_msg_iter->linebuf, + &dmesg_msg_iter->linebuf_len, dmesg_msg_iter->fp); + if (len < 0) { + if (errno == EINVAL) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } else if (errno == ENOMEM) { + status = + BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + } else { + if (dmesg_msg_iter->state == STATE_EMIT_STREAM_BEGINNING) { + /* Stream did not even begin */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + goto end; + } else { + /* End current packet now */ + dmesg_msg_iter->state = + STATE_EMIT_PACKET_END; + goto handle_state; + } + } + + goto end; + } + + BT_ASSERT(dmesg_msg_iter->linebuf); + + /* Ignore empty lines, once trimmed */ + for (ch = dmesg_msg_iter->linebuf; *ch != '\0'; ch++) { + if (!isspace(*ch)) { + only_spaces = false; + break; + } + } + + if (!only_spaces) { + break; + } + } + + dmesg_msg_iter->tmp_event_msg = create_msg_from_line( + dmesg_msg_iter, dmesg_msg_iter->linebuf); + if (!dmesg_msg_iter->tmp_event_msg) { + BT_LOGE("Cannot create event message from line: " + "dmesg-comp-addr=%p, line=\"%s\"", dmesg_comp, + dmesg_msg_iter->linebuf); + goto end; + } + +handle_state: + BT_ASSERT(dmesg_comp->trace); + + switch (dmesg_msg_iter->state) { + case STATE_EMIT_STREAM_BEGINNING: + BT_ASSERT(dmesg_msg_iter->tmp_event_msg); + *msg = bt_message_stream_beginning_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); + dmesg_msg_iter->state = STATE_EMIT_STREAM_ACTIVITY_BEGINNING; + break; + case STATE_EMIT_STREAM_ACTIVITY_BEGINNING: + BT_ASSERT(dmesg_msg_iter->tmp_event_msg); + *msg = bt_message_stream_activity_beginning_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); + dmesg_msg_iter->state = STATE_EMIT_PACKET_BEGINNING; + break; + case STATE_EMIT_PACKET_BEGINNING: + BT_ASSERT(dmesg_msg_iter->tmp_event_msg); + + if (dmesg_comp->clock_class) { + *msg = bt_message_packet_beginning_create_with_default_clock_snapshot( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet, + dmesg_msg_iter->last_clock_value); + } else { + *msg = bt_message_packet_beginning_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet); + } + + dmesg_msg_iter->state = STATE_EMIT_EVENT; + break; + case STATE_EMIT_EVENT: + BT_ASSERT(dmesg_msg_iter->tmp_event_msg); + *msg = dmesg_msg_iter->tmp_event_msg; + dmesg_msg_iter->tmp_event_msg = NULL; + break; + case STATE_EMIT_PACKET_END: + if (dmesg_comp->clock_class) { + *msg = bt_message_packet_end_create_with_default_clock_snapshot( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet, + dmesg_msg_iter->last_clock_value); + } else { + *msg = bt_message_packet_end_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->packet); + } + + dmesg_msg_iter->state = STATE_EMIT_STREAM_ACTIVITY_END; + break; + case STATE_EMIT_STREAM_ACTIVITY_END: + *msg = bt_message_stream_activity_end_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); + dmesg_msg_iter->state = STATE_EMIT_STREAM_END; + break; + case STATE_EMIT_STREAM_END: + *msg = bt_message_stream_end_create( + dmesg_msg_iter->pc_msg_iter, dmesg_comp->stream); + dmesg_msg_iter->state = STATE_DONE; + break; + default: + break; + } + + if (!*msg) { + BT_LOGE("Cannot create message: dmesg-comp-addr=%p", + dmesg_comp); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + } + +end: + return status; +} + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + struct dmesg_msg_iter *dmesg_msg_iter = + bt_self_message_iterator_get_data( + self_msg_iter); + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + uint64_t i = 0; + + while (i < capacity && + status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + bt_message *priv_msg = NULL; + + status = dmesg_msg_iter_next_one(dmesg_msg_iter, + &priv_msg); + msgs[i] = priv_msg; + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + i++; + } + } + + if (i > 0) { + /* + * Even if dmesg_msg_iter_next_one() returned + * something else than + * BT_SELF_MESSAGE_ITERATOR_STATUS_OK, we + * accumulated message objects in the output + * message array, so we need to return + * BT_SELF_MESSAGE_ITERATOR_STATUS_OK so that they + * are transfered to downstream. This other status + * occurs again the next time muxer_msg_iter_do_next() + * is called, possibly without any accumulated + * message, in which case we'll return it. + */ + *count = i; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + } + + return status; +} + +BT_HIDDEN +bt_bool dmesg_msg_iter_can_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct dmesg_msg_iter *dmesg_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + + /* Can't seek the beginning of the standard input stream */ + return !dmesg_msg_iter->dmesg_comp->params.read_from_stdin; +} + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct dmesg_msg_iter *dmesg_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + + BT_ASSERT(!dmesg_msg_iter->dmesg_comp->params.read_from_stdin); + + BT_MESSAGE_PUT_REF_AND_RESET(dmesg_msg_iter->tmp_event_msg); + dmesg_msg_iter->last_clock_value = 0; + dmesg_msg_iter->state = STATE_EMIT_STREAM_BEGINNING; + return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; +} diff --git a/src/plugins/text/dmesg/dmesg.h b/src/plugins/text/dmesg/dmesg.h new file mode 100644 index 00000000..6fc5ddef --- /dev/null +++ b/src/plugins/text/dmesg/dmesg.h @@ -0,0 +1,62 @@ +#ifndef BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H +#define BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include + +BT_HIDDEN +bt_self_component_status dmesg_init( + bt_self_component_source *self_comp, + const bt_value *params, void *init_method_data); + +BT_HIDDEN +void dmesg_finalize(bt_self_component_source *self_comp); + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_source *self_comp, + bt_self_component_port_output *self_port); + +BT_HIDDEN +void dmesg_msg_iter_finalize( + bt_self_message_iterator *self_msg_iter); + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +BT_HIDDEN +bt_bool dmesg_msg_iter_can_seek_beginning( + bt_self_message_iterator *message_iterator); + +BT_HIDDEN +bt_self_message_iterator_status dmesg_msg_iter_seek_beginning( + bt_self_message_iterator *message_iterator); + +#endif /* BABELTRACE_PLUGIN_TEXT_DMESG_DMESG_H */ diff --git a/src/plugins/text/dmesg/logging.c b/src/plugins/text/dmesg/logging.c new file mode 100644 index 00000000..cf042507 --- /dev/null +++ b/src/plugins/text/dmesg/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_dmesg_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_text_dmesg_log_level, + "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL"); diff --git a/src/plugins/text/dmesg/logging.h b/src/plugins/text/dmesg/logging.h new file mode 100644 index 00000000..1facad3e --- /dev/null +++ b/src/plugins/text/dmesg/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_TEXT_DMESG_LOG_LEVEL +#define PLUGINS_TEXT_DMESG_LOG_LEVEL + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_dmesg_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_text_dmesg_log_level); + +#endif /* PLUGINS_TEXT_DMESG_LOG_LEVEL */ diff --git a/src/plugins/text/plugin.c b/src/plugins/text/plugin.c new file mode 100644 index 00000000..17f814b9 --- /dev/null +++ b/src/plugins/text/plugin.c @@ -0,0 +1,58 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "pretty/pretty.h" +#include "dmesg/dmesg.h" + +#ifndef BT_BUILT_IN_PLUGINS +BT_PLUGIN_MODULE(); +#endif + +BT_PLUGIN(text); +BT_PLUGIN_DESCRIPTION("Plain text component classes"); +BT_PLUGIN_AUTHOR("Julien Desfossez, Mathieu Desnoyers, Philippe Proulx"); +BT_PLUGIN_LICENSE("MIT"); + +/* pretty sink */ +BT_PLUGIN_SINK_COMPONENT_CLASS(pretty, pretty_consume); +BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(pretty, pretty_init); +BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(pretty, pretty_finalize); +BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(pretty, + pretty_graph_is_configured); +BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(pretty, + "Pretty-print messages (`text` format of Babeltrace 1)."); + +/* dmesg source */ +BT_PLUGIN_SOURCE_COMPONENT_CLASS(dmesg, dmesg_msg_iter_next); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(dmesg, + "Read a dmesg output from a file or from standard input."); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_INIT_METHOD(dmesg, dmesg_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(dmesg, dmesg_finalize); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(dmesg, + dmesg_msg_iter_init); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(dmesg, + dmesg_msg_iter_finalize); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(dmesg, + dmesg_msg_iter_seek_beginning); +BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD(dmesg, + dmesg_msg_iter_can_seek_beginning); diff --git a/src/plugins/text/pretty/Makefile.am b/src/plugins/text/pretty/Makefile.am new file mode 100644 index 00000000..37000e13 --- /dev/null +++ b/src/plugins/text/pretty/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-text-pretty-cc.la + +# ctf-text plugin +libbabeltrace2_plugin_text_pretty_cc_la_SOURCES = \ + logging.c \ + logging.h \ + pretty.c \ + pretty.h \ + print.c diff --git a/src/plugins/text/pretty/logging.c b/src/plugins/text/pretty/logging.c new file mode 100644 index 00000000..03dd9c15 --- /dev/null +++ b/src/plugins/text/pretty/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_pretty_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_text_pretty_log_level, + "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL"); diff --git a/src/plugins/text/pretty/logging.h b/src/plugins/text/pretty/logging.h new file mode 100644 index 00000000..61d4f2b4 --- /dev/null +++ b/src/plugins/text/pretty/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_TEXT_PRETTY_LOG_LEVEL +#define PLUGINS_TEXT_PRETTY_LOG_LEVEL + +/* + * Copyright (c) 2019 Francis Deslauriers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_text_pretty_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_text_pretty_log_level); + +#endif /* PLUGINS_TEXT_PRETTY_LOG_LEVEL */ diff --git a/src/plugins/text/pretty/pretty.c b/src/plugins/text/pretty/pretty.c new file mode 100644 index 00000000..9db8770e --- /dev/null +++ b/src/plugins/text/pretty/pretty.c @@ -0,0 +1,689 @@ +/* + * Copyright 2016 Jérémie Galarneau + * Copyright 2016 Mathieu Desnoyers + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-TEXT-PRETTY-SINK" +#include "logging.h" + +#include +#include "compat/compiler.h" +#include "common/common.h" +#include "plugins/plugins-common.h" +#include +#include +#include +#include "common/assert.h" + +#include "pretty.h" + +GQuark stream_packet_context_quarks[STREAM_PACKET_CONTEXT_QUARKS_LEN]; + +static +const char *plugin_options[] = { + "color", + "path", + "no-delta", + "clock-cycles", + "clock-seconds", + "clock-date", + "clock-gmt", + "verbose", + "name-default", /* show/hide */ + "name-payload", + "name-context", + "name-scope", + "name-header", + "field-default", /* show/hide */ + "field-trace", + "field-trace:hostname", + "field-trace:domain", + "field-trace:procname", + "field-trace:vpid", + "field-loglevel", + "field-emf", + "field-callsite", +}; + +static +const char * const in_port_name = "in"; + +static +void destroy_pretty_data(struct pretty_component *pretty) +{ + bt_self_component_port_input_message_iterator_put_ref(pretty->iterator); + + if (pretty->string) { + (void) g_string_free(pretty->string, TRUE); + } + + if (pretty->tmp_string) { + (void) g_string_free(pretty->tmp_string, TRUE); + } + + if (pretty->out != stdout) { + int ret; + + ret = fclose(pretty->out); + if (ret) { + perror("close output file"); + } + } + g_free(pretty->options.output_path); + g_free(pretty); +} + +static +struct pretty_component *create_pretty(void) +{ + struct pretty_component *pretty; + + pretty = g_new0(struct pretty_component, 1); + if (!pretty) { + goto end; + } + pretty->string = g_string_new(""); + if (!pretty->string) { + goto error; + } + pretty->tmp_string = g_string_new(""); + if (!pretty->tmp_string) { + goto error; + } +end: + return pretty; + +error: + g_free(pretty); + return NULL; +} + +BT_HIDDEN +void pretty_finalize(bt_self_component_sink *comp) +{ + destroy_pretty_data( + bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp))); +} + +static +bt_self_component_status handle_message( + struct pretty_component *pretty, + const bt_message *message) +{ + bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; + + BT_ASSERT(pretty); + + switch (bt_message_get_type(message)) { + case BT_MESSAGE_TYPE_EVENT: + if (pretty_print_event(pretty, message)) { + ret = BT_SELF_COMPONENT_STATUS_ERROR; + } + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + BT_LOGD_STR("Message iterator inactivity message."); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + if (pretty_print_discarded_items(pretty, message)) { + ret = BT_SELF_COMPONENT_STATUS_ERROR; + } + break; + default: + break; + } + + return ret; +} + +BT_HIDDEN +bt_self_component_status pretty_graph_is_configured( + bt_self_component_sink *comp) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct pretty_component *pretty; + + pretty = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(pretty); + BT_ASSERT(!pretty->iterator); + pretty->iterator = bt_self_component_port_input_message_iterator_create( + bt_self_component_sink_borrow_input_port_by_name(comp, + in_port_name)); + if (!pretty->iterator) { + status = BT_SELF_COMPONENT_STATUS_NOMEM; + } + + return status; +} + +BT_HIDDEN +bt_self_component_status pretty_consume( + bt_self_component_sink *comp) +{ + bt_self_component_status ret; + bt_message_array_const msgs; + bt_self_component_port_input_message_iterator *it; + struct pretty_component *pretty = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + bt_message_iterator_status it_ret; + uint64_t count = 0; + uint64_t i = 0; + + it = pretty->iterator; + it_ret = bt_self_component_port_input_message_iterator_next(it, + &msgs, &count); + + switch (it_ret) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + break; + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + ret = BT_SELF_COMPONENT_STATUS_AGAIN; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_END: + ret = BT_SELF_COMPONENT_STATUS_END; + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_PUT_REF_AND_RESET( + pretty->iterator); + goto end; + default: + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + BT_ASSERT(it_ret == BT_MESSAGE_ITERATOR_STATUS_OK); + + for (i = 0; i < count; i++) { + ret = handle_message(pretty, msgs[i]); + if (ret) { + goto end; + } + + bt_message_put_ref(msgs[i]); + } + +end: + for (; i < count; i++) { + bt_message_put_ref(msgs[i]); + } + + return ret; +} + +static +int add_params_to_map(bt_value *plugin_opt_map) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < BT_ARRAY_SIZE(plugin_options); i++) { + const char *key = plugin_options[i]; + bt_value_status status; + + status = bt_value_map_insert_entry(plugin_opt_map, key, + bt_value_null); + switch (status) { + case BT_VALUE_STATUS_OK: + break; + default: + ret = -1; + goto end; + } + } +end: + return ret; +} + +static +bt_bool check_param_exists(const char *key, const bt_value *object, + void *data) +{ + struct pretty_component *pretty = data; + + if (!bt_value_map_has_entry(pretty->plugin_opt_map, + key)) { + fprintf(pretty->err, + "[warning] Parameter \"%s\" unknown to \"text.pretty\" sink component\n", key); + } + return BT_TRUE; +} + +static +void apply_one_string(const char *key, const bt_value *params, char **option) +{ + const bt_value *value = NULL; + const char *str; + + value = bt_value_map_borrow_entry_value_const(params, key); + if (!value) { + goto end; + } + if (bt_value_is_null(value)) { + goto end; + } + str = bt_value_string_get(value); + *option = g_strdup(str); + +end: + return; +} + +static +void apply_one_bool(const char *key, const bt_value *params, bool *option, + bool *found) +{ + const bt_value *value = NULL; + bt_bool bool_val; + + value = bt_value_map_borrow_entry_value_const(params, key); + if (!value) { + goto end; + } + bool_val = bt_value_bool_get(value); + *option = (bool) bool_val; + if (found) { + *found = true; + } + +end: + return; +} + +static +void warn_wrong_color_param(struct pretty_component *pretty) +{ + fprintf(pretty->err, + "[warning] Accepted values for the \"color\" parameter are:\n \"always\", \"auto\", \"never\"\n"); +} + +static +int open_output_file(struct pretty_component *pretty) +{ + int ret = 0; + + if (!pretty->options.output_path) { + goto end; + } + + pretty->out = fopen(pretty->options.output_path, "w"); + if (!pretty->out) { + goto error; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int apply_params(struct pretty_component *pretty, const bt_value *params) +{ + int ret = 0; + bt_value_status status; + bool value, found; + char *str = NULL; + + pretty->plugin_opt_map = bt_value_map_create(); + if (!pretty->plugin_opt_map) { + ret = -1; + goto end; + } + ret = add_params_to_map(pretty->plugin_opt_map); + if (ret) { + goto end; + } + /* Report unknown parameters. */ + status = bt_value_map_foreach_entry_const(params, + check_param_exists, pretty); + switch (status) { + case BT_VALUE_STATUS_OK: + break; + default: + ret = -1; + goto end; + } + /* Known parameters. */ + pretty->options.color = PRETTY_COLOR_OPT_AUTO; + if (bt_value_map_has_entry(params, "color")) { + const bt_value *color_value; + const char *color; + + color_value = bt_value_map_borrow_entry_value_const(params, + "color"); + if (!color_value) { + goto end; + } + + color = bt_value_string_get(color_value); + + if (strcmp(color, "never") == 0) { + pretty->options.color = PRETTY_COLOR_OPT_NEVER; + } else if (strcmp(color, "auto") == 0) { + pretty->options.color = PRETTY_COLOR_OPT_AUTO; + } else if (strcmp(color, "always") == 0) { + pretty->options.color = PRETTY_COLOR_OPT_ALWAYS; + } else { + warn_wrong_color_param(pretty); + } + } + + apply_one_string("path", params, &pretty->options.output_path); + ret = open_output_file(pretty); + if (ret) { + goto end; + } + + value = false; /* Default. */ + apply_one_bool("no-delta", params, &value, NULL); + pretty->options.print_delta_field = !value; /* Reverse logic. */ + + value = false; /* Default. */ + apply_one_bool("clock-cycles", params, &value, NULL); + pretty->options.print_timestamp_cycles = value; + + value = false; /* Default. */ + apply_one_bool("clock-seconds", params, &value, NULL); + pretty->options.clock_seconds = value; + + value = false; /* Default. */ + apply_one_bool("clock-date", params, &value, NULL); + pretty->options.clock_date = value; + + value = false; /* Default. */ + apply_one_bool("clock-gmt", params, &value, NULL); + pretty->options.clock_gmt = value; + + value = false; /* Default. */ + apply_one_bool("verbose", params, &value, NULL); + pretty->options.verbose = value; + + /* Names. */ + apply_one_string("name-default", params, &str); + if (!str) { + pretty->options.name_default = PRETTY_DEFAULT_UNSET; + } else if (!strcmp(str, "show")) { + pretty->options.name_default = PRETTY_DEFAULT_SHOW; + } else if (!strcmp(str, "hide")) { + pretty->options.name_default = PRETTY_DEFAULT_HIDE; + } else { + ret = -1; + goto end; + } + g_free(str); + str = NULL; + + switch (pretty->options.name_default) { + case PRETTY_DEFAULT_UNSET: + pretty->options.print_payload_field_names = true; + pretty->options.print_context_field_names = true; + pretty->options.print_header_field_names = false; + pretty->options.print_scope_field_names = false; + break; + case PRETTY_DEFAULT_SHOW: + pretty->options.print_payload_field_names = true; + pretty->options.print_context_field_names = true; + pretty->options.print_header_field_names = true; + pretty->options.print_scope_field_names = true; + break; + case PRETTY_DEFAULT_HIDE: + pretty->options.print_payload_field_names = false; + pretty->options.print_context_field_names = false; + pretty->options.print_header_field_names = false; + pretty->options.print_scope_field_names = false; + break; + default: + ret = -1; + goto end; + } + + value = false; + found = false; + apply_one_bool("name-payload", params, &value, &found); + if (found) { + pretty->options.print_payload_field_names = value; + } + + value = false; + found = false; + apply_one_bool("name-context", params, &value, &found); + if (found) { + pretty->options.print_context_field_names = value; + } + + value = false; + found = false; + apply_one_bool("name-header", params, &value, &found); + if (found) { + pretty->options.print_header_field_names = value; + } + + value = false; + found = false; + apply_one_bool("name-scope", params, &value, &found); + if (found) { + pretty->options.print_scope_field_names = value; + } + + /* Fields. */ + apply_one_string("field-default", params, &str); + if (!str) { + pretty->options.field_default = PRETTY_DEFAULT_UNSET; + } else if (!strcmp(str, "show")) { + pretty->options.field_default = PRETTY_DEFAULT_SHOW; + } else if (!strcmp(str, "hide")) { + pretty->options.field_default = PRETTY_DEFAULT_HIDE; + } else { + ret = -1; + goto end; + } + g_free(str); + str = NULL; + + switch (pretty->options.field_default) { + case PRETTY_DEFAULT_UNSET: + pretty->options.print_trace_field = false; + pretty->options.print_trace_hostname_field = true; + pretty->options.print_trace_domain_field = false; + pretty->options.print_trace_procname_field = true; + pretty->options.print_trace_vpid_field = true; + pretty->options.print_loglevel_field = false; + pretty->options.print_emf_field = false; + pretty->options.print_callsite_field = false; + break; + case PRETTY_DEFAULT_SHOW: + pretty->options.print_trace_field = true; + pretty->options.print_trace_hostname_field = true; + pretty->options.print_trace_domain_field = true; + pretty->options.print_trace_procname_field = true; + pretty->options.print_trace_vpid_field = true; + pretty->options.print_loglevel_field = true; + pretty->options.print_emf_field = true; + pretty->options.print_callsite_field = true; + break; + case PRETTY_DEFAULT_HIDE: + pretty->options.print_trace_field = false; + pretty->options.print_trace_hostname_field = false; + pretty->options.print_trace_domain_field = false; + pretty->options.print_trace_procname_field = false; + pretty->options.print_trace_vpid_field = false; + pretty->options.print_loglevel_field = false; + pretty->options.print_emf_field = false; + pretty->options.print_callsite_field = false; + break; + default: + ret = -1; + goto end; + } + + value = false; + found = false; + apply_one_bool("field-trace", params, &value, &found); + if (found) { + pretty->options.print_trace_field = value; + } + + value = false; + found = false; + apply_one_bool("field-trace:hostname", params, &value, &found); + if (found) { + pretty->options.print_trace_hostname_field = value; + } + + value = false; + found = false; + apply_one_bool("field-trace:domain", params, &value, &found); + if (found) { + pretty->options.print_trace_domain_field = value; + } + + value = false; + found = false; + apply_one_bool("field-trace:procname", params, &value, &found); + if (found) { + pretty->options.print_trace_procname_field = value; + } + + value = false; + found = false; + apply_one_bool("field-trace:vpid", params, &value, &found); + if (found) { + pretty->options.print_trace_vpid_field = value; + } + + value = false; + found = false; + apply_one_bool("field-loglevel", params, &value, &found); + if (found) { + pretty->options.print_loglevel_field = value; + } + + value = false; + found = false; + apply_one_bool("field-emf", params, &value, &found); + if (found) { + pretty->options.print_emf_field = value; + } + + value = false; + found = false; + apply_one_bool("field-callsite", params, &value, &found); + if (found) { + pretty->options.print_callsite_field = value; + } + +end: + bt_value_put_ref(pretty->plugin_opt_map); + pretty->plugin_opt_map = NULL; + g_free(str); + return ret; +} + +static +void set_use_colors(struct pretty_component *pretty) +{ + switch (pretty->options.color) { + case PRETTY_COLOR_OPT_ALWAYS: + pretty->use_colors = true; + break; + case PRETTY_COLOR_OPT_AUTO: + pretty->use_colors = pretty->out == stdout && + bt_common_colors_supported(); + break; + case PRETTY_COLOR_OPT_NEVER: + pretty->use_colors = false; + break; + } +} + +static +void init_stream_packet_context_quarks(void) +{ + stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] = + g_quark_from_string("timestamp_begin"); + stream_packet_context_quarks[Q_TIMESTAMP_BEGIN] = + g_quark_from_string("timestamp_begin"); + stream_packet_context_quarks[Q_TIMESTAMP_END] = + g_quark_from_string("timestamp_end"); + stream_packet_context_quarks[Q_EVENTS_DISCARDED] = + g_quark_from_string("events_discarded"); + stream_packet_context_quarks[Q_CONTENT_SIZE] = + g_quark_from_string("content_size"); + stream_packet_context_quarks[Q_PACKET_SIZE] = + g_quark_from_string("packet_size"); + stream_packet_context_quarks[Q_PACKET_SEQ_NUM] = + g_quark_from_string("packet_seq_num"); +} + +BT_HIDDEN +bt_self_component_status pretty_init( + bt_self_component_sink *comp, + const bt_value *params, + UNUSED_VAR void *init_method_data) +{ + bt_self_component_status ret; + struct pretty_component *pretty = create_pretty(); + + if (!pretty) { + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + ret = bt_self_component_sink_add_input_port(comp, in_port_name, + NULL, NULL); + if (ret != BT_SELF_COMPONENT_STATUS_OK) { + goto end; + } + + pretty->out = stdout; + pretty->err = stderr; + + pretty->delta_cycles = -1ULL; + pretty->last_cycles_timestamp = -1ULL; + + pretty->delta_real_timestamp = -1ULL; + pretty->last_real_timestamp = -1ULL; + + if (apply_params(pretty, params)) { + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto error; + } + + set_use_colors(pretty); + bt_self_component_set_data( + bt_self_component_sink_as_self_component(comp), pretty); + init_stream_packet_context_quarks(); + +end: + return ret; + +error: + destroy_pretty_data(pretty); + return ret; +} diff --git a/src/plugins/text/pretty/pretty.h b/src/plugins/text/pretty/pretty.h new file mode 100644 index 00000000..e3a24106 --- /dev/null +++ b/src/plugins/text/pretty/pretty.h @@ -0,0 +1,131 @@ +#ifndef BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H +#define BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H + +/* + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include + +enum pretty_default { + PRETTY_DEFAULT_UNSET, + PRETTY_DEFAULT_SHOW, + PRETTY_DEFAULT_HIDE, +}; + +enum pretty_color_option { + PRETTY_COLOR_OPT_NEVER, + PRETTY_COLOR_OPT_AUTO, + PRETTY_COLOR_OPT_ALWAYS, +}; + +struct pretty_options { + char *output_path; + + enum pretty_default name_default; + enum pretty_default field_default; + + bool print_scope_field_names; + bool print_header_field_names; + bool print_context_field_names; + bool print_payload_field_names; + + bool print_delta_field; + bool print_loglevel_field; + bool print_emf_field; + bool print_callsite_field; + bool print_trace_field; + bool print_trace_domain_field; + bool print_trace_procname_field; + bool print_trace_vpid_field; + bool print_trace_hostname_field; + + bool print_timestamp_cycles; + bool clock_seconds; + bool clock_date; + bool clock_gmt; + enum pretty_color_option color; + bool verbose; +}; + +struct pretty_component { + struct pretty_options options; + bt_self_component_port_input_message_iterator *iterator; + FILE *out, *err; + int depth; /* nesting, used for tabulation alignment. */ + bool start_line; + GString *string; + GString *tmp_string; + bt_value *plugin_opt_map; /* Temporary parameter map. */ + bool use_colors; + + uint64_t last_cycles_timestamp; + uint64_t delta_cycles; + + uint64_t last_real_timestamp; + uint64_t delta_real_timestamp; + + bool negative_timestamp_warning_done; +}; + +enum stream_packet_context_quarks_enum { + Q_TIMESTAMP_BEGIN, + Q_TIMESTAMP_END, + Q_EVENTS_DISCARDED, + Q_CONTENT_SIZE, + Q_PACKET_SIZE, + Q_PACKET_SEQ_NUM, + STREAM_PACKET_CONTEXT_QUARKS_LEN, /* Always the last one of this enum. */ +}; + +extern +GQuark stream_packet_context_quarks[STREAM_PACKET_CONTEXT_QUARKS_LEN]; + +BT_HIDDEN +bt_self_component_status pretty_init( + bt_self_component_sink *component, + const bt_value *params, + void *init_method_data); + +BT_HIDDEN +bt_self_component_status pretty_consume( + bt_self_component_sink *component); + +BT_HIDDEN +bt_self_component_status pretty_graph_is_configured( + bt_self_component_sink *component); + +BT_HIDDEN +void pretty_finalize(bt_self_component_sink *component); + +BT_HIDDEN +int pretty_print_event(struct pretty_component *pretty, + const bt_message *event_msg); + +BT_HIDDEN +int pretty_print_discarded_items(struct pretty_component *pretty, + const bt_message *msg); + +#endif /* BABELTRACE_PLUGIN_TEXT_PRETTY_PRETTY_H */ diff --git a/src/plugins/text/pretty/print.c b/src/plugins/text/pretty/print.c new file mode 100644 index 00000000..388a0915 --- /dev/null +++ b/src/plugins/text/pretty/print.c @@ -0,0 +1,1390 @@ +/* + * Copyright 2016 Jérémie Galarneau + * Copyright 2016 Mathieu Desnoyers + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "compat/bitfield.h" +#include "common/common.h" +#include "compat/time.h" +#include "common/assert.h" +#include +#include +#include "pretty.h" + +#define NSEC_PER_SEC 1000000000LL + +#define COLOR_NAME BT_COMMON_COLOR_BOLD +#define COLOR_FIELD_NAME BT_COMMON_COLOR_FG_CYAN +#define COLOR_RST BT_COMMON_COLOR_RESET +#define COLOR_STRING_VALUE BT_COMMON_COLOR_BOLD +#define COLOR_NUMBER_VALUE BT_COMMON_COLOR_BOLD +#define COLOR_ENUM_MAPPING_NAME BT_COMMON_COLOR_BOLD +#define COLOR_UNKNOWN BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_RED +#define COLOR_EVENT_NAME BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_MAGENTA +#define COLOR_TIMESTAMP BT_COMMON_COLOR_BOLD BT_COMMON_COLOR_FG_YELLOW + +struct timestamp { + int64_t real_timestamp; /* Relative to UNIX epoch. */ + uint64_t clock_snapshot; /* In cycles. */ +}; + +static +int print_field(struct pretty_component *pretty, + const bt_field *field, bool print_names, + GQuark *filters_fields, int filter_array_len); + +static +void print_name_equal(struct pretty_component *pretty, const char *name) +{ + if (pretty->use_colors) { + g_string_append_printf(pretty->string, "%s%s%s = ", COLOR_NAME, + name, COLOR_RST); + } else { + g_string_append_printf(pretty->string, "%s = ", name); + } +} + +static +void print_field_name_equal(struct pretty_component *pretty, const char *name) +{ + if (pretty->use_colors) { + g_string_append_printf(pretty->string, "%s%s%s = ", + COLOR_FIELD_NAME, name, COLOR_RST); + } else { + g_string_append_printf(pretty->string, "%s = ", name); + } +} + +static +void print_timestamp_cycles(struct pretty_component *pretty, + const bt_clock_snapshot *clock_snapshot, bool update_last) +{ + uint64_t cycles; + + cycles = bt_clock_snapshot_get_value(clock_snapshot); + g_string_append_printf(pretty->string, "%020" PRIu64, cycles); + + if (update_last) { + if (pretty->last_cycles_timestamp != -1ULL) { + pretty->delta_cycles = cycles - pretty->last_cycles_timestamp; + } + + pretty->last_cycles_timestamp = cycles; + } +} + +static +void print_timestamp_wall(struct pretty_component *pretty, + const bt_clock_snapshot *clock_snapshot, bool update_last) +{ + int ret; + int64_t ts_nsec = 0; /* add configurable offset */ + int64_t ts_sec = 0; /* add configurable offset */ + uint64_t ts_sec_abs, ts_nsec_abs; + bool is_negative; + + if (!clock_snapshot) { + g_string_append(pretty->string, "??:??:??.?????????"); + return; + } + + ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, &ts_nsec); + if (ret) { + // TODO: log, this is unexpected + g_string_append(pretty->string, "Error"); + return; + } + + if (update_last) { + if (pretty->last_real_timestamp != -1ULL) { + pretty->delta_real_timestamp = ts_nsec - pretty->last_real_timestamp; + } + + pretty->last_real_timestamp = ts_nsec; + } + + ts_sec += ts_nsec / NSEC_PER_SEC; + ts_nsec = ts_nsec % NSEC_PER_SEC; + + if (ts_sec >= 0 && ts_nsec >= 0) { + is_negative = false; + ts_sec_abs = ts_sec; + ts_nsec_abs = ts_nsec; + } else if (ts_sec > 0 && ts_nsec < 0) { + is_negative = false; + ts_sec_abs = ts_sec - 1; + ts_nsec_abs = NSEC_PER_SEC + ts_nsec; + } else if (ts_sec == 0 && ts_nsec < 0) { + is_negative = true; + ts_sec_abs = ts_sec; + ts_nsec_abs = -ts_nsec; + } else if (ts_sec < 0 && ts_nsec > 0) { + is_negative = true; + ts_sec_abs = -(ts_sec + 1); + ts_nsec_abs = NSEC_PER_SEC - ts_nsec; + } else if (ts_sec < 0 && ts_nsec == 0) { + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = ts_nsec; + } else { /* (ts_sec < 0 && ts_nsec < 0) */ + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = -ts_nsec; + } + + if (!pretty->options.clock_seconds) { + struct tm tm; + time_t time_s = (time_t) ts_sec_abs; + + if (is_negative && !pretty->negative_timestamp_warning_done) { + // TODO: log instead + fprintf(stderr, "[warning] Fallback to [sec.ns] to print negative time value. Use --clock-seconds.\n"); + pretty->negative_timestamp_warning_done = true; + goto seconds; + } + + if (!pretty->options.clock_gmt) { + struct tm *res; + + res = bt_localtime_r(&time_s, &tm); + if (!res) { + // TODO: log instead + fprintf(stderr, "[warning] Unable to get localtime.\n"); + goto seconds; + } + } else { + struct tm *res; + + res = bt_gmtime_r(&time_s, &tm); + if (!res) { + // TODO: log instead + fprintf(stderr, "[warning] Unable to get gmtime.\n"); + goto seconds; + } + } + if (pretty->options.clock_date) { + char timestr[26]; + size_t res; + + /* Print date and time */ + res = strftime(timestr, sizeof(timestr), + "%Y-%m-%d ", &tm); + if (!res) { + // TODO: log instead + fprintf(stderr, "[warning] Unable to print ascii time.\n"); + goto seconds; + } + + g_string_append(pretty->string, timestr); + } + + /* Print time in HH:MM:SS.ns */ + g_string_append_printf(pretty->string, + "%02d:%02d:%02d.%09" PRIu64, tm.tm_hour, tm.tm_min, + tm.tm_sec, ts_nsec_abs); + goto end; + } +seconds: + g_string_append_printf(pretty->string, "%s%" PRId64 ".%09" PRIu64, + is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); +end: + return; +} + +static +int print_event_timestamp(struct pretty_component *pretty, + const bt_message *event_msg, bool *start_line) +{ + bool print_names = pretty->options.print_header_field_names; + int ret = 0; + const bt_clock_snapshot *clock_snapshot = NULL; + + if (!bt_message_event_borrow_stream_class_default_clock_class_const( + event_msg)) { + /* No default clock class: skip the timestamp without an error */ + goto end; + } + + clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const(event_msg); + + if (print_names) { + print_name_equal(pretty, "timestamp"); + } else { + g_string_append(pretty->string, "["); + } + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_TIMESTAMP); + } + if (pretty->options.print_timestamp_cycles) { + print_timestamp_cycles(pretty, clock_snapshot, true); + } else { + print_timestamp_wall(pretty, clock_snapshot, true); + } + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + + if (!print_names) + g_string_append(pretty->string, "] "); + + if (pretty->options.print_delta_field) { + if (print_names) { + g_string_append(pretty->string, ", "); + print_name_equal(pretty, "delta"); + } else { + g_string_append(pretty->string, "("); + } + if (pretty->options.print_timestamp_cycles) { + if (pretty->delta_cycles == -1ULL) { + g_string_append(pretty->string, + "+??????????\?\?"); /* Not a trigraph. */ + } else { + g_string_append_printf(pretty->string, + "+%012" PRIu64, pretty->delta_cycles); + } + } else { + if (pretty->delta_real_timestamp != -1ULL) { + uint64_t delta_sec, delta_nsec, delta; + + delta = pretty->delta_real_timestamp; + delta_sec = delta / NSEC_PER_SEC; + delta_nsec = delta % NSEC_PER_SEC; + g_string_append_printf(pretty->string, + "+%" PRIu64 ".%09" PRIu64, + delta_sec, delta_nsec); + } else { + g_string_append(pretty->string, "+?.?????????"); + } + } + if (!print_names) { + g_string_append(pretty->string, ") "); + } + } + *start_line = !print_names; + +end: + return ret; +} + +static +int print_event_header(struct pretty_component *pretty, + const bt_message *event_msg) +{ + bool print_names = pretty->options.print_header_field_names; + int ret = 0; + const bt_event_class *event_class = NULL; + const bt_stream_class *stream_class = NULL; + const bt_trace_class *trace_class = NULL; + const bt_packet *packet = NULL; + const bt_stream *stream = NULL; + const bt_trace *trace = NULL; + const bt_event *event = bt_message_event_borrow_event_const(event_msg); + int dom_print = 0; + bt_property_availability prop_avail; + + event_class = bt_event_borrow_class_const(event); + stream_class = bt_event_class_borrow_stream_class_const(event_class); + trace_class = bt_stream_class_borrow_trace_class_const(stream_class); + packet = bt_event_borrow_packet_const(event); + stream = bt_packet_borrow_stream_const(packet); + trace = bt_stream_borrow_trace_const(stream); + ret = print_event_timestamp(pretty, event_msg, &pretty->start_line); + if (ret) { + goto end; + } + if (pretty->options.print_trace_field) { + const char *name; + + name = bt_trace_get_name(trace); + if (name) { + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "trace"); + } + + g_string_append(pretty->string, name); + + if (print_names) { + g_string_append(pretty->string, ", "); + } + } + } + if (pretty->options.print_trace_hostname_field) { + const bt_value *hostname_str; + + hostname_str = bt_trace_class_borrow_environment_entry_value_by_name_const( + trace_class, "hostname"); + if (hostname_str) { + const char *str; + + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "trace:hostname"); + } + str = bt_value_string_get(hostname_str); + g_string_append(pretty->string, str); + dom_print = 1; + } + } + if (pretty->options.print_trace_domain_field) { + const bt_value *domain_str; + + domain_str = bt_trace_class_borrow_environment_entry_value_by_name_const( + trace_class, "domain"); + if (domain_str) { + const char *str; + + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "trace:domain"); + } else if (dom_print) { + g_string_append(pretty->string, ":"); + } + str = bt_value_string_get(domain_str); + g_string_append(pretty->string, str); + dom_print = 1; + } + } + if (pretty->options.print_trace_procname_field) { + const bt_value *procname_str; + + procname_str = bt_trace_class_borrow_environment_entry_value_by_name_const( + trace_class, "procname"); + if (procname_str) { + const char *str; + + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "trace:procname"); + } else if (dom_print) { + g_string_append(pretty->string, ":"); + } + str = bt_value_string_get(procname_str); + g_string_append(pretty->string, str); + dom_print = 1; + } + } + if (pretty->options.print_trace_vpid_field) { + const bt_value *vpid_value; + + vpid_value = bt_trace_class_borrow_environment_entry_value_by_name_const( + trace_class, "vpid"); + if (vpid_value) { + int64_t value; + + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "trace:vpid"); + } else if (dom_print) { + g_string_append(pretty->string, ":"); + } + value = bt_value_signed_integer_get(vpid_value); + g_string_append_printf(pretty->string, + "(%" PRId64 ")", value); + dom_print = 1; + } + } + if (pretty->options.print_loglevel_field) { + static const char *log_level_names[] = { + [ BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY ] = "TRACE_EMERG", + [ BT_EVENT_CLASS_LOG_LEVEL_ALERT ] = "TRACE_ALERT", + [ BT_EVENT_CLASS_LOG_LEVEL_CRITICAL ] = "TRACE_CRIT", + [ BT_EVENT_CLASS_LOG_LEVEL_ERROR ] = "TRACE_ERR", + [ BT_EVENT_CLASS_LOG_LEVEL_WARNING ] = "TRACE_WARNING", + [ BT_EVENT_CLASS_LOG_LEVEL_NOTICE ] = "TRACE_NOTICE", + [ BT_EVENT_CLASS_LOG_LEVEL_INFO ] = "TRACE_INFO", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM ] = "TRACE_DEBUG_SYSTEM", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM ] = "TRACE_DEBUG_PROGRAM", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS ] = "TRACE_DEBUG_PROCESS", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE ] = "TRACE_DEBUG_MODULE", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT ] = "TRACE_DEBUG_UNIT", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION ] = "TRACE_DEBUG_FUNCTION", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE ] = "TRACE_DEBUG_LINE", + [ BT_EVENT_CLASS_LOG_LEVEL_DEBUG ] = "TRACE_DEBUG", + }; + bt_event_class_log_level log_level; + const char *log_level_str = NULL; + + prop_avail = bt_event_class_get_log_level(event_class, + &log_level); + if (prop_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { + log_level_str = log_level_names[log_level]; + BT_ASSERT(log_level_str); + + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "loglevel"); + } else if (dom_print) { + g_string_append(pretty->string, ":"); + } + + g_string_append(pretty->string, log_level_str); + g_string_append_printf( + pretty->string, " (%d)", (int) log_level); + dom_print = 1; + } + } + if (pretty->options.print_emf_field) { + const char *uri_str; + + uri_str = bt_event_class_get_emf_uri(event_class); + if (uri_str) { + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + if (print_names) { + print_name_equal(pretty, "model.emf.uri"); + } else if (dom_print) { + g_string_append(pretty->string, ":"); + } + + g_string_append(pretty->string, uri_str); + dom_print = 1; + } + } + if (dom_print && !print_names) { + g_string_append(pretty->string, " "); + } + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + pretty->start_line = true; + if (print_names) { + print_name_equal(pretty, "name"); + } + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_EVENT_NAME); + } + g_string_append(pretty->string, bt_event_class_get_name(event_class)); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + if (!print_names) { + g_string_append(pretty->string, ": "); + } else { + g_string_append(pretty->string, ", "); + } + +end: + return ret; +} + +static +int print_integer(struct pretty_component *pretty, + const bt_field *field) +{ + int ret = 0; + bt_field_class_integer_preferred_display_base base; + const bt_field_class *int_fc; + union { + uint64_t u; + int64_t s; + } v; + bool rst_color = false; + bt_field_class_type ft_type; + + int_fc = bt_field_borrow_class_const(field); + BT_ASSERT(int_fc); + ft_type = bt_field_get_class_type(field); + if (ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || + ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) { + v.u = bt_field_unsigned_integer_get_value(field); + } else { + v.s = bt_field_signed_integer_get_value(field); + } + + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_NUMBER_VALUE); + rst_color = true; + } + + base = bt_field_class_integer_get_preferred_display_base(int_fc); + switch (base) { + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY: + { + int bitnr, len; + + len = bt_field_class_integer_get_field_value_range(int_fc); + g_string_append(pretty->string, "0b"); + _bt_safe_lshift(v.u, 64 - len); + for (bitnr = 0; bitnr < len; bitnr++) { + g_string_append_printf(pretty->string, "%u", (v.u & (1ULL << 63)) ? 1 : 0); + _bt_safe_lshift(v.u, 1); + } + break; + } + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL: + { + if (ft_type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER || + ft_type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) { + int len; + + len = bt_field_class_integer_get_field_value_range( + int_fc); + if (len < 64) { + size_t rounded_len; + + BT_ASSERT(len != 0); + /* Round length to the nearest 3-bit */ + rounded_len = (((len - 1) / 3) + 1) * 3; + v.u &= ((uint64_t) 1 << rounded_len) - 1; + } + } + + g_string_append_printf(pretty->string, "0%" PRIo64, v.u); + break; + } + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL: + if (ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER || + ft_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) { + g_string_append_printf(pretty->string, "%" PRIu64, v.u); + } else { + g_string_append_printf(pretty->string, "%" PRId64, v.s); + } + break; + case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL: + { + int len; + + len = bt_field_class_integer_get_field_value_range(int_fc); + if (len < 64) { + /* Round length to the nearest nibble */ + uint8_t rounded_len = ((len + 3) & ~0x3); + + v.u &= ((uint64_t) 1 << rounded_len) - 1; + } + + g_string_append_printf(pretty->string, "0x%" PRIX64, v.u); + break; + } + default: + ret = -1; + goto end; + } +end: + if (rst_color) { + g_string_append(pretty->string, COLOR_RST); + } + return ret; +} + +static +void print_escape_string(struct pretty_component *pretty, const char *str) +{ + int i; + + g_string_append_c(pretty->string, '"'); + + for (i = 0; i < strlen(str); i++) { + /* Escape sequences not recognized by iscntrl(). */ + switch (str[i]) { + case '\\': + g_string_append(pretty->string, "\\\\"); + continue; + case '\'': + g_string_append(pretty->string, "\\\'"); + continue; + case '\"': + g_string_append(pretty->string, "\\\""); + continue; + case '\?': + g_string_append(pretty->string, "\\\?"); + continue; + } + + /* Standard characters. */ + if (!iscntrl(str[i])) { + g_string_append_c(pretty->string, str[i]); + continue; + } + + switch (str[i]) { + case '\0': + g_string_append(pretty->string, "\\0"); + break; + case '\a': + g_string_append(pretty->string, "\\a"); + break; + case '\b': + g_string_append(pretty->string, "\\b"); + break; + case '\e': + g_string_append(pretty->string, "\\e"); + break; + case '\f': + g_string_append(pretty->string, "\\f"); + break; + case '\n': + g_string_append(pretty->string, "\\n"); + break; + case '\r': + g_string_append(pretty->string, "\\r"); + break; + case '\t': + g_string_append(pretty->string, "\\t"); + break; + case '\v': + g_string_append(pretty->string, "\\v"); + break; + default: + /* Unhandled control-sequence, print as hex. */ + g_string_append_printf(pretty->string, "\\x%02x", str[i]); + break; + } + } + + g_string_append_c(pretty->string, '"'); +} + +static +int print_enum(struct pretty_component *pretty, + const bt_field *field) +{ + int ret = 0; + const bt_field_class *enumeration_field_class = NULL; + bt_field_class_enumeration_mapping_label_array label_array; + uint64_t label_count; + uint64_t i; + + enumeration_field_class = bt_field_borrow_class_const(field); + if (!enumeration_field_class) { + ret = -1; + goto end; + } + + switch (bt_field_get_class_type(field)) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + ret = bt_field_unsigned_enumeration_get_mapping_labels(field, + &label_array, &label_count); + break; + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + ret = bt_field_signed_enumeration_get_mapping_labels(field, + &label_array, &label_count); + break; + default: + abort(); + } + + if (ret) { + ret = -1; + goto end; + } + + g_string_append(pretty->string, "( "); + if (label_count == 0) { + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_UNKNOWN); + } + g_string_append(pretty->string, ""); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + goto skip_loop; + } + for (i = 0; i < label_count; i++) { + const char *mapping_name = label_array[i]; + + if (i != 0) { + g_string_append(pretty->string, ", "); + } + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_ENUM_MAPPING_NAME); + } + print_escape_string(pretty, mapping_name); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + } +skip_loop: + g_string_append(pretty->string, " : container = "); + ret = print_integer(pretty, field); + if (ret != 0) { + goto end; + } + g_string_append(pretty->string, " )"); +end: + return ret; +} + +static +int filter_field_name(struct pretty_component *pretty, const char *field_name, + GQuark *filter_fields, int filter_array_len) +{ + int i; + GQuark field_quark = g_quark_try_string(field_name); + + if (!field_quark || pretty->options.verbose) { + return 1; + } + + for (i = 0; i < filter_array_len; i++) { + if (field_quark == filter_fields[i]) { + return 0; + } + } + return 1; +} + +static +int print_struct_field(struct pretty_component *pretty, + const bt_field *_struct, + const bt_field_class *struct_class, + uint64_t i, bool print_names, uint64_t *nr_printed_fields, + GQuark *filter_fields, int filter_array_len) +{ + int ret = 0; + const char *field_name; + const bt_field *field = NULL; + const bt_field_class_structure_member *member; + + field = bt_field_structure_borrow_member_field_by_index_const(_struct, i); + if (!field) { + ret = -1; + goto end; + } + + member = bt_field_class_structure_borrow_member_by_index_const( + struct_class, i); + field_name = bt_field_class_structure_member_get_name(member); + + if (filter_fields && !filter_field_name(pretty, field_name, + filter_fields, filter_array_len)) { + ret = 0; + goto end; + } + + if (*nr_printed_fields > 0) { + g_string_append(pretty->string, ", "); + } else { + g_string_append(pretty->string, " "); + } + if (print_names) { + print_field_name_equal(pretty, field_name); + } + ret = print_field(pretty, field, print_names, NULL, 0); + *nr_printed_fields += 1; + +end: + return ret; +} + +static +int print_struct(struct pretty_component *pretty, + const bt_field *_struct, bool print_names, + GQuark *filter_fields, int filter_array_len) +{ + int ret = 0; + const bt_field_class *struct_class = NULL; + uint64_t nr_fields, i, nr_printed_fields; + + struct_class = bt_field_borrow_class_const(_struct); + if (!struct_class) { + ret = -1; + goto end; + } + + nr_fields = bt_field_class_structure_get_member_count(struct_class); + + g_string_append(pretty->string, "{"); + pretty->depth++; + nr_printed_fields = 0; + for (i = 0; i < nr_fields; i++) { + ret = print_struct_field(pretty, _struct, struct_class, i, + print_names, &nr_printed_fields, filter_fields, + filter_array_len); + if (ret != 0) { + goto end; + } + } + pretty->depth--; + g_string_append(pretty->string, " }"); + +end: + return ret; +} + +static +int print_array_field(struct pretty_component *pretty, + const bt_field *array, uint64_t i, bool print_names) +{ + const bt_field *field = NULL; + + if (i != 0) { + g_string_append(pretty->string, ", "); + } else { + g_string_append(pretty->string, " "); + } + if (print_names) { + g_string_append_printf(pretty->string, "[%" PRIu64 "] = ", i); + } + + field = bt_field_array_borrow_element_field_by_index_const(array, i); + BT_ASSERT(field); + return print_field(pretty, field, print_names, NULL, 0); +} + +static +int print_array(struct pretty_component *pretty, + const bt_field *array, bool print_names) +{ + int ret = 0; + const bt_field_class *array_class = NULL; + uint64_t len; + uint64_t i; + + array_class = bt_field_borrow_class_const(array); + if (!array_class) { + ret = -1; + goto end; + } + len = bt_field_array_get_length(array); + g_string_append(pretty->string, "["); + pretty->depth++; + for (i = 0; i < len; i++) { + ret = print_array_field(pretty, array, i, print_names); + if (ret != 0) { + goto end; + } + } + pretty->depth--; + g_string_append(pretty->string, " ]"); + +end: + return ret; +} + +static +int print_sequence_field(struct pretty_component *pretty, + const bt_field *seq, uint64_t i, bool print_names) +{ + const bt_field *field = NULL; + + if (i != 0) { + g_string_append(pretty->string, ", "); + } else { + g_string_append(pretty->string, " "); + } + if (print_names) { + g_string_append_printf(pretty->string, "[%" PRIu64 "] = ", i); + } + + field = bt_field_array_borrow_element_field_by_index_const(seq, i); + BT_ASSERT(field); + return print_field(pretty, field, print_names, NULL, 0); +} + +static +int print_sequence(struct pretty_component *pretty, + const bt_field *seq, bool print_names) +{ + int ret = 0; + uint64_t len; + uint64_t i; + + len = bt_field_array_get_length(seq); + g_string_append(pretty->string, "["); + + pretty->depth++; + for (i = 0; i < len; i++) { + ret = print_sequence_field(pretty, seq, i, print_names); + if (ret != 0) { + goto end; + } + } + pretty->depth--; + g_string_append(pretty->string, " ]"); + +end: + return ret; +} + +static +int print_variant(struct pretty_component *pretty, + const bt_field *variant, bool print_names) +{ + int ret = 0; + const bt_field *field = NULL; + + field = bt_field_variant_borrow_selected_option_field_const(variant); + BT_ASSERT(field); + g_string_append(pretty->string, "{ "); + pretty->depth++; + if (print_names) { + // TODO: find tag's name using field path + // print_field_name_equal(pretty, tag_choice); + } + ret = print_field(pretty, field, print_names, NULL, 0); + if (ret != 0) { + goto end; + } + pretty->depth--; + g_string_append(pretty->string, " }"); + +end: + return ret; +} + +static +int print_field(struct pretty_component *pretty, + const bt_field *field, bool print_names, + GQuark *filter_fields, int filter_array_len) +{ + bt_field_class_type class_id; + + class_id = bt_field_get_class_type(field); + switch (class_id) { + case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER: + case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER: + return print_integer(pretty, field); + case BT_FIELD_CLASS_TYPE_REAL: + { + double v; + + v = bt_field_real_get_value(field); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_NUMBER_VALUE); + } + g_string_append_printf(pretty->string, "%g", v); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + return 0; + } + case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: + case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION: + return print_enum(pretty, field); + case BT_FIELD_CLASS_TYPE_STRING: + { + const char *str; + + str = bt_field_string_get_value(field); + if (!str) { + return -1; + } + + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_STRING_VALUE); + } + print_escape_string(pretty, str); + if (pretty->use_colors) { + g_string_append(pretty->string, COLOR_RST); + } + return 0; + } + case BT_FIELD_CLASS_TYPE_STRUCTURE: + return print_struct(pretty, field, print_names, filter_fields, + filter_array_len); + case BT_FIELD_CLASS_TYPE_VARIANT: + return print_variant(pretty, field, print_names); + case BT_FIELD_CLASS_TYPE_STATIC_ARRAY: + return print_array(pretty, field, print_names); + case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY: + return print_sequence(pretty, field, print_names); + default: + // TODO: log instead + fprintf(pretty->err, "[error] Unknown type id: %d\n", (int) class_id); + return -1; + } +} + +static +int print_stream_packet_context(struct pretty_component *pretty, + const bt_event *event) +{ + int ret = 0; + const bt_packet *packet = NULL; + const bt_field *main_field = NULL; + + packet = bt_event_borrow_packet_const(event); + if (!packet) { + ret = -1; + goto end; + } + main_field = bt_packet_borrow_context_field_const(packet); + if (!main_field) { + goto end; + } + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + pretty->start_line = false; + if (pretty->options.print_scope_field_names) { + print_name_equal(pretty, "stream.packet.context"); + } + ret = print_field(pretty, main_field, + pretty->options.print_context_field_names, + stream_packet_context_quarks, + STREAM_PACKET_CONTEXT_QUARKS_LEN); + +end: + return ret; +} + +static +int print_stream_event_context(struct pretty_component *pretty, + const bt_event *event) +{ + int ret = 0; + const bt_field *main_field = NULL; + + main_field = bt_event_borrow_common_context_field_const(event); + if (!main_field) { + goto end; + } + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + pretty->start_line = false; + if (pretty->options.print_scope_field_names) { + print_name_equal(pretty, "stream.event.context"); + } + ret = print_field(pretty, main_field, + pretty->options.print_context_field_names, NULL, 0); + +end: + return ret; +} + +static +int print_event_context(struct pretty_component *pretty, + const bt_event *event) +{ + int ret = 0; + const bt_field *main_field = NULL; + + main_field = bt_event_borrow_specific_context_field_const(event); + if (!main_field) { + goto end; + } + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + pretty->start_line = false; + if (pretty->options.print_scope_field_names) { + print_name_equal(pretty, "event.context"); + } + ret = print_field(pretty, main_field, + pretty->options.print_context_field_names, NULL, 0); + +end: + return ret; +} + +static +int print_event_payload(struct pretty_component *pretty, + const bt_event *event) +{ + int ret = 0; + const bt_field *main_field = NULL; + + main_field = bt_event_borrow_payload_field_const(event); + if (!main_field) { + goto end; + } + if (!pretty->start_line) { + g_string_append(pretty->string, ", "); + } + pretty->start_line = false; + if (pretty->options.print_scope_field_names) { + print_name_equal(pretty, "event.fields"); + } + ret = print_field(pretty, main_field, + pretty->options.print_payload_field_names, NULL, 0); + +end: + return ret; +} + +static +int flush_buf(FILE *stream, struct pretty_component *pretty) +{ + int ret = 0; + + if (pretty->string->len == 0) { + goto end; + } + + if (fwrite(pretty->string->str, pretty->string->len, 1, stream) != 1) { + ret = -1; + } + +end: + return ret; +} + +BT_HIDDEN +int pretty_print_event(struct pretty_component *pretty, + const bt_message *event_msg) +{ + int ret; + const bt_event *event = + bt_message_event_borrow_event_const(event_msg); + + BT_ASSERT(event); + pretty->start_line = true; + g_string_assign(pretty->string, ""); + ret = print_event_header(pretty, event_msg); + if (ret != 0) { + goto end; + } + + ret = print_stream_packet_context(pretty, event); + if (ret != 0) { + goto end; + } + + ret = print_stream_event_context(pretty, event); + if (ret != 0) { + goto end; + } + + ret = print_event_context(pretty, event); + if (ret != 0) { + goto end; + } + + ret = print_event_payload(pretty, event); + if (ret != 0) { + goto end; + } + + g_string_append_c(pretty->string, '\n'); + if (flush_buf(pretty->out, pretty)) { + ret = -1; + goto end; + } + +end: + return ret; +} + +static +int print_discarded_elements_msg(struct pretty_component *pretty, + const bt_stream *stream, + const bt_clock_snapshot *begin_clock_snapshot, + const bt_clock_snapshot *end_clock_snapshot, + uint64_t count, const char *elem_type) +{ + int ret = 0; + const bt_stream_class *stream_class = NULL; + const bt_trace *trace = NULL; + const char *stream_name; + const char *trace_name; + bt_uuid trace_uuid; + int64_t stream_class_id; + int64_t stream_id; + const char *init_msg; + + /* Stream name */ + stream_name = bt_stream_get_name(stream); + if (!stream_name) { + stream_name = "(unknown)"; + } + + /* Stream class ID */ + stream_class = bt_stream_borrow_class_const(stream); + BT_ASSERT(stream_class); + stream_class_id = bt_stream_class_get_id(stream_class); + + /* Stream ID */ + stream_id = bt_stream_get_id(stream); + + /* Trace name */ + trace = bt_stream_borrow_trace_const(stream); + BT_ASSERT(trace); + trace_name = bt_trace_get_name(trace); + if (!trace_name) { + trace_name = "(unknown)"; + } + + /* Trace UUID */ + trace_uuid = bt_trace_class_get_uuid( + bt_trace_borrow_class_const(trace)); + + /* Format message */ + g_string_assign(pretty->string, ""); + + if (count == UINT64_C(-1)) { + init_msg = "Tracer may have discarded"; + } else { + init_msg = "Tracer discarded"; + } + + g_string_append_printf(pretty->string, + "%s%sWARNING%s%s: %s ", + bt_common_color_fg_yellow(), + bt_common_color_bold(), + bt_common_color_reset(), + bt_common_color_fg_yellow(), init_msg); + + if (count == UINT64_C(-1)) { + g_string_append_printf(pretty->string, "%ss", elem_type); + } else { + g_string_append_printf(pretty->string, + "%" PRIu64 " %s%s", count, elem_type, + count == 1 ? "" : "s"); + } + + g_string_append_c(pretty->string, ' '); + + if (begin_clock_snapshot && end_clock_snapshot) { + g_string_append(pretty->string, "between ["); + print_timestamp_wall(pretty, begin_clock_snapshot, false); + g_string_append(pretty->string, "] and ["); + print_timestamp_wall(pretty, end_clock_snapshot, false); + g_string_append(pretty->string, "]"); + } else { + g_string_append(pretty->string, "(unknown time range)"); + } + + g_string_append_printf(pretty->string, " in trace \"%s\" ", trace_name); + + if (trace_uuid) { + g_string_append_printf(pretty->string, + "(UUID: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x) ", + trace_uuid[0], + trace_uuid[1], + trace_uuid[2], + trace_uuid[3], + trace_uuid[4], + trace_uuid[5], + trace_uuid[6], + trace_uuid[7], + trace_uuid[8], + trace_uuid[9], + trace_uuid[10], + trace_uuid[11], + trace_uuid[12], + trace_uuid[13], + trace_uuid[14], + trace_uuid[15]); + } else { + g_string_append(pretty->string, "(no UUID) "); + } + + g_string_append_printf(pretty->string, + "within stream \"%s\" (stream class ID: %" PRIu64 ", ", + stream_name, stream_class_id); + + if (stream_id >= 0) { + g_string_append_printf(pretty->string, + "stream ID: %" PRIu64, stream_id); + } else { + g_string_append(pretty->string, "no stream ID"); + } + + g_string_append_printf(pretty->string, ").%s\n", + bt_common_color_reset()); + + /* + * Print to standard error stream to remain backward compatible + * with Babeltrace 1. + */ + if (flush_buf(stderr, pretty)) { + ret = -1; + } + + return ret; +} + +BT_HIDDEN +int pretty_print_discarded_items(struct pretty_component *pretty, + const bt_message *msg) +{ + const bt_clock_snapshot *begin = NULL; + const bt_clock_snapshot *end = NULL; + const bt_stream *stream; + const bt_stream_class *stream_class; + uint64_t count = UINT64_C(-1); + const char *elem_type; + + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + stream = bt_message_discarded_events_borrow_stream_const(msg); + + if (bt_message_discarded_events_get_count(msg, &count) == + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { + count = UINT64_C(-1); + } + + elem_type = "event"; + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + stream = bt_message_discarded_packets_borrow_stream_const(msg); + + if (bt_message_discarded_packets_get_count(msg, &count) == + BT_PROPERTY_AVAILABILITY_NOT_AVAILABLE) { + count = UINT64_C(-1); + } + + elem_type = "packet"; + break; + default: + abort(); + } + + BT_ASSERT(stream); + stream_class = bt_stream_borrow_class_const(stream); + + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + if (bt_stream_class_discarded_events_have_default_clock_snapshots( + stream_class)) { + begin = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + end = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + msg); + } + + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + if (bt_stream_class_discarded_packets_have_default_clock_snapshots( + stream_class)) { + begin = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + end = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + msg); + } + + break; + default: + abort(); + } + + print_discarded_elements_msg(pretty, stream, begin, end, + count, elem_type); + return 0; +} diff --git a/src/plugins/utils/Makefile.am b/src/plugins/utils/Makefile.am new file mode 100644 index 00000000..67f34f22 --- /dev/null +++ b/src/plugins/utils/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = dummy muxer counter trimmer + +plugindir = "$(PLUGINSDIR)" +plugin_LTLIBRARIES = babeltrace-plugin-utils.la + +babeltrace_plugin_utils_la_SOURCES = plugin.c +babeltrace_plugin_utils_la_LDFLAGS = \ + $(LT_NO_UNDEFINED) \ + -avoid-version -module +babeltrace_plugin_utils_la_LIBADD = \ + dummy/libbabeltrace2-plugin-dummy-cc.la \ + muxer/libbabeltrace2-plugin-muxer.la \ + counter/libbabeltrace2-plugin-counter-cc.la \ + trimmer/libbabeltrace2-plugin-trimmer.la + +if !ENABLE_BUILT_IN_PLUGINS +babeltrace_plugin_utils_la_LIBADD += \ + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la +endif diff --git a/src/plugins/utils/counter/Makefile.am b/src/plugins/utils/counter/Makefile.am new file mode 100644 index 00000000..ef060f1a --- /dev/null +++ b/src/plugins/utils/counter/Makefile.am @@ -0,0 +1,6 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-counter-cc.la +libbabeltrace2_plugin_counter_cc_la_SOURCES = \ + counter.c \ + counter.h \ + logging.c \ + logging.h diff --git a/src/plugins/utils/counter/counter.c b/src/plugins/utils/counter/counter.c new file mode 100644 index 00000000..37ddd030 --- /dev/null +++ b/src/plugins/utils/counter/counter.c @@ -0,0 +1,321 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-UTILS-COUNTER-FLT" +#include "logging.h" + +#include +#include "common/babeltrace.h" +#include "common/common.h" +#include "plugins/plugins-common.h" +#include "common/assert.h" +#include +#include + +#include "counter.h" + +#define PRINTF_COUNT(_what, _var, args...) \ + do { \ + if (counter->count._var != 0 || !counter->hide_zero) { \ + printf("%15" PRIu64 " %s message%s\n", \ + counter->count._var, \ + (_what), \ + counter->count._var == 1 ? "" : "s"); \ + } \ + } while (0) + +static +const char * const in_port_name = "in"; + +static +uint64_t get_total_count(struct counter *counter) +{ + return counter->count.event + + counter->count.stream_begin + + counter->count.stream_end + + counter->count.stream_activity_begin + + counter->count.stream_activity_end + + counter->count.packet_begin + + counter->count.packet_end + + counter->count.disc_events + + counter->count.disc_packets + + counter->count.msg_iter_inactivity + + counter->count.other; +} + +static +void print_count(struct counter *counter) +{ + uint64_t total = get_total_count(counter); + + PRINTF_COUNT("Event", event); + PRINTF_COUNT("Stream beginning", stream_begin); + PRINTF_COUNT("Stream end", stream_end); + PRINTF_COUNT("Stream activity beginning", stream_activity_begin); + PRINTF_COUNT("Stream activity end", stream_activity_end); + PRINTF_COUNT("Packet beginning", packet_begin); + PRINTF_COUNT("Packet end", packet_end); + PRINTF_COUNT("Discarded event", disc_events); + PRINTF_COUNT("Discarded packet", disc_packets); + PRINTF_COUNT("Message iterator inactivity", msg_iter_inactivity); + + if (counter->count.other > 0) { + PRINTF_COUNT("Other (unknown)", other); + } + + printf("%s%15" PRIu64 " message%s (TOTAL)%s\n", + bt_common_color_bold(), total, total == 1 ? "" : "s", + bt_common_color_reset()); + counter->last_printed_total = total; +} + +static +void try_print_count(struct counter *counter, uint64_t msg_count) +{ + if (counter->step == 0) { + /* No update */ + return; + } + + counter->at += msg_count; + + if (counter->at >= counter->step) { + counter->at = 0; + print_count(counter); + putchar('\n'); + } +} + +static +void try_print_last(struct counter *counter) +{ + const uint64_t total = get_total_count(counter); + + if (total != counter->last_printed_total) { + print_count(counter); + } +} + +void destroy_private_counter_data(struct counter *counter) +{ + bt_self_component_port_input_message_iterator_put_ref(counter->msg_iter); + g_free(counter); +} + +BT_HIDDEN +void counter_finalize(bt_self_component_sink *comp) +{ + struct counter *counter; + + BT_ASSERT(comp); + counter = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(counter); + try_print_last(counter); + bt_self_component_port_input_message_iterator_put_ref(counter->msg_iter); + g_free(counter); +} + +BT_HIDDEN +bt_self_component_status counter_init( + bt_self_component_sink *component, + const bt_value *params, + UNUSED_VAR void *init_method_data) +{ + bt_self_component_status ret; + struct counter *counter = g_new0(struct counter, 1); + const bt_value *step = NULL; + const bt_value *hide_zero = NULL; + + if (!counter) { + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto error; + } + + ret = bt_self_component_sink_add_input_port(component, + "in", NULL, NULL); + if (ret != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + counter->last_printed_total = -1ULL; + counter->step = 10000; + step = bt_value_map_borrow_entry_value_const(params, "step"); + if (step) { + if (!bt_value_is_unsigned_integer(step)) { + BT_LOGE("`step` parameter: expecting an unsigned integer value: " + "type=%s", bt_common_value_type_string( + bt_value_get_type(step))); + goto error; + } + + counter->step = bt_value_unsigned_integer_get(step); + } + + hide_zero = bt_value_map_borrow_entry_value_const(params, "hide-zero"); + if (hide_zero) { + if (!bt_value_is_bool(hide_zero)) { + BT_LOGE("`hide-zero` parameter: expecting a boolean value: " + "type=%s", bt_common_value_type_string( + bt_value_get_type(hide_zero))); + goto error; + } + + counter->hide_zero = (bool) bt_value_bool_get(hide_zero); + } + + bt_self_component_set_data( + bt_self_component_sink_as_self_component(component), + counter); + goto end; + +error: + destroy_private_counter_data(counter); + ret = BT_SELF_COMPONENT_STATUS_ERROR; + +end: + return ret; +} + +BT_HIDDEN +bt_self_component_status counter_graph_is_configured( + bt_self_component_sink *comp) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct counter *counter; + bt_self_component_port_input_message_iterator *iterator; + + counter = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(counter); + iterator = bt_self_component_port_input_message_iterator_create( + bt_self_component_sink_borrow_input_port_by_name(comp, + in_port_name)); + if (!iterator) { + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( + counter->msg_iter, iterator); + +end: + return status; +} + +BT_HIDDEN +bt_self_component_status counter_consume( + bt_self_component_sink *comp) +{ + bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; + struct counter *counter; + bt_message_iterator_status it_ret; + uint64_t msg_count; + bt_message_array_const msgs; + + counter = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(counter); + + if (unlikely(!counter->msg_iter)) { + try_print_last(counter); + ret = BT_SELF_COMPONENT_STATUS_END; + goto end; + } + + /* Consume messages */ + it_ret = bt_self_component_port_input_message_iterator_next( + counter->msg_iter, &msgs, &msg_count); + if (it_ret < 0) { + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + + switch (it_ret) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + { + uint64_t i; + + for (i = 0; i < msg_count; i++) { + const bt_message *msg = msgs[i]; + + BT_ASSERT(msg); + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_EVENT: + counter->count.event++; + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + counter->count.packet_begin++; + break; + case BT_MESSAGE_TYPE_PACKET_END: + counter->count.packet_end++; + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + counter->count.msg_iter_inactivity++; + break; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + counter->count.stream_begin++; + break; + case BT_MESSAGE_TYPE_STREAM_END: + counter->count.stream_end++; + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + counter->count.stream_activity_begin++; + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + counter->count.stream_activity_end++; + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + counter->count.disc_events++; + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + counter->count.disc_packets++; + break; + default: + counter->count.other++; + } + + bt_message_put_ref(msg); + } + + ret = BT_SELF_COMPONENT_STATUS_OK; + break; + } + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + ret = BT_SELF_COMPONENT_STATUS_AGAIN; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_END: + try_print_last(counter); + ret = BT_SELF_COMPONENT_STATUS_END; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + default: + break; + } + + try_print_count(counter, msg_count); + +end: + return ret; +} diff --git a/src/plugins/utils/counter/counter.h b/src/plugins/utils/counter/counter.h new file mode 100644 index 00000000..c29f0fd5 --- /dev/null +++ b/src/plugins/utils/counter/counter.h @@ -0,0 +1,67 @@ +#ifndef BABELTRACE_PLUGINS_UTILS_COUNTER_H +#define BABELTRACE_PLUGINS_UTILS_COUNTER_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +struct counter { + bt_self_component_port_input_message_iterator *msg_iter; + struct { + uint64_t event; + uint64_t stream_begin; + uint64_t stream_end; + uint64_t stream_activity_begin; + uint64_t stream_activity_end; + uint64_t packet_begin; + uint64_t packet_end; + uint64_t disc_events; + uint64_t disc_packets; + uint64_t msg_iter_inactivity; + uint64_t other; + } count; + uint64_t last_printed_total; + uint64_t at; + uint64_t step; + bool hide_zero; +}; + +BT_HIDDEN +bt_self_component_status counter_init( + bt_self_component_sink *component, + const bt_value *params, void *init_method_data); + +BT_HIDDEN +void counter_finalize(bt_self_component_sink *component); + +BT_HIDDEN +bt_self_component_status counter_graph_is_configured( + bt_self_component_sink *component); + +BT_HIDDEN +bt_self_component_status counter_consume(bt_self_component_sink *component); + +#endif /* BABELTRACE_PLUGINS_UTILS_COUNTER_H */ diff --git a/src/plugins/utils/counter/logging.c b/src/plugins/utils/counter/logging.c new file mode 100644 index 00000000..6fc046e1 --- /dev/null +++ b/src/plugins/utils/counter/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_counter_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_counter_log_level, + "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL"); diff --git a/src/plugins/utils/counter/logging.h b/src/plugins/utils/counter/logging.h new file mode 100644 index 00000000..83192434 --- /dev/null +++ b/src/plugins/utils/counter/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_UTILS_COUNTER_LOGGING_H +#define PLUGINS_UTILS_COUNTER_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_counter_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_counter_log_level); + +#endif /* PLUGINS_UTILS_COUNTER_LOGGING_H */ diff --git a/src/plugins/utils/dummy/Makefile.am b/src/plugins/utils/dummy/Makefile.am new file mode 100644 index 00000000..c0eb371f --- /dev/null +++ b/src/plugins/utils/dummy/Makefile.am @@ -0,0 +1,2 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-dummy-cc.la +libbabeltrace2_plugin_dummy_cc_la_SOURCES = dummy.c dummy.h diff --git a/src/plugins/utils/dummy/dummy.c b/src/plugins/utils/dummy/dummy.c new file mode 100644 index 00000000..5da07ea2 --- /dev/null +++ b/src/plugins/utils/dummy/dummy.c @@ -0,0 +1,158 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include "plugins/plugins-common.h" +#include "common/assert.h" +#include "dummy.h" + +static +const char * const in_port_name = "in"; + +void destroy_private_dummy_data(struct dummy *dummy) +{ + bt_self_component_port_input_message_iterator_put_ref(dummy->msg_iter); + g_free(dummy); + +} + +BT_HIDDEN +void dummy_finalize(bt_self_component_sink *comp) +{ + struct dummy *dummy; + + BT_ASSERT(comp); + dummy = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(dummy); + destroy_private_dummy_data(dummy); +} + +BT_HIDDEN +bt_self_component_status dummy_init( + bt_self_component_sink *component, + const bt_value *params, + UNUSED_VAR void *init_method_data) +{ + bt_self_component_status ret; + struct dummy *dummy = g_new0(struct dummy, 1); + + if (!dummy) { + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + ret = bt_self_component_sink_add_input_port(component, + "in", NULL, NULL); + if (ret != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + bt_self_component_set_data( + bt_self_component_sink_as_self_component(component), dummy); + goto end; + +error: + destroy_private_dummy_data(dummy); + +end: + return ret; +} + +BT_HIDDEN +bt_self_component_status dummy_graph_is_configured( + bt_self_component_sink *comp) +{ + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct dummy *dummy; + bt_self_component_port_input_message_iterator *iterator; + + dummy = bt_self_component_get_data( + bt_self_component_sink_as_self_component(comp)); + BT_ASSERT(dummy); + iterator = bt_self_component_port_input_message_iterator_create( + bt_self_component_sink_borrow_input_port_by_name(comp, + in_port_name)); + if (!iterator) { + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_MOVE_REF( + dummy->msg_iter, iterator); + +end: + return status; +} + +BT_HIDDEN +bt_self_component_status dummy_consume( + bt_self_component_sink *component) +{ + bt_self_component_status ret = BT_SELF_COMPONENT_STATUS_OK; + bt_message_array_const msgs; + uint64_t count; + struct dummy *dummy; + bt_message_iterator_status it_ret; + uint64_t i; + + dummy = bt_self_component_get_data( + bt_self_component_sink_as_self_component(component)); + BT_ASSERT(dummy); + + if (unlikely(!dummy->msg_iter)) { + ret = BT_SELF_COMPONENT_STATUS_END; + goto end; + } + + /* Consume one message */ + it_ret = bt_self_component_port_input_message_iterator_next( + dummy->msg_iter, &msgs, &count); + switch (it_ret) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + ret = BT_SELF_COMPONENT_STATUS_OK; + + for (i = 0; i < count; i++) { + bt_message_put_ref(msgs[i]); + } + + break; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + ret = BT_SELF_COMPONENT_STATUS_AGAIN; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_END: + ret = BT_SELF_COMPONENT_STATUS_END; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_ERROR: + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + case BT_MESSAGE_ITERATOR_STATUS_NOMEM: + ret = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + default: + break; + } + +end: + return ret; +} diff --git a/src/plugins/utils/dummy/dummy.h b/src/plugins/utils/dummy/dummy.h new file mode 100644 index 00000000..ea44ecb9 --- /dev/null +++ b/src/plugins/utils/dummy/dummy.h @@ -0,0 +1,51 @@ +#ifndef BABELTRACE_PLUGINS_UTILS_DUMMY_H +#define BABELTRACE_PLUGINS_UTILS_DUMMY_H + +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" +#include + +struct dummy { + bt_self_component_port_input_message_iterator *msg_iter; +}; + +BT_HIDDEN +bt_self_component_status dummy_init( + bt_self_component_sink *component, + const bt_value *params, void *init_method_data); + +BT_HIDDEN +void dummy_finalize(bt_self_component_sink *component); + +BT_HIDDEN +bt_self_component_status dummy_graph_is_configured( + bt_self_component_sink *comp); + +BT_HIDDEN +bt_self_component_status dummy_consume( + bt_self_component_sink *component); + +#endif /* BABELTRACE_PLUGINS_UTILS_DUMMY_H */ diff --git a/src/plugins/utils/muxer/Makefile.am b/src/plugins/utils/muxer/Makefile.am new file mode 100644 index 00000000..3eeb008a --- /dev/null +++ b/src/plugins/utils/muxer/Makefile.am @@ -0,0 +1,2 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-muxer.la +libbabeltrace2_plugin_muxer_la_SOURCES = muxer.c muxer.h logging.c logging.h diff --git a/src/plugins/utils/muxer/logging.c b/src/plugins/utils/muxer/logging.c new file mode 100644 index 00000000..1bf5383c --- /dev/null +++ b/src/plugins/utils/muxer/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_muxer_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_muxer_log_level, + "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL"); diff --git a/src/plugins/utils/muxer/logging.h b/src/plugins/utils/muxer/logging.h new file mode 100644 index 00000000..ebb3538a --- /dev/null +++ b/src/plugins/utils/muxer/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_UTILS_MUXER_LOGGING_H +#define PLUGINS_UTILS_MUXER_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_muxer_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_muxer_log_level); + +#endif /* PLUGINS_UTILS_MUXER_LOGGING_H */ diff --git a/src/plugins/utils/muxer/muxer.c b/src/plugins/utils/muxer/muxer.c new file mode 100644 index 00000000..b575f967 --- /dev/null +++ b/src/plugins/utils/muxer/muxer.c @@ -0,0 +1,1534 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-UTILS-MUXER-FLT" +#include "logging.h" + +#include "common/babeltrace.h" +#include "compat/uuid.h" +#include +#include "lib/value.h" +#include "lib/graph/component.h" +#include "lib/graph/message/iterator.h" +#include "lib/graph/connection.h" +#include "plugins/plugins-common.h" +#include +#include +#include +#include "common/assert.h" +#include "common/common.h" +#include +#include + +#include "muxer.h" + +#define ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME "assume-absolute-clock-classes" + +struct muxer_comp { + /* Weak ref */ + bt_self_component_filter *self_comp; + + unsigned int next_port_num; + size_t available_input_ports; + bool initializing_muxer_msg_iter; + bool assume_absolute_clock_classes; +}; + +struct muxer_upstream_msg_iter { + /* Owned by this, NULL if ended */ + bt_self_component_port_input_message_iterator *msg_iter; + + /* Contains `const bt_message *`, owned by this */ + GQueue *msgs; +}; + +enum muxer_msg_iter_clock_class_expectation { + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY = 0, + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE, + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE, + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID, + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID, +}; + +struct muxer_msg_iter { + /* + * Array of struct muxer_upstream_msg_iter * (owned by this). + * + * NOTE: This array is searched in linearly to find the youngest + * current message. Keep this until benchmarks confirm that + * another data structure is faster than this for our typical + * use cases. + */ + GPtrArray *active_muxer_upstream_msg_iters; + + /* + * Array of struct muxer_upstream_msg_iter * (owned by this). + * + * We move ended message iterators from + * `active_muxer_upstream_msg_iters` to this array so as to be + * able to restore them when seeking. + */ + GPtrArray *ended_muxer_upstream_msg_iters; + + /* Last time returned in a message */ + int64_t last_returned_ts_ns; + + /* Clock class expectation state */ + enum muxer_msg_iter_clock_class_expectation clock_class_expectation; + + /* + * Expected clock class UUID, only valid when + * clock_class_expectation is + * MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID. + */ + unsigned char expected_clock_class_uuid[BABELTRACE_UUID_LEN]; +}; + +static +void empty_message_queue(struct muxer_upstream_msg_iter *upstream_msg_iter) +{ + const bt_message *msg; + + while ((msg = g_queue_pop_head(upstream_msg_iter->msgs))) { + bt_message_put_ref(msg); + } +} + +static +void destroy_muxer_upstream_msg_iter( + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter) +{ + if (!muxer_upstream_msg_iter) { + return; + } + + BT_LOGD("Destroying muxer's upstream message iterator wrapper: " + "addr=%p, msg-iter-addr=%p, queue-len=%u", + muxer_upstream_msg_iter, + muxer_upstream_msg_iter->msg_iter, + muxer_upstream_msg_iter->msgs->length); + bt_self_component_port_input_message_iterator_put_ref( + muxer_upstream_msg_iter->msg_iter); + + if (muxer_upstream_msg_iter->msgs) { + empty_message_queue(muxer_upstream_msg_iter); + g_queue_free(muxer_upstream_msg_iter->msgs); + } + + g_free(muxer_upstream_msg_iter); +} + +static +int muxer_msg_iter_add_upstream_msg_iter(struct muxer_msg_iter *muxer_msg_iter, + bt_self_component_port_input_message_iterator *self_msg_iter) +{ + int ret = 0; + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = + g_new0(struct muxer_upstream_msg_iter, 1); + + if (!muxer_upstream_msg_iter) { + BT_LOGE_STR("Failed to allocate one muxer's upstream message iterator wrapper."); + goto error; + } + + muxer_upstream_msg_iter->msg_iter = self_msg_iter; + bt_self_component_port_input_message_iterator_get_ref(muxer_upstream_msg_iter->msg_iter); + muxer_upstream_msg_iter->msgs = g_queue_new(); + if (!muxer_upstream_msg_iter->msgs) { + BT_LOGE_STR("Failed to allocate a GQueue."); + goto error; + } + + g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters, + muxer_upstream_msg_iter); + BT_LOGD("Added muxer's upstream message iterator wrapper: " + "addr=%p, muxer-msg-iter-addr=%p, msg-iter-addr=%p", + muxer_upstream_msg_iter, muxer_msg_iter, + self_msg_iter); + + goto end; + +error: + g_free(muxer_upstream_msg_iter); + ret = -1; + +end: + return ret; +} + +static +bt_self_component_status add_available_input_port( + bt_self_component_filter *self_comp) +{ + struct muxer_comp *muxer_comp = bt_self_component_get_data( + bt_self_component_filter_as_self_component(self_comp)); + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + GString *port_name = NULL; + + BT_ASSERT(muxer_comp); + port_name = g_string_new("in"); + if (!port_name) { + BT_LOGE_STR("Failed to allocate a GString."); + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto end; + } + + g_string_append_printf(port_name, "%u", muxer_comp->next_port_num); + status = bt_self_component_filter_add_input_port( + self_comp, port_name->str, NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGE("Cannot add input port to muxer component: " + "port-name=\"%s\", comp-addr=%p, status=%s", + port_name->str, self_comp, + bt_self_component_status_string(status)); + goto end; + } + + muxer_comp->available_input_ports++; + muxer_comp->next_port_num++; + BT_LOGD("Added one input port to muxer component: " + "port-name=\"%s\", comp-addr=%p", + port_name->str, self_comp); + +end: + if (port_name) { + g_string_free(port_name, TRUE); + } + + return status; +} + +static +bt_self_component_status create_output_port( + bt_self_component_filter *self_comp) +{ + return bt_self_component_filter_add_output_port( + self_comp, "out", NULL, NULL); +} + +static +void destroy_muxer_comp(struct muxer_comp *muxer_comp) +{ + if (!muxer_comp) { + return; + } + + g_free(muxer_comp); +} + +static +bt_value *get_default_params(void) +{ + bt_value *params; + int ret; + + params = bt_value_map_create(); + if (!params) { + BT_LOGE_STR("Cannot create a map value object."); + goto error; + } + + ret = bt_value_map_insert_bool_entry(params, + ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME, false); + if (ret) { + BT_LOGE_STR("Cannot add boolean value to map value object."); + goto error; + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(params); + +end: + return params; +} + +static +int configure_muxer_comp(struct muxer_comp *muxer_comp, + const bt_value *params) +{ + bt_value *default_params = NULL; + bt_value *real_params = NULL; + const bt_value *assume_absolute_clock_classes = NULL; + int ret = 0; + bt_bool bool_val; + + default_params = get_default_params(); + if (!default_params) { + BT_LOGE("Cannot get default parameters: " + "muxer-comp-addr=%p", muxer_comp); + goto error; + } + + ret = bt_value_map_extend(default_params, params, &real_params); + if (ret) { + BT_LOGE("Cannot extend default parameters map value: " + "muxer-comp-addr=%p, def-params-addr=%p, " + "params-addr=%p", muxer_comp, default_params, + params); + goto error; + } + + assume_absolute_clock_classes = bt_value_map_borrow_entry_value(real_params, + ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME); + if (assume_absolute_clock_classes && + !bt_value_is_bool(assume_absolute_clock_classes)) { + BT_LOGE("Expecting a boolean value for the `%s` parameter: " + "muxer-comp-addr=%p, value-type=%s", + ASSUME_ABSOLUTE_CLOCK_CLASSES_PARAM_NAME, muxer_comp, + bt_common_value_type_string( + bt_value_get_type(assume_absolute_clock_classes))); + goto error; + } + + bool_val = bt_value_bool_get(assume_absolute_clock_classes); + muxer_comp->assume_absolute_clock_classes = (bool) bool_val; + BT_LOGD("Configured muxer component: muxer-comp-addr=%p, " + "assume-absolute-clock-classes=%d", + muxer_comp, muxer_comp->assume_absolute_clock_classes); + goto end; + +error: + ret = -1; + +end: + bt_value_put_ref(default_params); + bt_value_put_ref(real_params); + return ret; +} + +BT_HIDDEN +bt_self_component_status muxer_init( + bt_self_component_filter *self_comp, + const bt_value *params, void *init_data) +{ + int ret; + bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK; + struct muxer_comp *muxer_comp = g_new0(struct muxer_comp, 1); + + BT_LOGD("Initializing muxer component: " + "comp-addr=%p, params-addr=%p", self_comp, params); + + if (!muxer_comp) { + BT_LOGE_STR("Failed to allocate one muxer component."); + goto error; + } + + ret = configure_muxer_comp(muxer_comp, params); + if (ret) { + BT_LOGE("Cannot configure muxer component: " + "muxer-comp-addr=%p, params-addr=%p", + muxer_comp, params); + goto error; + } + + muxer_comp->self_comp = self_comp; + bt_self_component_set_data( + bt_self_component_filter_as_self_component(self_comp), + muxer_comp); + status = add_available_input_port(self_comp); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + BT_LOGE("Cannot ensure that at least one muxer component's input port is available: " + "muxer-comp-addr=%p, status=%s", + muxer_comp, + bt_self_component_status_string(status)); + goto error; + } + + status = create_output_port(self_comp); + if (status) { + BT_LOGE("Cannot create muxer component's output port: " + "muxer-comp-addr=%p, status=%s", + muxer_comp, + bt_self_component_status_string(status)); + goto error; + } + + BT_LOGD("Initialized muxer component: " + "comp-addr=%p, params-addr=%p, muxer-comp-addr=%p", + self_comp, params, muxer_comp); + + goto end; + +error: + destroy_muxer_comp(muxer_comp); + bt_self_component_set_data( + bt_self_component_filter_as_self_component(self_comp), + NULL); + + if (status == BT_SELF_COMPONENT_STATUS_OK) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + } + +end: + return status; +} + +BT_HIDDEN +void muxer_finalize(bt_self_component_filter *self_comp) +{ + struct muxer_comp *muxer_comp = bt_self_component_get_data( + bt_self_component_filter_as_self_component(self_comp)); + + BT_LOGD("Finalizing muxer component: comp-addr=%p", + self_comp); + destroy_muxer_comp(muxer_comp); +} + +static +bt_self_component_port_input_message_iterator * +create_msg_iter_on_input_port(bt_self_component_port_input *self_port) +{ + const bt_port *port = bt_self_component_port_as_port( + bt_self_component_port_input_as_self_component_port( + self_port)); + bt_self_component_port_input_message_iterator *msg_iter = + NULL; + + BT_ASSERT(port); + BT_ASSERT(bt_port_is_connected(port)); + + // TODO: Advance the iterator to >= the time of the latest + // returned message by the muxer message + // iterator which creates it. + msg_iter = bt_self_component_port_input_message_iterator_create( + self_port); + if (!msg_iter) { + BT_LOGE("Cannot create upstream message iterator on input port: " + "port-addr=%p, port-name=\"%s\"", + port, bt_port_get_name(port)); + goto end; + } + + BT_LOGD("Created upstream message iterator on input port: " + "port-addr=%p, port-name=\"%s\", msg-iter-addr=%p", + port, bt_port_get_name(port), msg_iter); + +end: + return msg_iter; +} + +static +bt_self_message_iterator_status muxer_upstream_msg_iter_next( + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter, + bool *is_ended) +{ + bt_self_message_iterator_status status; + bt_message_iterator_status input_port_iter_status; + bt_message_array_const msgs; + uint64_t i; + uint64_t count; + + BT_LOGV("Calling upstream message iterator's \"next\" method: " + "muxer-upstream-msg-iter-wrap-addr=%p, msg-iter-addr=%p", + muxer_upstream_msg_iter, + muxer_upstream_msg_iter->msg_iter); + input_port_iter_status = bt_self_component_port_input_message_iterator_next( + muxer_upstream_msg_iter->msg_iter, &msgs, &count); + BT_LOGV("Upstream message iterator's \"next\" method returned: " + "status=%s", bt_message_iterator_status_string(input_port_iter_status)); + + switch (input_port_iter_status) { + case BT_MESSAGE_ITERATOR_STATUS_OK: + /* + * Message iterator's current message is + * valid: it must be considered for muxing operations. + */ + BT_LOGV_STR("Validated upstream message iterator wrapper."); + BT_ASSERT(count > 0); + + /* Move messages to our queue */ + for (i = 0; i < count; i++) { + /* + * Push to tail in order; other side + * (muxer_msg_iter_do_next_one()) consumes + * from the head first. + */ + g_queue_push_tail(muxer_upstream_msg_iter->msgs, + (void *) msgs[i]); + } + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + break; + case BT_MESSAGE_ITERATOR_STATUS_AGAIN: + /* + * Message iterator's current message is not + * valid anymore. Return + * BT_MESSAGE_ITERATOR_STATUS_AGAIN immediately. + */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_AGAIN; + break; + case BT_MESSAGE_ITERATOR_STATUS_END: /* Fall-through. */ + /* + * Message iterator reached the end: release it. It + * won't be considered again to find the youngest + * message. + */ + *is_ended = true; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + break; + default: + /* Error or unsupported status code */ + BT_LOGE("Error or unsupported status code: " + "status-code=%d", input_port_iter_status); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + break; + } + + return status; +} + +static +int get_msg_ts_ns(struct muxer_comp *muxer_comp, + struct muxer_msg_iter *muxer_msg_iter, + const bt_message *msg, int64_t last_returned_ts_ns, + int64_t *ts_ns) +{ + const bt_clock_snapshot *clock_snapshot = NULL; + int ret = 0; + bt_message_stream_activity_clock_snapshot_state sa_cs_state; + const bt_stream_class *stream_class = NULL; + bt_message_type msg_type; + + BT_ASSERT(msg); + BT_ASSERT(ts_ns); + BT_LOGV("Getting message's timestamp: " + "muxer-msg-iter-addr=%p, msg-addr=%p, " + "last-returned-ts=%" PRId64, + muxer_msg_iter, msg, last_returned_ts_ns); + + if (unlikely(muxer_msg_iter->clock_class_expectation == + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE)) { + *ts_ns = last_returned_ts_ns; + goto end; + } + + msg_type = bt_message_get_type(msg); + + if (unlikely(msg_type == BT_MESSAGE_TYPE_PACKET_BEGINNING)) { + stream_class = bt_stream_borrow_class_const( + bt_packet_borrow_stream_const( + bt_message_packet_beginning_borrow_packet_const( + msg))); + } else if (unlikely(msg_type == BT_MESSAGE_TYPE_PACKET_END)) { + stream_class = bt_stream_borrow_class_const( + bt_packet_borrow_stream_const( + bt_message_packet_end_borrow_packet_const( + msg))); + } else if (unlikely(msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS)) { + stream_class = bt_stream_borrow_class_const( + bt_message_discarded_events_borrow_stream_const(msg)); + } else if (unlikely(msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS)) { + stream_class = bt_stream_borrow_class_const( + bt_message_discarded_packets_borrow_stream_const(msg)); + } + + switch (msg_type) { + case BT_MESSAGE_TYPE_EVENT: + BT_ASSERT(bt_message_event_borrow_stream_class_default_clock_class_const( + msg)); + clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + if (bt_stream_class_packets_have_beginning_default_clock_snapshot( + stream_class)) { + clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( + msg); + } else { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_PACKET_END: + if (bt_stream_class_packets_have_end_default_clock_snapshot( + stream_class)) { + clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( + msg); + } else { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + if (bt_stream_class_discarded_events_have_default_clock_snapshots( + stream_class)) { + clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + } else { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + if (bt_stream_class_discarded_packets_have_default_clock_snapshots( + stream_class)) { + clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + } else { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + BT_ASSERT(bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( + msg)); + sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + BT_ASSERT(bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( + msg)); + sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state != BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_KNOWN) { + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + clock_snapshot = bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + msg); + break; + default: + /* All the other messages have a higher priority */ + BT_LOGV_STR("Message has no timestamp: using the last returned timestamp."); + *ts_ns = last_returned_ts_ns; + goto end; + } + + ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns); + if (ret) { + BT_LOGE("Cannot get nanoseconds from Epoch of clock snapshot: " + "clock-snapshot-addr=%p", clock_snapshot); + goto error; + } + + goto end; + +no_clock_snapshot: + BT_LOGV_STR("Message's default clock snapshot is missing: " + "using the last returned timestamp."); + *ts_ns = last_returned_ts_ns; + goto end; + +error: + ret = -1; + +end: + if (ret == 0) { + BT_LOGV("Found message's timestamp: " + "muxer-msg-iter-addr=%p, msg-addr=%p, " + "last-returned-ts=%" PRId64 ", ts=%" PRId64, + muxer_msg_iter, msg, last_returned_ts_ns, + *ts_ns); + } + + return ret; +} + +static inline +int validate_clock_class(struct muxer_msg_iter *muxer_msg_iter, + struct muxer_comp *muxer_comp, + const bt_clock_class *clock_class) +{ + int ret = 0; + const unsigned char *cc_uuid; + const char *cc_name; + + BT_ASSERT(clock_class); + cc_uuid = bt_clock_class_get_uuid(clock_class); + cc_name = bt_clock_class_get_name(clock_class); + + if (muxer_msg_iter->clock_class_expectation == + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) { + /* + * This is the first clock class that this muxer + * message iterator encounters. Its properties + * determine what to expect for the whole lifetime of + * the iterator without a true + * `assume-absolute-clock-classes` parameter. + */ + if (bt_clock_class_origin_is_unix_epoch(clock_class)) { + /* Expect absolute clock classes */ + muxer_msg_iter->clock_class_expectation = + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE; + } else { + if (cc_uuid) { + /* + * Expect non-absolute clock classes + * with a specific UUID. + */ + muxer_msg_iter->clock_class_expectation = + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID; + memcpy(muxer_msg_iter->expected_clock_class_uuid, + cc_uuid, BABELTRACE_UUID_LEN); + } else { + /* + * Expect non-absolute clock classes + * with no UUID. + */ + muxer_msg_iter->clock_class_expectation = + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID; + } + } + } + + if (!muxer_comp->assume_absolute_clock_classes) { + switch (muxer_msg_iter->clock_class_expectation) { + case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ABSOLUTE: + if (!bt_clock_class_origin_is_unix_epoch(clock_class)) { + BT_LOGE("Expecting an absolute clock class, " + "but got a non-absolute one: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + clock_class, cc_name); + goto error; + } + break; + case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_NO_UUID: + if (bt_clock_class_origin_is_unix_epoch(clock_class)) { + BT_LOGE("Expecting a non-absolute clock class with no UUID, " + "but got an absolute one: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + clock_class, cc_name); + goto error; + } + + if (cc_uuid) { + BT_LOGE("Expecting a non-absolute clock class with no UUID, " + "but got one with a UUID: " + "clock-class-addr=%p, clock-class-name=\"%s\", " + "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + clock_class, cc_name, + (unsigned int) cc_uuid[0], + (unsigned int) cc_uuid[1], + (unsigned int) cc_uuid[2], + (unsigned int) cc_uuid[3], + (unsigned int) cc_uuid[4], + (unsigned int) cc_uuid[5], + (unsigned int) cc_uuid[6], + (unsigned int) cc_uuid[7], + (unsigned int) cc_uuid[8], + (unsigned int) cc_uuid[9], + (unsigned int) cc_uuid[10], + (unsigned int) cc_uuid[11], + (unsigned int) cc_uuid[12], + (unsigned int) cc_uuid[13], + (unsigned int) cc_uuid[14], + (unsigned int) cc_uuid[15]); + goto error; + } + break; + case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NOT_ABS_SPEC_UUID: + if (bt_clock_class_origin_is_unix_epoch(clock_class)) { + BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " + "but got an absolute one: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + clock_class, cc_name); + goto error; + } + + if (!cc_uuid) { + BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " + "but got one with no UUID: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + clock_class, cc_name); + goto error; + } + + if (memcmp(muxer_msg_iter->expected_clock_class_uuid, + cc_uuid, BABELTRACE_UUID_LEN) != 0) { + BT_LOGE("Expecting a non-absolute clock class with a specific UUID, " + "but got one with different UUID: " + "clock-class-addr=%p, clock-class-name=\"%s\", " + "expected-uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\", " + "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"", + clock_class, cc_name, + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[0], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[1], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[2], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[3], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[4], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[5], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[6], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[7], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[8], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[9], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[10], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[11], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[12], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[13], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[14], + (unsigned int) muxer_msg_iter->expected_clock_class_uuid[15], + (unsigned int) cc_uuid[0], + (unsigned int) cc_uuid[1], + (unsigned int) cc_uuid[2], + (unsigned int) cc_uuid[3], + (unsigned int) cc_uuid[4], + (unsigned int) cc_uuid[5], + (unsigned int) cc_uuid[6], + (unsigned int) cc_uuid[7], + (unsigned int) cc_uuid[8], + (unsigned int) cc_uuid[9], + (unsigned int) cc_uuid[10], + (unsigned int) cc_uuid[11], + (unsigned int) cc_uuid[12], + (unsigned int) cc_uuid[13], + (unsigned int) cc_uuid[14], + (unsigned int) cc_uuid[15]); + goto error; + } + break; + case MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE: + BT_LOGE("Expecting no clock class, but got one: " + "clock-class-addr=%p, clock-class-name=\"%s\"", + clock_class, cc_name); + goto error; + default: + /* Unexpected */ + BT_LOGF("Unexpected clock class expectation: " + "expectation-code=%d", + muxer_msg_iter->clock_class_expectation); + abort(); + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static inline +int validate_new_stream_clock_class(struct muxer_msg_iter *muxer_msg_iter, + struct muxer_comp *muxer_comp, const bt_stream *stream) +{ + int ret = 0; + const bt_stream_class *stream_class = + bt_stream_borrow_class_const(stream); + const bt_clock_class *clock_class = + bt_stream_class_borrow_default_clock_class_const(stream_class); + + if (!clock_class) { + if (muxer_msg_iter->clock_class_expectation == + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY) { + /* Expect no clock class */ + muxer_msg_iter->clock_class_expectation = + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_NONE; + } else { + BT_LOGE("Expecting stream class with a default clock class: " + "stream-class-addr=%p, stream-class-name=\"%s\", " + "stream-class-id=%" PRIu64, + stream_class, bt_stream_class_get_name(stream_class), + bt_stream_class_get_id(stream_class)); + ret = -1; + } + + goto end; + } + + ret = validate_clock_class(muxer_msg_iter, muxer_comp, clock_class); + +end: + return ret; +} + +/* + * This function finds the youngest available message amongst the + * non-ended upstream message iterators and returns the upstream + * message iterator which has it, or + * BT_MESSAGE_ITERATOR_STATUS_END if there's no available + * message. + * + * This function does NOT: + * + * * Update any upstream message iterator. + * * Check the upstream message iterators to retry. + * + * On sucess, this function sets *muxer_upstream_msg_iter to the + * upstream message iterator of which the current message is + * the youngest, and sets *ts_ns to its time. + */ +static +bt_self_message_iterator_status +muxer_msg_iter_youngest_upstream_msg_iter( + struct muxer_comp *muxer_comp, + struct muxer_msg_iter *muxer_msg_iter, + struct muxer_upstream_msg_iter **muxer_upstream_msg_iter, + int64_t *ts_ns) +{ + size_t i; + int ret; + int64_t youngest_ts_ns = INT64_MAX; + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(muxer_comp); + BT_ASSERT(muxer_msg_iter); + BT_ASSERT(muxer_upstream_msg_iter); + *muxer_upstream_msg_iter = NULL; + + for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; + i++) { + const bt_message *msg; + struct muxer_upstream_msg_iter *cur_muxer_upstream_msg_iter = + g_ptr_array_index( + muxer_msg_iter->active_muxer_upstream_msg_iters, + i); + int64_t msg_ts_ns; + + if (!cur_muxer_upstream_msg_iter->msg_iter) { + /* This upstream message iterator is ended */ + BT_LOGV("Skipping ended upstream message iterator: " + "muxer-upstream-msg-iter-wrap-addr=%p", + cur_muxer_upstream_msg_iter); + continue; + } + + BT_ASSERT(cur_muxer_upstream_msg_iter->msgs->length > 0); + msg = g_queue_peek_head(cur_muxer_upstream_msg_iter->msgs); + BT_ASSERT(msg); + + if (unlikely(bt_message_get_type(msg) == + BT_MESSAGE_TYPE_STREAM_BEGINNING)) { + ret = validate_new_stream_clock_class( + muxer_msg_iter, muxer_comp, + bt_message_stream_beginning_borrow_stream_const( + msg)); + if (ret) { + /* + * validate_new_stream_clock_class() logs + * errors. + */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + } else if (unlikely(bt_message_get_type(msg) == + BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY)) { + const bt_clock_snapshot *cs; + + cs = bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + msg); + ret = validate_clock_class(muxer_msg_iter, muxer_comp, + bt_clock_snapshot_borrow_clock_class_const(cs)); + if (ret) { + /* validate_clock_class() logs errors */ + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + } + + ret = get_msg_ts_ns(muxer_comp, muxer_msg_iter, msg, + muxer_msg_iter->last_returned_ts_ns, &msg_ts_ns); + if (ret) { + /* get_msg_ts_ns() logs errors */ + *muxer_upstream_msg_iter = NULL; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (msg_ts_ns <= youngest_ts_ns) { + *muxer_upstream_msg_iter = + cur_muxer_upstream_msg_iter; + youngest_ts_ns = msg_ts_ns; + *ts_ns = youngest_ts_ns; + } + } + + if (!*muxer_upstream_msg_iter) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + *ts_ns = INT64_MIN; + } + +end: + return status; +} + +static +bt_self_message_iterator_status validate_muxer_upstream_msg_iter( + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter, + bool *is_ended) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_LOGV("Validating muxer's upstream message iterator wrapper: " + "muxer-upstream-msg-iter-wrap-addr=%p", + muxer_upstream_msg_iter); + + if (muxer_upstream_msg_iter->msgs->length > 0 || + !muxer_upstream_msg_iter->msg_iter) { + BT_LOGV("Already valid or not considered: " + "queue-len=%u, upstream-msg-iter-addr=%p", + muxer_upstream_msg_iter->msgs->length, + muxer_upstream_msg_iter->msg_iter); + goto end; + } + + /* muxer_upstream_msg_iter_next() logs details/errors */ + status = muxer_upstream_msg_iter_next(muxer_upstream_msg_iter, + is_ended); + +end: + return status; +} + +static +bt_self_message_iterator_status validate_muxer_upstream_msg_iters( + struct muxer_msg_iter *muxer_msg_iter) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + size_t i; + + BT_LOGV("Validating muxer's upstream message iterator wrappers: " + "muxer-msg-iter-addr=%p", muxer_msg_iter); + + for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; + i++) { + bool is_ended = false; + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = + g_ptr_array_index( + muxer_msg_iter->active_muxer_upstream_msg_iters, + i); + + status = validate_muxer_upstream_msg_iter( + muxer_upstream_msg_iter, &is_ended); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + if (status < 0) { + BT_LOGE("Cannot validate muxer's upstream message iterator wrapper: " + "muxer-msg-iter-addr=%p, " + "muxer-upstream-msg-iter-wrap-addr=%p", + muxer_msg_iter, + muxer_upstream_msg_iter); + } else { + BT_LOGV("Cannot validate muxer's upstream message iterator wrapper: " + "muxer-msg-iter-addr=%p, " + "muxer-upstream-msg-iter-wrap-addr=%p", + muxer_msg_iter, + muxer_upstream_msg_iter); + } + + goto end; + } + + /* + * Move this muxer upstream message iterator to the + * array of ended iterators if it's ended. + */ + if (unlikely(is_ended)) { + BT_LOGV("Muxer's upstream message iterator wrapper: ended or canceled: " + "muxer-msg-iter-addr=%p, " + "muxer-upstream-msg-iter-wrap-addr=%p", + muxer_msg_iter, muxer_upstream_msg_iter); + g_ptr_array_add( + muxer_msg_iter->ended_muxer_upstream_msg_iters, + muxer_upstream_msg_iter); + muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i] = NULL; + + /* + * Use g_ptr_array_remove_fast() because the + * order of those elements is not important. + */ + g_ptr_array_remove_index_fast( + muxer_msg_iter->active_muxer_upstream_msg_iters, + i); + i--; + } + } + +end: + return status; +} + +static inline +bt_self_message_iterator_status muxer_msg_iter_do_next_one( + struct muxer_comp *muxer_comp, + struct muxer_msg_iter *muxer_msg_iter, + const bt_message **msg) +{ + bt_self_message_iterator_status status; + struct muxer_upstream_msg_iter *muxer_upstream_msg_iter = NULL; + int64_t next_return_ts; + + status = validate_muxer_upstream_msg_iters(muxer_msg_iter); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + /* validate_muxer_upstream_msg_iters() logs details */ + goto end; + } + + /* + * At this point we know that all the existing upstream + * message iterators are valid. We can find the one, + * amongst those, of which the current message is the + * youngest. + */ + status = muxer_msg_iter_youngest_upstream_msg_iter(muxer_comp, + muxer_msg_iter, &muxer_upstream_msg_iter, + &next_return_ts); + if (status < 0 || status == BT_SELF_MESSAGE_ITERATOR_STATUS_END) { + if (status < 0) { + BT_LOGE("Cannot find the youngest upstream message iterator wrapper: " + "status=%s", + bt_common_self_message_iterator_status_string(status)); + } else { + BT_LOGV("Cannot find the youngest upstream message iterator wrapper: " + "status=%s", + bt_common_self_message_iterator_status_string(status)); + } + + goto end; + } + + if (next_return_ts < muxer_msg_iter->last_returned_ts_ns) { + BT_LOGE("Youngest upstream message iterator wrapper's timestamp is less than muxer's message iterator's last returned timestamp: " + "muxer-msg-iter-addr=%p, ts=%" PRId64 ", " + "last-returned-ts=%" PRId64, + muxer_msg_iter, next_return_ts, + muxer_msg_iter->last_returned_ts_ns); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + BT_LOGV("Found youngest upstream message iterator wrapper: " + "muxer-msg-iter-addr=%p, " + "muxer-upstream-msg-iter-wrap-addr=%p, " + "ts=%" PRId64, + muxer_msg_iter, muxer_upstream_msg_iter, next_return_ts); + BT_ASSERT(status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK); + BT_ASSERT(muxer_upstream_msg_iter); + + /* + * Consume from the queue's head: other side + * (muxer_upstream_msg_iter_next()) writes to the tail. + */ + *msg = g_queue_pop_head(muxer_upstream_msg_iter->msgs); + BT_ASSERT(*msg); + muxer_msg_iter->last_returned_ts_ns = next_return_ts; + +end: + return status; +} + +static +bt_self_message_iterator_status muxer_msg_iter_do_next( + struct muxer_comp *muxer_comp, + struct muxer_msg_iter *muxer_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + uint64_t i = 0; + + while (i < capacity && status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + status = muxer_msg_iter_do_next_one(muxer_comp, + muxer_msg_iter, &msgs[i]); + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + i++; + } + } + + if (i > 0) { + /* + * Even if muxer_msg_iter_do_next_one() returned + * something else than + * BT_MESSAGE_ITERATOR_STATUS_OK, we accumulated + * message objects in the output message + * array, so we need to return + * BT_MESSAGE_ITERATOR_STATUS_OK so that they are + * transfered to downstream. This other status occurs + * again the next time muxer_msg_iter_do_next() is + * called, possibly without any accumulated + * message, in which case we'll return it. + */ + *count = i; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + } + + return status; +} + +static +void destroy_muxer_msg_iter(struct muxer_msg_iter *muxer_msg_iter) +{ + if (!muxer_msg_iter) { + return; + } + + BT_LOGD("Destroying muxer component's message iterator: " + "muxer-msg-iter-addr=%p", muxer_msg_iter); + + if (muxer_msg_iter->active_muxer_upstream_msg_iters) { + BT_LOGD_STR("Destroying muxer's active upstream message iterator wrappers."); + g_ptr_array_free( + muxer_msg_iter->active_muxer_upstream_msg_iters, TRUE); + } + + if (muxer_msg_iter->ended_muxer_upstream_msg_iters) { + BT_LOGD_STR("Destroying muxer's ended upstream message iterator wrappers."); + g_ptr_array_free( + muxer_msg_iter->ended_muxer_upstream_msg_iters, TRUE); + } + + g_free(muxer_msg_iter); +} + +static +int muxer_msg_iter_init_upstream_iterators(struct muxer_comp *muxer_comp, + struct muxer_msg_iter *muxer_msg_iter) +{ + int64_t count; + int64_t i; + int ret = 0; + + count = bt_component_filter_get_input_port_count( + bt_self_component_filter_as_component_filter( + muxer_comp->self_comp)); + if (count < 0) { + BT_LOGD("No input port to initialize for muxer component's message iterator: " + "muxer-comp-addr=%p, muxer-msg-iter-addr=%p", + muxer_comp, muxer_msg_iter); + goto end; + } + + for (i = 0; i < count; i++) { + bt_self_component_port_input_message_iterator *upstream_msg_iter; + bt_self_component_port_input *self_port = + bt_self_component_filter_borrow_input_port_by_index( + muxer_comp->self_comp, i); + const bt_port *port; + + BT_ASSERT(self_port); + port = bt_self_component_port_as_port( + bt_self_component_port_input_as_self_component_port( + self_port)); + BT_ASSERT(port); + + if (!bt_port_is_connected(port)) { + /* Skip non-connected port */ + continue; + } + + upstream_msg_iter = create_msg_iter_on_input_port(self_port); + if (!upstream_msg_iter) { + /* create_msg_iter_on_input_port() logs errors */ + BT_ASSERT(!upstream_msg_iter); + ret = -1; + goto end; + } + + ret = muxer_msg_iter_add_upstream_msg_iter(muxer_msg_iter, + upstream_msg_iter); + bt_self_component_port_input_message_iterator_put_ref( + upstream_msg_iter); + if (ret) { + /* muxer_msg_iter_add_upstream_msg_iter() logs errors */ + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *port) +{ + struct muxer_comp *muxer_comp = NULL; + struct muxer_msg_iter *muxer_msg_iter = NULL; + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + int ret; + + muxer_comp = bt_self_component_get_data( + bt_self_component_filter_as_self_component(self_comp)); + BT_ASSERT(muxer_comp); + BT_LOGD("Initializing muxer component's message iterator: " + "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p", + self_comp, muxer_comp, self_msg_iter); + + if (muxer_comp->initializing_muxer_msg_iter) { + /* + * Weird, unhandled situation detected: downstream + * creates a muxer message iterator while creating + * another muxer message iterator (same component). + */ + BT_LOGE("Recursive initialization of muxer component's message iterator: " + "comp-addr=%p, muxer-comp-addr=%p, msg-iter-addr=%p", + self_comp, muxer_comp, self_msg_iter); + goto error; + } + + muxer_comp->initializing_muxer_msg_iter = true; + muxer_msg_iter = g_new0(struct muxer_msg_iter, 1); + if (!muxer_msg_iter) { + BT_LOGE_STR("Failed to allocate one muxer component's message iterator."); + goto error; + } + + muxer_msg_iter->last_returned_ts_ns = INT64_MIN; + muxer_msg_iter->active_muxer_upstream_msg_iters = + g_ptr_array_new_with_free_func( + (GDestroyNotify) destroy_muxer_upstream_msg_iter); + if (!muxer_msg_iter->active_muxer_upstream_msg_iters) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + muxer_msg_iter->ended_muxer_upstream_msg_iters = + g_ptr_array_new_with_free_func( + (GDestroyNotify) destroy_muxer_upstream_msg_iter); + if (!muxer_msg_iter->ended_muxer_upstream_msg_iters) { + BT_LOGE_STR("Failed to allocate a GPtrArray."); + goto error; + } + + ret = muxer_msg_iter_init_upstream_iterators(muxer_comp, + muxer_msg_iter); + if (ret) { + BT_LOGE("Cannot initialize connected input ports for muxer component's message iterator: " + "comp-addr=%p, muxer-comp-addr=%p, " + "muxer-msg-iter-addr=%p, msg-iter-addr=%p, ret=%d", + self_comp, muxer_comp, muxer_msg_iter, + self_msg_iter, ret); + goto error; + } + + bt_self_message_iterator_set_data(self_msg_iter, muxer_msg_iter); + BT_LOGD("Initialized muxer component's message iterator: " + "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " + "msg-iter-addr=%p", + self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); + goto end; + +error: + destroy_muxer_msg_iter(muxer_msg_iter); + bt_self_message_iterator_set_data(self_msg_iter, NULL); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + +end: + muxer_comp->initializing_muxer_msg_iter = false; + return status; +} + +BT_HIDDEN +void muxer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) +{ + struct muxer_msg_iter *muxer_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + bt_self_component *self_comp = NULL; + struct muxer_comp *muxer_comp = NULL; + + self_comp = bt_self_message_iterator_borrow_component( + self_msg_iter); + BT_ASSERT(self_comp); + muxer_comp = bt_self_component_get_data(self_comp); + BT_LOGD("Finalizing muxer component's message iterator: " + "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " + "msg-iter-addr=%p", + self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); + + if (muxer_msg_iter) { + destroy_muxer_msg_iter(muxer_msg_iter); + } +} + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status; + struct muxer_msg_iter *muxer_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + bt_self_component *self_comp = NULL; + struct muxer_comp *muxer_comp = NULL; + + BT_ASSERT(muxer_msg_iter); + self_comp = bt_self_message_iterator_borrow_component( + self_msg_iter); + BT_ASSERT(self_comp); + muxer_comp = bt_self_component_get_data(self_comp); + BT_ASSERT(muxer_comp); + BT_LOGV("Muxer component's message iterator's \"next\" method called: " + "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " + "msg-iter-addr=%p", + self_comp, muxer_comp, muxer_msg_iter, self_msg_iter); + + status = muxer_msg_iter_do_next(muxer_comp, muxer_msg_iter, + msgs, capacity, count); + if (status < 0) { + BT_LOGE("Cannot get next message: " + "comp-addr=%p, muxer-comp-addr=%p, muxer-msg-iter-addr=%p, " + "msg-iter-addr=%p, status=%s", + self_comp, muxer_comp, muxer_msg_iter, self_msg_iter, + bt_common_self_message_iterator_status_string(status)); + } else { + BT_LOGV("Returning from muxer component's message iterator's \"next\" method: " + "status=%s", + bt_common_self_message_iterator_status_string(status)); + } + + return status; +} + +BT_HIDDEN +bt_self_component_status muxer_input_port_connected( + bt_self_component_filter *self_comp, + bt_self_component_port_input *self_port, + const bt_port_output *other_port) +{ + bt_self_component_status status; + + status = add_available_input_port(self_comp); + if (status) { + /* + * Only way to report an error later since this + * method does not return anything. + */ + BT_LOGE("Cannot add one muxer component's input port: " + "status=%s", + bt_self_component_status_string(status)); + goto end; + } + +end: + return status; +} + +static inline +bt_bool muxer_upstream_msg_iters_can_all_seek_beginning( + GPtrArray *muxer_upstream_msg_iters) +{ + uint64_t i; + bt_bool ret = BT_TRUE; + + for (i = 0; i < muxer_upstream_msg_iters->len; i++) { + struct muxer_upstream_msg_iter *upstream_msg_iter = + muxer_upstream_msg_iters->pdata[i]; + + if (!bt_self_component_port_input_message_iterator_can_seek_beginning( + upstream_msg_iter->msg_iter)) { + ret = BT_FALSE; + goto end; + } + } + +end: + return ret; +} + +BT_HIDDEN +bt_bool muxer_msg_iter_can_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct muxer_msg_iter *muxer_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + bt_bool ret = BT_TRUE; + + if (!muxer_upstream_msg_iters_can_all_seek_beginning( + muxer_msg_iter->active_muxer_upstream_msg_iters)) { + ret = BT_FALSE; + goto end; + } + + if (!muxer_upstream_msg_iters_can_all_seek_beginning( + muxer_msg_iter->ended_muxer_upstream_msg_iters)) { + ret = BT_FALSE; + goto end; + } + +end: + return ret; +} + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_seek_beginning( + bt_self_message_iterator *self_msg_iter) +{ + struct muxer_msg_iter *muxer_msg_iter = + bt_self_message_iterator_get_data(self_msg_iter); + bt_message_iterator_status status = BT_MESSAGE_ITERATOR_STATUS_OK; + uint64_t i; + + /* Seek all ended upstream iterators first */ + for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len; + i++) { + struct muxer_upstream_msg_iter *upstream_msg_iter = + muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i]; + + status = bt_self_component_port_input_message_iterator_seek_beginning( + upstream_msg_iter->msg_iter); + if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + empty_message_queue(upstream_msg_iter); + } + + /* Seek all previously active upstream iterators */ + for (i = 0; i < muxer_msg_iter->active_muxer_upstream_msg_iters->len; + i++) { + struct muxer_upstream_msg_iter *upstream_msg_iter = + muxer_msg_iter->active_muxer_upstream_msg_iters->pdata[i]; + + status = bt_self_component_port_input_message_iterator_seek_beginning( + upstream_msg_iter->msg_iter); + if (status != BT_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + empty_message_queue(upstream_msg_iter); + } + + /* Make them all active */ + for (i = 0; i < muxer_msg_iter->ended_muxer_upstream_msg_iters->len; + i++) { + struct muxer_upstream_msg_iter *upstream_msg_iter = + muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i]; + + g_ptr_array_add(muxer_msg_iter->active_muxer_upstream_msg_iters, + upstream_msg_iter); + muxer_msg_iter->ended_muxer_upstream_msg_iters->pdata[i] = NULL; + } + + g_ptr_array_remove_range(muxer_msg_iter->ended_muxer_upstream_msg_iters, + 0, muxer_msg_iter->ended_muxer_upstream_msg_iters->len); + muxer_msg_iter->last_returned_ts_ns = INT64_MIN; + muxer_msg_iter->clock_class_expectation = + MUXER_MSG_ITER_CLOCK_CLASS_EXPECTATION_ANY; + +end: + return (bt_self_message_iterator_status) status; +} diff --git a/src/plugins/utils/muxer/muxer.h b/src/plugins/utils/muxer/muxer.h new file mode 100644 index 00000000..a383e4c2 --- /dev/null +++ b/src/plugins/utils/muxer/muxer.h @@ -0,0 +1,69 @@ +#ifndef BABELTRACE_PLUGINS_UTILS_MUXER_H +#define BABELTRACE_PLUGINS_UTILS_MUXER_H + +/* + * Copyright 2016 Jérémie Galarneau + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "common/babeltrace.h" + +BT_HIDDEN +bt_self_component_status muxer_init( + bt_self_component_filter *self_comp, + const bt_value *params, void *init_data); + +BT_HIDDEN +void muxer_finalize(bt_self_component_filter *self_comp); + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *self_port); + +BT_HIDDEN +void muxer_msg_iter_finalize( + bt_self_message_iterator *self_msg_iter); + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +BT_HIDDEN +bt_self_component_status muxer_input_port_connected( + bt_self_component_filter *comp, + bt_self_component_port_input *self_port, + const bt_port_output *other_port); + +BT_HIDDEN +bt_bool muxer_msg_iter_can_seek_beginning( + bt_self_message_iterator *message_iterator); + +BT_HIDDEN +bt_self_message_iterator_status muxer_msg_iter_seek_beginning( + bt_self_message_iterator *message_iterator); + +#endif /* BABELTRACE_PLUGINS_UTILS_MUXER_H */ diff --git a/src/plugins/utils/plugin.c b/src/plugins/utils/plugin.c new file mode 100644 index 00000000..d6b5b244 --- /dev/null +++ b/src/plugins/utils/plugin.c @@ -0,0 +1,82 @@ +/* + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "dummy/dummy.h" +#include "counter/counter.h" +#include "muxer/muxer.h" +#include "trimmer/trimmer.h" + +#ifndef BT_BUILT_IN_PLUGINS +BT_PLUGIN_MODULE(); +#endif + +BT_PLUGIN(utils); +BT_PLUGIN_DESCRIPTION("Graph utilities"); +BT_PLUGIN_AUTHOR("Julien Desfossez, Jérémie Galarneau, Philippe Proulx"); +BT_PLUGIN_LICENSE("MIT"); + +/* sink.utils.dummy */ +BT_PLUGIN_SINK_COMPONENT_CLASS(dummy, dummy_consume); +BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(dummy, dummy_init); +BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(dummy, dummy_finalize); +BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(dummy, + dummy_graph_is_configured); +BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(dummy, + "Consume messages and discard them."); + +/* sink.utils.counter */ +BT_PLUGIN_SINK_COMPONENT_CLASS(counter, counter_consume); +BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(counter, counter_init); +BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(counter, counter_finalize); +BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(counter, + counter_graph_is_configured); +BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(counter, + "Count messages and print the results."); + +/* flt.utils.trimmer */ +BT_PLUGIN_FILTER_COMPONENT_CLASS(trimmer, trimmer_msg_iter_next); +BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(trimmer, + "Keep messages that occur within a specific time range."); +BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD(trimmer, trimmer_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(trimmer, trimmer_finalize); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(trimmer, + trimmer_msg_iter_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(trimmer, + trimmer_msg_iter_finalize); + +/* flt.utils.muxer */ +BT_PLUGIN_FILTER_COMPONENT_CLASS(muxer, muxer_msg_iter_next); +BT_PLUGIN_FILTER_COMPONENT_CLASS_DESCRIPTION(muxer, + "Sort messages from multiple input ports to a single output port by time."); +BT_PLUGIN_FILTER_COMPONENT_CLASS_INIT_METHOD(muxer, muxer_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_FINALIZE_METHOD(muxer, muxer_finalize); +BT_PLUGIN_FILTER_COMPONENT_CLASS_INPUT_PORT_CONNECTED_METHOD(muxer, + muxer_input_port_connected); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_INIT_METHOD(muxer, + muxer_msg_iter_init); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_FINALIZE_METHOD(muxer, + muxer_msg_iter_finalize); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(muxer, + muxer_msg_iter_seek_beginning); +BT_PLUGIN_FILTER_COMPONENT_CLASS_MESSAGE_ITERATOR_CAN_SEEK_BEGINNING_METHOD(muxer, + muxer_msg_iter_can_seek_beginning); diff --git a/src/plugins/utils/trimmer/Makefile.am b/src/plugins/utils/trimmer/Makefile.am new file mode 100644 index 00000000..9ce4c9b2 --- /dev/null +++ b/src/plugins/utils/trimmer/Makefile.am @@ -0,0 +1,6 @@ +noinst_LTLIBRARIES = libbabeltrace2-plugin-trimmer.la +libbabeltrace2_plugin_trimmer_la_SOURCES = \ + trimmer.c \ + trimmer.h \ + logging.c \ + logging.h diff --git a/src/plugins/utils/trimmer/logging.c b/src/plugins/utils/trimmer/logging.c new file mode 100644 index 00000000..f6721231 --- /dev/null +++ b/src/plugins/utils/trimmer/logging.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_trimmer_log_level +#include "logging/log.h" + +BT_LOG_INIT_LOG_LEVEL(bt_plugin_utils_trimmer_log_level, + "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL"); diff --git a/src/plugins/utils/trimmer/logging.h b/src/plugins/utils/trimmer/logging.h new file mode 100644 index 00000000..aba659b9 --- /dev/null +++ b/src/plugins/utils/trimmer/logging.h @@ -0,0 +1,31 @@ +#ifndef PLUGINS_UTILS_TRIMMER_LOGGING_H +#define PLUGINS_UTILS_TRIMMER_LOGGING_H + +/* + * Copyright (c) 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_OUTPUT_LEVEL bt_plugin_utils_trimmer_log_level +#include "logging/log.h" + +BT_LOG_LEVEL_EXTERN_SYMBOL(bt_plugin_utils_trimmer_log_level); + +#endif /* PLUGINS_UTILS_TRIMMER_LOGGING_H */ diff --git a/src/plugins/utils/trimmer/trimmer.c b/src/plugins/utils/trimmer/trimmer.c new file mode 100644 index 00000000..f2d2c434 --- /dev/null +++ b/src/plugins/utils/trimmer/trimmer.c @@ -0,0 +1,2049 @@ +/* + * Copyright 2016 Jérémie Galarneau + * Copyright 2019 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-UTILS-TRIMMER-FLT" +#include "logging.h" + +#include "compat/utc.h" +#include "compat/time.h" +#include +#include "common/common.h" +#include "plugins/plugins-common.h" +#include "common/assert.h" +#include +#include +#include + +#include "trimmer.h" + +#define NS_PER_S INT64_C(1000000000) + +static const char * const in_port_name = "in"; + +struct trimmer_time { + unsigned int hour, minute, second, ns; +}; + +struct trimmer_bound { + /* + * Nanoseconds from origin, valid if `is_set` is set and + * `is_infinite` is false. + */ + int64_t ns_from_origin; + + /* True if this bound's full time (`ns_from_origin`) is set */ + bool is_set; + + /* + * True if this bound represents the infinity (negative or + * positive depending on which bound it is). If this is true, + * then we don't care about `ns_from_origin` above. + */ + bool is_infinite; + + /* + * This bound's time without the date; this time is used to set + * `ns_from_origin` once we know the date. + */ + struct trimmer_time time; +}; + +struct trimmer_comp { + struct trimmer_bound begin, end; + bool is_gmt; +}; + +enum trimmer_iterator_state { + /* + * Find the first message's date and set the bounds's times + * accordingly. + */ + TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN, + + /* + * Initially seek to the trimming range's beginning time. + */ + TRIMMER_ITERATOR_STATE_SEEK_INITIALLY, + + /* + * Fill the output message queue for as long as received input + * messages are within the trimming time range. + */ + TRIMMER_ITERATOR_STATE_TRIM, + + /* Flush the remaining messages in the output message queue */ + TRIMMER_ITERATOR_STATE_ENDING, + + /* Trimming operation and message iterator is ended */ + TRIMMER_ITERATOR_STATE_ENDED, +}; + +struct trimmer_iterator { + /* Weak */ + struct trimmer_comp *trimmer_comp; + + /* Weak */ + bt_self_message_iterator *self_msg_iter; + + enum trimmer_iterator_state state; + + /* Owned by this */ + bt_self_component_port_input_message_iterator *upstream_iter; + struct trimmer_bound begin, end; + + /* + * Queue of `const bt_message *` (owned by the queue). + * + * This is where the trimming operation pushes the messages to + * output by this message iterator. + */ + GQueue *output_messages; + + /* + * Hash table of `bt_stream *` (weak) to + * `struct trimmer_iterator_stream_state *` (owned by the HT). + */ + GHashTable *stream_states; +}; + +struct trimmer_iterator_stream_state { + /* + * True if both stream beginning and initial stream activity + * beginning messages were pushed for this stream. + */ + bool inited; + + /* + * True if the last pushed message for this stream was a stream + * activity end message. + */ + bool last_msg_is_stream_activity_end; + + /* + * Time to use for a generated stream end activity message when + * ending the stream. + */ + int64_t stream_act_end_ns_from_origin; + + /* Weak */ + const bt_stream *stream; + + /* Owned by this (`NULL` initially and between packets) */ + const bt_packet *cur_packet; + + /* Owned by this */ + const bt_message *stream_beginning_msg; +}; + +static +void destroy_trimmer_comp(struct trimmer_comp *trimmer_comp) +{ + BT_ASSERT(trimmer_comp); + g_free(trimmer_comp); +} + +static +struct trimmer_comp *create_trimmer_comp(void) +{ + return g_new0(struct trimmer_comp, 1); +} + +BT_HIDDEN +void trimmer_finalize(bt_self_component_filter *self_comp) +{ + struct trimmer_comp *trimmer_comp = + bt_self_component_get_data( + bt_self_component_filter_as_self_component(self_comp)); + + if (trimmer_comp) { + destroy_trimmer_comp(trimmer_comp); + } +} + +/* + * Sets the time (in ns from origin) of a trimmer bound from date and + * time components. + * + * Returns a negative value if anything goes wrong. + */ +static +int set_bound_ns_from_origin(struct trimmer_bound *bound, + unsigned int year, unsigned int month, unsigned int day, + unsigned int hour, unsigned int minute, unsigned int second, + unsigned int ns, bool is_gmt) +{ + int ret = 0; + time_t result; + struct tm tm = { + .tm_sec = second, + .tm_min = minute, + .tm_hour = hour, + .tm_mday = day, + .tm_mon = month - 1, + .tm_year = year - 1900, + .tm_isdst = -1, + }; + + if (is_gmt) { + result = bt_timegm(&tm); + } else { + result = mktime(&tm); + } + + if (result < 0) { + ret = -1; + goto end; + } + + BT_ASSERT(bound); + bound->ns_from_origin = (int64_t) result; + bound->ns_from_origin *= NS_PER_S; + bound->ns_from_origin += ns; + bound->is_set = true; + +end: + return ret; +} + +/* + * Parses a timestamp, figuring out its format. + * + * Returns a negative value if anything goes wrong. + * + * Expected formats: + * + * YYYY-MM-DD hh:mm[:ss[.ns]] + * [hh:mm:]ss[.ns] + * [-]s[.ns] + * + * TODO: Check overflows. + */ +static +int set_bound_from_str(const char *str, struct trimmer_bound *bound, + bool is_gmt) +{ + int ret = 0; + int s_ret; + unsigned int year, month, day, hour, minute, second, ns; + char dummy; + + /* Try `YYYY-MM-DD hh:mm:ss.ns` format */ + s_ret = sscanf(str, "%u-%u-%u %u:%u:%u.%u%c", &year, &month, &day, + &hour, &minute, &second, &ns, &dummy); + if (s_ret == 7) { + ret = set_bound_ns_from_origin(bound, year, month, day, + hour, minute, second, ns, is_gmt); + goto end; + } + + /* Try `YYYY-MM-DD hh:mm:ss` format */ + s_ret = sscanf(str, "%u-%u-%u %u:%u:%u%c", &year, &month, &day, + &hour, &minute, &second, &dummy); + if (s_ret == 6) { + ret = set_bound_ns_from_origin(bound, year, month, day, + hour, minute, second, 0, is_gmt); + goto end; + } + + /* Try `YYYY-MM-DD hh:mm` format */ + s_ret = sscanf(str, "%u-%u-%u %u:%u%c", &year, &month, &day, + &hour, &minute, &dummy); + if (s_ret == 5) { + ret = set_bound_ns_from_origin(bound, year, month, day, + hour, minute, 0, 0, is_gmt); + goto end; + } + + /* Try `YYYY-MM-DD` format */ + s_ret = sscanf(str, "%u-%u-%u%c", &year, &month, &day, &dummy); + if (s_ret == 3) { + ret = set_bound_ns_from_origin(bound, year, month, day, + 0, 0, 0, 0, is_gmt); + goto end; + } + + /* Try `hh:mm:ss.ns` format */ + s_ret = sscanf(str, "%u:%u:%u.%u%c", &hour, &minute, &second, &ns, + &dummy); + if (s_ret == 4) { + bound->time.hour = hour; + bound->time.minute = minute; + bound->time.second = second; + bound->time.ns = ns; + goto end; + } + + /* Try `hh:mm:ss` format */ + s_ret = sscanf(str, "%u:%u:%u%c", &hour, &minute, &second, &dummy); + if (s_ret == 3) { + bound->time.hour = hour; + bound->time.minute = minute; + bound->time.second = second; + bound->time.ns = 0; + goto end; + } + + /* Try `-s.ns` format */ + s_ret = sscanf(str, "-%u.%u%c", &second, &ns, &dummy); + if (s_ret == 2) { + bound->ns_from_origin = -((int64_t) second) * NS_PER_S; + bound->ns_from_origin -= (int64_t) ns; + bound->is_set = true; + goto end; + } + + /* Try `s.ns` format */ + s_ret = sscanf(str, "%u.%u%c", &second, &ns, &dummy); + if (s_ret == 2) { + bound->ns_from_origin = ((int64_t) second) * NS_PER_S; + bound->ns_from_origin += (int64_t) ns; + bound->is_set = true; + goto end; + } + + /* Try `-s` format */ + s_ret = sscanf(str, "-%u%c", &second, &dummy); + if (s_ret == 1) { + bound->ns_from_origin = -((int64_t) second) * NS_PER_S; + bound->is_set = true; + goto end; + } + + /* Try `s` format */ + s_ret = sscanf(str, "%u%c", &second, &dummy); + if (s_ret == 1) { + bound->ns_from_origin = (int64_t) second * NS_PER_S; + bound->is_set = true; + goto end; + } + + BT_LOGE("Invalid date/time format: param=\"%s\"", str); + ret = -1; + +end: + return ret; +} + +/* + * Sets a trimmer bound's properties from a parameter string/integer + * value. + * + * Returns a negative value if anything goes wrong. + */ +static +int set_bound_from_param(const char *param_name, const bt_value *param, + struct trimmer_bound *bound, bool is_gmt) +{ + int ret; + const char *arg; + char tmp_arg[64]; + + if (bt_value_is_signed_integer(param)) { + int64_t value = bt_value_signed_integer_get(param); + + /* + * Just convert it to a temporary string to handle + * everything the same way. + */ + sprintf(tmp_arg, "%" PRId64, value); + arg = tmp_arg; + } else if (bt_value_is_string(param)) { + arg = bt_value_string_get(param); + } else { + BT_LOGE("`%s` parameter must be an integer or a string value.", + param_name); + ret = -1; + goto end; + } + + ret = set_bound_from_str(arg, bound, is_gmt); + +end: + return ret; +} + +static +int validate_trimmer_bounds(struct trimmer_bound *begin, + struct trimmer_bound *end) +{ + int ret = 0; + + BT_ASSERT(begin->is_set); + BT_ASSERT(end->is_set); + + if (!begin->is_infinite && !end->is_infinite && + begin->ns_from_origin > end->ns_from_origin) { + BT_LOGE("Trimming time range's beginning time is greater than end time: " + "begin-ns-from-origin=%" PRId64 ", " + "end-ns-from-origin=%" PRId64, + begin->ns_from_origin, + end->ns_from_origin); + ret = -1; + goto end; + } + + if (!begin->is_infinite && begin->ns_from_origin == INT64_MIN) { + BT_LOGE("Invalid trimming time range's beginning time: " + "ns-from-origin=%" PRId64, + begin->ns_from_origin); + ret = -1; + goto end; + } + + if (!end->is_infinite && end->ns_from_origin == INT64_MIN) { + BT_LOGE("Invalid trimming time range's end time: " + "ns-from-origin=%" PRId64, + end->ns_from_origin); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +int init_trimmer_comp_from_params(struct trimmer_comp *trimmer_comp, + const bt_value *params) +{ + const bt_value *value; + int ret = 0; + + BT_ASSERT(params); + value = bt_value_map_borrow_entry_value_const(params, "gmt"); + if (value) { + trimmer_comp->is_gmt = (bool) bt_value_bool_get(value); + } + + value = bt_value_map_borrow_entry_value_const(params, "begin"); + if (value) { + if (set_bound_from_param("begin", value, + &trimmer_comp->begin, trimmer_comp->is_gmt)) { + /* set_bound_from_param() logs errors */ + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + } else { + trimmer_comp->begin.is_infinite = true; + trimmer_comp->begin.is_set = true; + } + + value = bt_value_map_borrow_entry_value_const(params, "end"); + if (value) { + if (set_bound_from_param("end", value, + &trimmer_comp->end, trimmer_comp->is_gmt)) { + /* set_bound_from_param() logs errors */ + ret = BT_SELF_COMPONENT_STATUS_ERROR; + goto end; + } + } else { + trimmer_comp->end.is_infinite = true; + trimmer_comp->end.is_set = true; + } + +end: + if (trimmer_comp->begin.is_set && trimmer_comp->end.is_set) { + /* validate_trimmer_bounds() logs errors */ + ret = validate_trimmer_bounds(&trimmer_comp->begin, + &trimmer_comp->end); + } + + return ret; +} + +bt_self_component_status trimmer_init(bt_self_component_filter *self_comp, + const bt_value *params, void *init_data) +{ + int ret; + bt_self_component_status status; + struct trimmer_comp *trimmer_comp = create_trimmer_comp(); + + if (!trimmer_comp) { + status = BT_SELF_COMPONENT_STATUS_NOMEM; + goto error; + } + + status = bt_self_component_filter_add_input_port( + self_comp, in_port_name, NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + status = bt_self_component_filter_add_output_port( + self_comp, "out", NULL, NULL); + if (status != BT_SELF_COMPONENT_STATUS_OK) { + goto error; + } + + ret = init_trimmer_comp_from_params(trimmer_comp, params); + if (ret) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + goto error; + } + + bt_self_component_set_data( + bt_self_component_filter_as_self_component(self_comp), + trimmer_comp); + goto end; + +error: + if (status == BT_SELF_COMPONENT_STATUS_OK) { + status = BT_SELF_COMPONENT_STATUS_ERROR; + } + + if (trimmer_comp) { + destroy_trimmer_comp(trimmer_comp); + } + +end: + return status; +} + +static +void destroy_trimmer_iterator(struct trimmer_iterator *trimmer_it) +{ + BT_ASSERT(trimmer_it); + bt_self_component_port_input_message_iterator_put_ref( + trimmer_it->upstream_iter); + + if (trimmer_it->output_messages) { + g_queue_free(trimmer_it->output_messages); + } + + if (trimmer_it->stream_states) { + g_hash_table_destroy(trimmer_it->stream_states); + } + + g_free(trimmer_it); +} + +static +void destroy_trimmer_iterator_stream_state( + struct trimmer_iterator_stream_state *sstate) +{ + BT_ASSERT(sstate); + BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); + BT_MESSAGE_PUT_REF_AND_RESET(sstate->stream_beginning_msg); + g_free(sstate); +} + +BT_HIDDEN +bt_self_message_iterator_status trimmer_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *port) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + struct trimmer_iterator *trimmer_it; + + trimmer_it = g_new0(struct trimmer_iterator, 1); + if (!trimmer_it) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + trimmer_it->trimmer_comp = bt_self_component_get_data( + bt_self_component_filter_as_self_component(self_comp)); + BT_ASSERT(trimmer_it->trimmer_comp); + + if (trimmer_it->trimmer_comp->begin.is_set && + trimmer_it->trimmer_comp->end.is_set) { + /* + * Both trimming time range's bounds are set, so skip + * the + * `TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN` + * phase. + */ + trimmer_it->state = TRIMMER_ITERATOR_STATE_SEEK_INITIALLY; + } + + trimmer_it->begin = trimmer_it->trimmer_comp->begin; + trimmer_it->end = trimmer_it->trimmer_comp->end; + trimmer_it->upstream_iter = + bt_self_component_port_input_message_iterator_create( + bt_self_component_filter_borrow_input_port_by_name( + self_comp, in_port_name)); + if (!trimmer_it->upstream_iter) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + trimmer_it->output_messages = g_queue_new(); + if (!trimmer_it->output_messages) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + trimmer_it->stream_states = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, + (GDestroyNotify) destroy_trimmer_iterator_stream_state); + if (!trimmer_it->stream_states) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + trimmer_it->self_msg_iter = self_msg_iter; + bt_self_message_iterator_set_data(self_msg_iter, trimmer_it); + +end: + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK && trimmer_it) { + destroy_trimmer_iterator(trimmer_it); + } + + return status; +} + +static inline +int get_msg_ns_from_origin(const bt_message *msg, int64_t *ns_from_origin, + bool *skip) +{ + const bt_clock_class *clock_class = NULL; + const bt_clock_snapshot *clock_snapshot = NULL; + bt_message_stream_activity_clock_snapshot_state sa_cs_state; + int ret = 0; + + BT_ASSERT(msg); + BT_ASSERT(ns_from_origin); + BT_ASSERT(skip); + + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_EVENT: + clock_class = + bt_message_event_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + clock_class = + bt_message_packet_beginning_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_PACKET_END: + clock_class = + bt_message_packet_end_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + clock_class = + bt_message_discarded_events_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + clock_class = + bt_message_discarded_packets_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + clock_class = + bt_message_stream_activity_beginning_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN || + sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE) { + /* Lowest possible time to always include them */ + *ns_from_origin = INT64_MIN; + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + clock_class = + bt_message_stream_activity_end_borrow_stream_class_default_clock_class_const( + msg); + if (unlikely(!clock_class)) { + goto error; + } + + sa_cs_state = bt_message_stream_activity_end_borrow_default_clock_snapshot_const( + msg, &clock_snapshot); + if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_UNKNOWN) { + /* Lowest time to always include it */ + *ns_from_origin = INT64_MIN; + goto no_clock_snapshot; + } else if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE) { + /* Greatest time to always exclude it */ + *ns_from_origin = INT64_MAX; + goto no_clock_snapshot; + } + + break; + case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY: + clock_snapshot = + bt_message_message_iterator_inactivity_borrow_default_clock_snapshot_const( + msg); + break; + default: + goto no_clock_snapshot; + } + + ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, + ns_from_origin); + if (unlikely(ret)) { + goto error; + } + + goto end; + +no_clock_snapshot: + *skip = true; + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static inline +void put_messages(bt_message_array_const msgs, uint64_t count) +{ + uint64_t i; + + for (i = 0; i < count; i++) { + BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]); + } +} + +static inline +int set_trimmer_iterator_bound(struct trimmer_bound *bound, + int64_t ns_from_origin, bool is_gmt) +{ + struct tm tm; + time_t time_seconds = (time_t) (ns_from_origin / NS_PER_S); + int ret = 0; + + BT_ASSERT(!bound->is_set); + errno = 0; + + /* We only need to extract the date from this time */ + if (is_gmt) { + bt_gmtime_r(&time_seconds, &tm); + } else { + bt_localtime_r(&time_seconds, &tm); + } + + if (errno) { + BT_LOGE_ERRNO("Cannot convert timestamp to date and time", + "ts=%" PRId64, (int64_t) time_seconds); + ret = -1; + goto end; + } + + ret = set_bound_ns_from_origin(bound, tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, bound->time.hour, bound->time.minute, + bound->time.second, bound->time.ns, is_gmt); + +end: + return ret; +} + +static +bt_self_message_iterator_status state_set_trimmer_iterator_bounds( + struct trimmer_iterator *trimmer_it) +{ + bt_message_iterator_status upstream_iter_status = + BT_MESSAGE_ITERATOR_STATUS_OK; + struct trimmer_comp *trimmer_comp = trimmer_it->trimmer_comp; + bt_message_array_const msgs; + uint64_t count = 0; + int64_t ns_from_origin = INT64_MIN; + uint64_t i; + int ret; + + BT_ASSERT(!trimmer_it->begin.is_set || + !trimmer_it->end.is_set); + + while (true) { + upstream_iter_status = + bt_self_component_port_input_message_iterator_next( + trimmer_it->upstream_iter, &msgs, &count); + if (upstream_iter_status != BT_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + for (i = 0; i < count; i++) { + const bt_message *msg = msgs[i]; + bool skip = false; + int ret; + + ret = get_msg_ns_from_origin(msg, &ns_from_origin, + &skip); + if (ret) { + goto error; + } + + if (skip) { + continue; + } + + BT_ASSERT(ns_from_origin != INT64_MIN && + ns_from_origin != INT64_MAX); + put_messages(msgs, count); + goto found; + } + + put_messages(msgs, count); + } + +found: + if (!trimmer_it->begin.is_set) { + BT_ASSERT(!trimmer_it->begin.is_infinite); + ret = set_trimmer_iterator_bound(&trimmer_it->begin, + ns_from_origin, trimmer_comp->is_gmt); + if (ret) { + goto error; + } + } + + if (!trimmer_it->end.is_set) { + BT_ASSERT(!trimmer_it->end.is_infinite); + ret = set_trimmer_iterator_bound(&trimmer_it->end, + ns_from_origin, trimmer_comp->is_gmt); + if (ret) { + goto error; + } + } + + ret = validate_trimmer_bounds(&trimmer_it->begin, + &trimmer_it->end); + if (ret) { + goto error; + } + + goto end; + +error: + put_messages(msgs, count); + upstream_iter_status = BT_MESSAGE_ITERATOR_STATUS_ERROR; + +end: + return (int) upstream_iter_status; +} + +static +bt_self_message_iterator_status state_seek_initially( + struct trimmer_iterator *trimmer_it) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(trimmer_it->begin.is_set); + + if (trimmer_it->begin.is_infinite) { + if (!bt_self_component_port_input_message_iterator_can_seek_beginning( + trimmer_it->upstream_iter)) { + BT_LOGE_STR("Cannot make upstream message iterator initially seek its beginning."); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + status = (int) bt_self_component_port_input_message_iterator_seek_beginning( + trimmer_it->upstream_iter); + } else { + if (!bt_self_component_port_input_message_iterator_can_seek_ns_from_origin( + trimmer_it->upstream_iter, + trimmer_it->begin.ns_from_origin)) { + BT_LOGE("Cannot make upstream message iterator initially seek: " + "seek-ns-from-origin=%" PRId64, + trimmer_it->begin.ns_from_origin); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + status = (int) bt_self_component_port_input_message_iterator_seek_ns_from_origin( + trimmer_it->upstream_iter, trimmer_it->begin.ns_from_origin); + } + + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + trimmer_it->state = TRIMMER_ITERATOR_STATE_TRIM; + } + +end: + return status; +} + +static inline +void push_message(struct trimmer_iterator *trimmer_it, const bt_message *msg) +{ + g_queue_push_head(trimmer_it->output_messages, (void *) msg); +} + +static inline +const bt_message *pop_message(struct trimmer_iterator *trimmer_it) +{ + return g_queue_pop_tail(trimmer_it->output_messages); +} + +static inline +int clock_raw_value_from_ns_from_origin(const bt_clock_class *clock_class, + int64_t ns_from_origin, uint64_t *raw_value) +{ + + int64_t cc_offset_s; + uint64_t cc_offset_cycles; + uint64_t cc_freq; + + bt_clock_class_get_offset(clock_class, &cc_offset_s, &cc_offset_cycles); + cc_freq = bt_clock_class_get_frequency(clock_class); + return bt_common_clock_value_from_ns_from_origin(cc_offset_s, + cc_offset_cycles, cc_freq, ns_from_origin, raw_value); +} + +static inline +bt_self_message_iterator_status end_stream(struct trimmer_iterator *trimmer_it, + struct trimmer_iterator_stream_state *sstate) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + uint64_t raw_value; + const bt_clock_class *clock_class; + int ret; + bt_message *msg = NULL; + + BT_ASSERT(!trimmer_it->end.is_infinite); + + if (!sstate->stream) { + goto end; + } + + if (sstate->cur_packet) { + /* + * The last message could not have been a stream + * activity end message if we have a current packet. + */ + BT_ASSERT(!sstate->last_msg_is_stream_activity_end); + + /* + * Create and push a packet end message, making its time + * the trimming range's end time. + */ + clock_class = bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(sstate->stream)); + BT_ASSERT(clock_class); + ret = clock_raw_value_from_ns_from_origin(clock_class, + trimmer_it->end.ns_from_origin, &raw_value); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + msg = bt_message_packet_end_create_with_default_clock_snapshot( + trimmer_it->self_msg_iter, sstate->cur_packet, + raw_value); + if (!msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + push_message(trimmer_it, msg); + msg = NULL; + BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); + + /* + * Because we generated a packet end message, set the + * stream activity end message's time to use to the + * trimming range's end time (this packet end message's + * time). + */ + sstate->stream_act_end_ns_from_origin = + trimmer_it->end.ns_from_origin; + } + + if (!sstate->last_msg_is_stream_activity_end) { + /* Create and push a stream activity end message */ + msg = bt_message_stream_activity_end_create( + trimmer_it->self_msg_iter, sstate->stream); + if (!msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + clock_class = bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(sstate->stream)); + BT_ASSERT(clock_class); + + if (sstate->stream_act_end_ns_from_origin == INT64_MIN) { + /* + * We received at least what is necessary to + * have a stream state (stream beginning and + * stream activity beginning messages), but + * nothing else: use the trimmer range's end + * time. + */ + sstate->stream_act_end_ns_from_origin = + trimmer_it->end.ns_from_origin; + } + + ret = clock_raw_value_from_ns_from_origin(clock_class, + sstate->stream_act_end_ns_from_origin, &raw_value); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + bt_message_stream_activity_end_set_default_clock_snapshot( + msg, raw_value); + push_message(trimmer_it, msg); + msg = NULL; + } + + /* Create and push a stream end message */ + msg = bt_message_stream_end_create(trimmer_it->self_msg_iter, + sstate->stream); + if (!msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + push_message(trimmer_it, msg); + msg = NULL; + + /* + * Just to make sure that we don't use this stream state again + * in the future without an obvious error. + */ + sstate->stream = NULL; + +end: + bt_message_put_ref(msg); + return status; +} + +static inline +bt_self_message_iterator_status end_iterator_streams( + struct trimmer_iterator *trimmer_it) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + GHashTableIter iter; + gpointer key, sstate; + + if (trimmer_it->end.is_infinite) { + /* + * An infinite trimming range's end time guarantees that + * we received (and pushed) all the appropriate end + * messages. + */ + goto remove_all; + } + + /* + * End each stream and then remove them from the hash table of + * stream states to release unneeded references. + */ + g_hash_table_iter_init(&iter, trimmer_it->stream_states); + + while (g_hash_table_iter_next(&iter, &key, &sstate)) { + status = end_stream(trimmer_it, sstate); + if (status) { + goto end; + } + } + +remove_all: + g_hash_table_remove_all(trimmer_it->stream_states); + +end: + return status; +} + +static inline +bt_self_message_iterator_status create_stream_beginning_activity_message( + struct trimmer_iterator *trimmer_it, + const bt_stream *stream, + const bt_clock_class *clock_class, bt_message **msg) +{ + bt_message *local_msg; + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(msg); + BT_ASSERT(!trimmer_it->begin.is_infinite); + + local_msg = bt_message_stream_activity_beginning_create( + trimmer_it->self_msg_iter, stream); + if (!local_msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + if (clock_class) { + int ret; + uint64_t raw_value; + + ret = clock_raw_value_from_ns_from_origin(clock_class, + trimmer_it->begin.ns_from_origin, &raw_value); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + bt_message_put_ref(local_msg); + goto end; + } + + bt_message_stream_activity_beginning_set_default_clock_snapshot( + local_msg, raw_value); + } + + BT_MESSAGE_MOVE_REF(*msg, local_msg); + +end: + return status; +} + +/* + * Makes sure to initialize a stream state, pushing the appropriate + * initial messages. + * + * `stream_act_beginning_msg` is an initial stream activity beginning + * message to potentially use, depending on its clock snapshot state. + * This function consumes `stream_act_beginning_msg` unconditionally. + */ +static inline +bt_self_message_iterator_status ensure_stream_state_is_inited( + struct trimmer_iterator *trimmer_it, + struct trimmer_iterator_stream_state *sstate, + const bt_message *stream_act_beginning_msg) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + bt_message *new_msg = NULL; + const bt_clock_class *clock_class = + bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(sstate->stream)); + + BT_ASSERT(!sstate->inited); + + if (!sstate->stream_beginning_msg) { + /* No initial stream beginning message: create one */ + sstate->stream_beginning_msg = + bt_message_stream_beginning_create( + trimmer_it->self_msg_iter, sstate->stream); + if (!sstate->stream_beginning_msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + } + + /* Push initial stream beginning message */ + BT_ASSERT(sstate->stream_beginning_msg); + push_message(trimmer_it, sstate->stream_beginning_msg); + sstate->stream_beginning_msg = NULL; + + if (stream_act_beginning_msg) { + /* + * Initial stream activity beginning message exists: if + * its time is -inf, then create and push a new one + * having the trimming range's beginning time. Otherwise + * push it as is (known and unknown). + */ + const bt_clock_snapshot *cs; + bt_message_stream_activity_clock_snapshot_state sa_cs_state; + + sa_cs_state = bt_message_stream_activity_beginning_borrow_default_clock_snapshot_const( + stream_act_beginning_msg, &cs); + if (sa_cs_state == BT_MESSAGE_STREAM_ACTIVITY_CLOCK_SNAPSHOT_STATE_INFINITE && + !trimmer_it->begin.is_infinite) { + /* + * -inf time: use trimming range's beginning + * time (which is not -inf). + */ + status = create_stream_beginning_activity_message( + trimmer_it, sstate->stream, clock_class, + &new_msg); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + push_message(trimmer_it, new_msg); + new_msg = NULL; + } else { + /* Known/unknown: push as is */ + push_message(trimmer_it, stream_act_beginning_msg); + stream_act_beginning_msg = NULL; + } + } else { + BT_ASSERT(!trimmer_it->begin.is_infinite); + + /* + * No stream beginning activity message: create and push + * a new message. + */ + status = create_stream_beginning_activity_message( + trimmer_it, sstate->stream, clock_class, &new_msg); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + push_message(trimmer_it, new_msg); + new_msg = NULL; + } + + sstate->inited = true; + +end: + bt_message_put_ref(new_msg); + bt_message_put_ref(stream_act_beginning_msg); + return status; +} + +static inline +bt_self_message_iterator_status ensure_cur_packet_exists( + struct trimmer_iterator *trimmer_it, + struct trimmer_iterator_stream_state *sstate, + const bt_packet *packet) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + int ret; + const bt_clock_class *clock_class = + bt_stream_class_borrow_default_clock_class_const( + bt_stream_borrow_class_const(sstate->stream)); + bt_message *msg = NULL; + uint64_t raw_value; + + BT_ASSERT(!trimmer_it->begin.is_infinite); + BT_ASSERT(!sstate->cur_packet); + + /* + * Create and push an initial packet beginning message, + * making its time the trimming range's beginning time. + */ + ret = clock_raw_value_from_ns_from_origin(clock_class, + trimmer_it->begin.ns_from_origin, &raw_value); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + msg = bt_message_packet_beginning_create_with_default_clock_snapshot( + trimmer_it->self_msg_iter, packet, raw_value); + if (!msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + push_message(trimmer_it, msg); + msg = NULL; + + /* Set packet as this stream's current packet */ + sstate->cur_packet = packet; + bt_packet_get_ref(sstate->cur_packet); + +end: + bt_message_put_ref(msg); + return status; +} + +/* + * Handles a message which is associated to a given stream state. This + * _could_ make the iterator's output message queue grow; this could + * also consume the message without pushing anything to this queue, only + * modifying the stream state. + * + * This function consumes the `msg` reference, _whatever the outcome_. + * + * `ns_from_origin` is the message's time, as given by + * get_msg_ns_from_origin(). + * + * This function sets `reached_end` if handling this message made the + * iterator reach the end of the trimming range. Note that the output + * message queue could contain messages even if this function sets + * `reached_end`. + */ +static inline +bt_self_message_iterator_status handle_message_with_stream_state( + struct trimmer_iterator *trimmer_it, const bt_message *msg, + struct trimmer_iterator_stream_state *sstate, + int64_t ns_from_origin, bool *reached_end) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + bt_message_type msg_type = bt_message_get_type(msg); + int ret; + + switch (msg_type) { + case BT_MESSAGE_TYPE_EVENT: + if (unlikely(!trimmer_it->end.is_infinite && + ns_from_origin > trimmer_it->end.ns_from_origin)) { + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (unlikely(!sstate->inited)) { + status = ensure_stream_state_is_inited(trimmer_it, + sstate, NULL); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + if (unlikely(!sstate->cur_packet)) { + const bt_event *event = + bt_message_event_borrow_event_const(msg); + const bt_packet *packet = bt_event_borrow_packet_const( + event); + + status = ensure_cur_packet_exists(trimmer_it, sstate, + packet); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + BT_ASSERT(sstate->cur_packet); + push_message(trimmer_it, msg); + msg = NULL; + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + if (unlikely(!trimmer_it->end.is_infinite && + ns_from_origin > trimmer_it->end.ns_from_origin)) { + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (unlikely(!sstate->inited)) { + status = ensure_stream_state_is_inited(trimmer_it, + sstate, NULL); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + BT_ASSERT(!sstate->cur_packet); + sstate->cur_packet = + bt_message_packet_beginning_borrow_packet_const(msg); + bt_packet_get_ref(sstate->cur_packet); + push_message(trimmer_it, msg); + msg = NULL; + break; + case BT_MESSAGE_TYPE_PACKET_END: + sstate->stream_act_end_ns_from_origin = ns_from_origin; + + if (unlikely(!trimmer_it->end.is_infinite && + ns_from_origin > trimmer_it->end.ns_from_origin)) { + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (unlikely(!sstate->inited)) { + status = ensure_stream_state_is_inited(trimmer_it, + sstate, NULL); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + if (unlikely(!sstate->cur_packet)) { + const bt_packet *packet = + bt_message_packet_end_borrow_packet_const(msg); + + status = ensure_cur_packet_exists(trimmer_it, sstate, + packet); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + BT_ASSERT(sstate->cur_packet); + BT_PACKET_PUT_REF_AND_RESET(sstate->cur_packet); + push_message(trimmer_it, msg); + msg = NULL; + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + { + /* + * `ns_from_origin` is the message's time range's + * beginning time here. + */ + int64_t end_ns_from_origin; + const bt_clock_snapshot *end_cs; + + if (bt_message_get_type(msg) == + BT_MESSAGE_TYPE_DISCARDED_EVENTS) { + /* + * Safe to ignore the return value because we + * know there's a default clock and it's always + * known. + */ + end_cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const( + msg); + } else { + /* + * Safe to ignore the return value because we + * know there's a default clock and it's always + * known. + */ + end_cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const( + msg); + } + + if (bt_clock_snapshot_get_ns_from_origin(end_cs, + &end_ns_from_origin)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + sstate->stream_act_end_ns_from_origin = end_ns_from_origin; + + if (!trimmer_it->end.is_infinite && + ns_from_origin > trimmer_it->end.ns_from_origin) { + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (!trimmer_it->end.is_infinite && + end_ns_from_origin > trimmer_it->end.ns_from_origin) { + /* + * This message's end time is outside the + * trimming time range: replace it with a new + * message having an end time equal to the + * trimming time range's end and without a + * count. + */ + const bt_clock_class *clock_class = + bt_clock_snapshot_borrow_clock_class_const( + end_cs); + const bt_clock_snapshot *begin_cs; + bt_message *new_msg; + uint64_t end_raw_value; + + ret = clock_raw_value_from_ns_from_origin(clock_class, + trimmer_it->end.ns_from_origin, &end_raw_value); + if (ret) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS) { + begin_cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const( + msg); + new_msg = bt_message_discarded_events_create_with_default_clock_snapshots( + trimmer_it->self_msg_iter, + sstate->stream, + bt_clock_snapshot_get_value(begin_cs), + end_raw_value); + } else { + begin_cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const( + msg); + new_msg = bt_message_discarded_packets_create_with_default_clock_snapshots( + trimmer_it->self_msg_iter, + sstate->stream, + bt_clock_snapshot_get_value(begin_cs), + end_raw_value); + } + + if (!new_msg) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + /* Replace the original message */ + BT_MESSAGE_MOVE_REF(msg, new_msg); + } + + if (unlikely(!sstate->inited)) { + status = ensure_stream_state_is_inited(trimmer_it, + sstate, NULL); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } + + push_message(trimmer_it, msg); + msg = NULL; + break; + } + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + if (!trimmer_it->end.is_infinite && + ns_from_origin > trimmer_it->end.ns_from_origin) { + /* + * This only happens when the message's time is + * known and is greater than the trimming + * range's end time. Unknown and -inf times are + * always less than + * `trimmer_it->end.ns_from_origin`. + */ + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (!sstate->inited) { + status = ensure_stream_state_is_inited(trimmer_it, + sstate, msg); + msg = NULL; + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } else { + push_message(trimmer_it, msg); + msg = NULL; + } + + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + if (trimmer_it->end.is_infinite) { + push_message(trimmer_it, msg); + msg = NULL; + break; + } + + if (ns_from_origin == INT64_MIN) { + /* Unknown: push as is if stream state is inited */ + if (sstate->inited) { + push_message(trimmer_it, msg); + msg = NULL; + sstate->last_msg_is_stream_activity_end = true; + } + } else if (ns_from_origin == INT64_MAX) { + /* Infinite: use trimming range's end time */ + sstate->stream_act_end_ns_from_origin = + trimmer_it->end.ns_from_origin; + } else { + /* Known: check if outside of trimming range */ + if (ns_from_origin > trimmer_it->end.ns_from_origin) { + sstate->stream_act_end_ns_from_origin = + trimmer_it->end.ns_from_origin; + status = end_iterator_streams(trimmer_it); + *reached_end = true; + break; + } + + if (!sstate->inited) { + /* + * First message for this stream is a + * stream activity end: we can't deduce + * anything about the stream activity + * beginning's time, and using this + * message's time would make a useless + * pair of stream activity beginning/end + * with the same time. Just skip this + * message and wait for something + * useful. + */ + break; + } + + push_message(trimmer_it, msg); + msg = NULL; + sstate->last_msg_is_stream_activity_end = true; + sstate->stream_act_end_ns_from_origin = ns_from_origin; + } + + break; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + /* + * We don't know what follows at this point, so just + * keep this message until we know what to do with it + * (it will be used in ensure_stream_state_is_inited()). + */ + BT_ASSERT(!sstate->inited); + BT_MESSAGE_MOVE_REF(sstate->stream_beginning_msg, msg); + break; + case BT_MESSAGE_TYPE_STREAM_END: + if (sstate->inited) { + /* + * This is the end of an inited stream: end this + * stream if its stream activity end message + * time is not the trimming range's end time + * (which means the final stream activity end + * message had an infinite time). end_stream() + * will generate its own stream end message. + */ + if (trimmer_it->end.is_infinite) { + push_message(trimmer_it, msg); + msg = NULL; + g_hash_table_remove(trimmer_it->stream_states, + sstate->stream); + } else if (sstate->stream_act_end_ns_from_origin < + trimmer_it->end.ns_from_origin) { + status = end_stream(trimmer_it, sstate); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + /* We won't need this stream state again */ + g_hash_table_remove(trimmer_it->stream_states, + sstate->stream); + } + } else { + /* We dont't need this stream state anymore */ + g_hash_table_remove(trimmer_it->stream_states, sstate->stream); + } + + break; + default: + break; + } + +end: + /* We release the message's reference whatever the outcome */ + bt_message_put_ref(msg); + return BT_SELF_MESSAGE_ITERATOR_STATUS_OK; +} + +/* + * Handles an input message. This _could_ make the iterator's output + * message queue grow; this could also consume the message without + * pushing anything to this queue, only modifying the stream state. + * + * This function consumes the `msg` reference, _whatever the outcome_. + * + * This function sets `reached_end` if handling this message made the + * iterator reach the end of the trimming range. Note that the output + * message queue could contain messages even if this function sets + * `reached_end`. + */ +static inline +bt_self_message_iterator_status handle_message( + struct trimmer_iterator *trimmer_it, const bt_message *msg, + bool *reached_end) +{ + bt_self_message_iterator_status status; + const bt_stream *stream = NULL; + int64_t ns_from_origin = INT64_MIN; + bool skip; + int ret; + struct trimmer_iterator_stream_state *sstate = NULL; + + /* Find message's associated stream */ + switch (bt_message_get_type(msg)) { + case BT_MESSAGE_TYPE_EVENT: + stream = bt_event_borrow_stream_const( + bt_message_event_borrow_event_const(msg)); + break; + case BT_MESSAGE_TYPE_PACKET_BEGINNING: + stream = bt_packet_borrow_stream_const( + bt_message_packet_beginning_borrow_packet_const(msg)); + break; + case BT_MESSAGE_TYPE_PACKET_END: + stream = bt_packet_borrow_stream_const( + bt_message_packet_end_borrow_packet_const(msg)); + break; + case BT_MESSAGE_TYPE_DISCARDED_EVENTS: + stream = bt_message_discarded_events_borrow_stream_const(msg); + break; + case BT_MESSAGE_TYPE_DISCARDED_PACKETS: + stream = bt_message_discarded_packets_borrow_stream_const(msg); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING: + stream = bt_message_stream_activity_beginning_borrow_stream_const(msg); + break; + case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END: + stream = bt_message_stream_activity_end_borrow_stream_const(msg); + break; + case BT_MESSAGE_TYPE_STREAM_BEGINNING: + stream = bt_message_stream_beginning_borrow_stream_const(msg); + break; + case BT_MESSAGE_TYPE_STREAM_END: + stream = bt_message_stream_end_borrow_stream_const(msg); + break; + default: + break; + } + + if (likely(stream)) { + /* Find stream state */ + sstate = g_hash_table_lookup(trimmer_it->stream_states, + stream); + if (unlikely(!sstate)) { + /* No stream state yet: create one now */ + const bt_stream_class *sc; + + /* + * Validate right now that the stream's class + * has a registered default clock class so that + * an existing stream state guarantees existing + * default clock snapshots for its associated + * messages. + * + * Also check that clock snapshots are always + * known. + */ + sc = bt_stream_borrow_class_const(stream); + if (!bt_stream_class_borrow_default_clock_class_const(sc)) { + BT_LOGE("Unsupported stream: stream class does " + "not have a default clock class: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + stream, bt_stream_get_id(stream), + bt_stream_get_name(stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + /* + * Temporary: make sure packet beginning, packet + * end, discarded events, and discarded packets + * messages have default clock snapshots until + * the support for not having them is + * implemented. + */ + if (!bt_stream_class_packets_have_beginning_default_clock_snapshot( + sc)) { + BT_LOGE("Unsupported stream: packets have " + "no beginning clock snapshot: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + stream, bt_stream_get_id(stream), + bt_stream_get_name(stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (!bt_stream_class_packets_have_end_default_clock_snapshot( + sc)) { + BT_LOGE("Unsupported stream: packets have " + "no end clock snapshot: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + stream, bt_stream_get_id(stream), + bt_stream_get_name(stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (bt_stream_class_supports_discarded_events(sc) && + !bt_stream_class_discarded_events_have_default_clock_snapshots(sc)) { + BT_LOGE("Unsupported stream: discarded events " + "have no clock snapshots: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + stream, bt_stream_get_id(stream), + bt_stream_get_name(stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (bt_stream_class_supports_discarded_packets(sc) && + !bt_stream_class_discarded_packets_have_default_clock_snapshots(sc)) { + BT_LOGE("Unsupported stream: discarded packets " + "have no clock snapshots: " + "stream-addr=%p, " + "stream-id=%" PRIu64 ", " + "stream-name=\"%s\"", + stream, bt_stream_get_id(stream), + bt_stream_get_name(stream)); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + sstate = g_new0(struct trimmer_iterator_stream_state, + 1); + if (!sstate) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_NOMEM; + goto end; + } + + sstate->stream = stream; + sstate->stream_act_end_ns_from_origin = INT64_MIN; + g_hash_table_insert(trimmer_it->stream_states, + (void *) stream, sstate); + } + } + + /* Retrieve the message's time */ + ret = get_msg_ns_from_origin(msg, &ns_from_origin, &skip); + if (unlikely(ret)) { + status = BT_SELF_MESSAGE_ITERATOR_STATUS_ERROR; + goto end; + } + + if (likely(sstate)) { + /* Message associated to a stream */ + status = handle_message_with_stream_state(trimmer_it, msg, + sstate, ns_from_origin, reached_end); + + /* + * handle_message_with_stream_state() unconditionally + * consumes `msg`. + */ + msg = NULL; + } else { + /* + * Message not associated to a stream (message iterator + * inactivity). + */ + if (unlikely(ns_from_origin > trimmer_it->end.ns_from_origin)) { + BT_MESSAGE_PUT_REF_AND_RESET(msg); + status = end_iterator_streams(trimmer_it); + *reached_end = true; + } else { + push_message(trimmer_it, msg); + status = BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + msg = NULL; + } + } + +end: + /* We release the message's reference whatever the outcome */ + bt_message_put_ref(msg); + return status; +} + +static inline +void fill_message_array_from_output_messages( + struct trimmer_iterator *trimmer_it, + bt_message_array_const msgs, uint64_t capacity, uint64_t *count) +{ + *count = 0; + + /* + * Move auto-seek messages to the output array (which is this + * iterator's base message array). + */ + while (capacity > 0 && !g_queue_is_empty(trimmer_it->output_messages)) { + msgs[*count] = pop_message(trimmer_it); + capacity--; + (*count)++; + } + + BT_ASSERT(*count > 0); +} + +static inline +bt_self_message_iterator_status state_ending( + struct trimmer_iterator *trimmer_it, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + if (g_queue_is_empty(trimmer_it->output_messages)) { + trimmer_it->state = TRIMMER_ITERATOR_STATE_ENDED; + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + goto end; + } + + fill_message_array_from_output_messages(trimmer_it, msgs, + capacity, count); + +end: + return status; +} + +static inline +bt_self_message_iterator_status state_trim(struct trimmer_iterator *trimmer_it, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + bt_message_array_const my_msgs; + uint64_t my_count; + uint64_t i; + bool reached_end = false; + + while (g_queue_is_empty(trimmer_it->output_messages)) { + status = (int) bt_self_component_port_input_message_iterator_next( + trimmer_it->upstream_iter, &my_msgs, &my_count); + if (unlikely(status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK)) { + if (status == BT_SELF_MESSAGE_ITERATOR_STATUS_END) { + status = end_iterator_streams(trimmer_it); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + trimmer_it->state = + TRIMMER_ITERATOR_STATE_ENDING; + status = state_ending(trimmer_it, msgs, + capacity, count); + } + + goto end; + } + + BT_ASSERT(my_count > 0); + + for (i = 0; i < my_count; i++) { + status = handle_message(trimmer_it, my_msgs[i], + &reached_end); + + /* + * handle_message() unconditionally consumes the + * message reference. + */ + my_msgs[i] = NULL; + + if (unlikely(status != + BT_SELF_MESSAGE_ITERATOR_STATUS_OK)) { + put_messages(my_msgs, my_count); + goto end; + } + + if (unlikely(reached_end)) { + /* + * This message's time was passed the + * trimming time range's end time: we + * are done. Their might still be + * messages in the output message queue, + * so move to the "ending" state and + * apply it immediately since + * state_trim() is called within the + * "next" method. + */ + put_messages(my_msgs, my_count); + trimmer_it->state = + TRIMMER_ITERATOR_STATE_ENDING; + status = state_ending(trimmer_it, msgs, + capacity, count); + goto end; + } + } + } + + /* + * There's at least one message in the output message queue: + * move the messages to the output message array. + */ + BT_ASSERT(!g_queue_is_empty(trimmer_it->output_messages)); + fill_message_array_from_output_messages(trimmer_it, msgs, + capacity, count); + +end: + return status; +} + +BT_HIDDEN +bt_self_message_iterator_status trimmer_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count) +{ + struct trimmer_iterator *trimmer_it = + bt_self_message_iterator_get_data(self_msg_iter); + bt_self_message_iterator_status status = + BT_SELF_MESSAGE_ITERATOR_STATUS_OK; + + BT_ASSERT(trimmer_it); + + if (likely(trimmer_it->state == TRIMMER_ITERATOR_STATE_TRIM)) { + status = state_trim(trimmer_it, msgs, capacity, count); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + } else { + switch (trimmer_it->state) { + case TRIMMER_ITERATOR_STATE_SET_BOUNDS_NS_FROM_ORIGIN: + status = state_set_trimmer_iterator_bounds(trimmer_it); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + status = state_seek_initially(trimmer_it); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + status = state_trim(trimmer_it, msgs, capacity, count); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + break; + case TRIMMER_ITERATOR_STATE_SEEK_INITIALLY: + status = state_seek_initially(trimmer_it); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + status = state_trim(trimmer_it, msgs, capacity, count); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + break; + case TRIMMER_ITERATOR_STATE_ENDING: + status = state_ending(trimmer_it, msgs, capacity, + count); + if (status != BT_SELF_MESSAGE_ITERATOR_STATUS_OK) { + goto end; + } + + break; + case TRIMMER_ITERATOR_STATE_ENDED: + status = BT_SELF_MESSAGE_ITERATOR_STATUS_END; + break; + default: + abort(); + } + } + +end: + return status; +} + +BT_HIDDEN +void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter) +{ + struct trimmer_iterator *trimmer_it = + bt_self_message_iterator_get_data(self_msg_iter); + + BT_ASSERT(trimmer_it); + destroy_trimmer_iterator(trimmer_it); +} diff --git a/src/plugins/utils/trimmer/trimmer.h b/src/plugins/utils/trimmer/trimmer.h new file mode 100644 index 00000000..27f23294 --- /dev/null +++ b/src/plugins/utils/trimmer/trimmer.h @@ -0,0 +1,56 @@ +#ifndef BABELTRACE_PLUGINS_UTILS_TRIMMER_H +#define BABELTRACE_PLUGINS_UTILS_TRIMMER_H + +/* + * BabelTrace - Trace Trimmer Plug-in + * + * Copyright 2016 Jérémie Galarneau + * + * Author: Jérémie Galarneau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "common/babeltrace.h" +#include + +BT_HIDDEN +void trimmer_finalize(bt_self_component_filter *self_comp); + +BT_HIDDEN +bt_self_component_status trimmer_init(bt_self_component_filter *self_comp, + const bt_value *params, void *init_data); + +BT_HIDDEN +bt_self_message_iterator_status trimmer_msg_iter_init( + bt_self_message_iterator *self_msg_iter, + bt_self_component_filter *self_comp, + bt_self_component_port_output *port); + +BT_HIDDEN +bt_self_message_iterator_status trimmer_msg_iter_next( + bt_self_message_iterator *self_msg_iter, + bt_message_array_const msgs, uint64_t capacity, + uint64_t *count); + +BT_HIDDEN +void trimmer_msg_iter_finalize(bt_self_message_iterator *self_msg_iter); + +#endif /* BABELTRACE_PLUGINS_UTILS_TRIMMER_H */ diff --git a/src/python-plugin-provider/Makefile.am b/src/python-plugin-provider/Makefile.am new file mode 100644 index 00000000..e388406a --- /dev/null +++ b/src/python-plugin-provider/Makefile.am @@ -0,0 +1,25 @@ +if ENABLE_PYTHON_PLUGINS +AM_CPPFLAGS += $(PYTHON_INCLUDE) + +lib_LTLIBRARIES = libbabeltrace2-python-plugin-provider.la + +libbabeltrace2_python_plugin_provider_la_SOURCES = \ + python-plugin-provider.c \ + python-plugin-provider.h + +libbabeltrace2_python_plugin_provider_la_LDFLAGS = \ + $(LT_NO_UNDEFINED) \ + -version-info $(BABELTRACE_LIBRARY_VERSION) \ + $(PYTHON_LIBS) + +libbabeltrace2_python_plugin_provider_la_LIBADD = + +# Link the Python plugin provider library with libbabeltrace2 +# when it's not built-in the babeltrace2 executable. +if !ENABLE_BUILT_IN_PLUGINS +libbabeltrace2_python_plugin_provider_la_LIBADD += \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ + $(top_builddir)/src/lib/libbabeltrace2.la +endif +endif # ENABLE_PYTHON_PLUGINS diff --git a/src/python-plugin-provider/python-plugin-provider.c b/src/python-plugin-provider/python-plugin-provider.c new file mode 100644 index 00000000..35f87555 --- /dev/null +++ b/src/python-plugin-provider/python-plugin-provider.c @@ -0,0 +1,497 @@ +/* + * python-plugin-provider.c + * + * Babeltrace Python plugin provider + * + * Copyright 2017 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BT_LOG_TAG "PLUGIN-PY" + +#include "common/babeltrace.h" +#include "compat/compiler.h" +#include +#include "lib/plugin/plugin.h" +#include +#include "lib/graph/component-class.h" +#include +#include +#include +#include +#include + +#define PYTHON_PLUGIN_FILE_PREFIX "bt_plugin_" +#define PYTHON_PLUGIN_FILE_PREFIX_LEN (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1) +#define PYTHON_PLUGIN_FILE_EXT ".py" +#define PYTHON_PLUGIN_FILE_EXT_LEN (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1) + +enum python_state { + /* init_python() not called yet */ + PYTHON_STATE_NOT_INITED, + + /* init_python() called once with success */ + PYTHON_STATE_FULLY_INITIALIZED, + + /* init_python() called once without success */ + PYTHON_STATE_CANNOT_INITIALIZE, +} python_state = PYTHON_STATE_NOT_INITED; + +static PyObject *py_try_load_plugin_module_func = NULL; +static bool python_was_initialized_by_us; + +static +void print_python_traceback_warn(void) +{ + if (BT_LOG_ON_WARN && Py_IsInitialized() && PyErr_Occurred()) { + BT_LOGW_STR("Exception occured: traceback: "); + PyErr_Print(); + } +} + +static +void pyerr_clear(void) +{ + if (Py_IsInitialized()) { + PyErr_Clear(); + } +} + +static +void init_python(void) +{ + PyObject *py_bt2_py_plugin_mod = NULL; + const char *dis_python_env; +#ifndef __MINGW32__ + sighandler_t old_sigint = signal(SIGINT, SIG_DFL); +#endif + + if (python_state != PYTHON_STATE_NOT_INITED) { + goto end; + } + + /* + * User can disable Python plugin support with the + * BABELTRACE_DISABLE_PYTHON_PLUGINS environment variable set to + * 1. + */ + dis_python_env = getenv("BABELTRACE_DISABLE_PYTHON_PLUGINS"); + if (dis_python_env && strcmp(dis_python_env, "1") == 0) { + BT_LOGI_STR("Python plugin support is disabled because `BABELTRACE_DISABLE_PYTHON_PLUGINS=1`."); + python_state = PYTHON_STATE_CANNOT_INITIALIZE; + goto end; + } + + if (!Py_IsInitialized()) { + BT_LOGI_STR("Python interpreter is not initialized: initializing Python interpreter."); + Py_InitializeEx(0); + python_was_initialized_by_us = true; + BT_LOGI("Initialized Python interpreter: version=\"%s\"", + Py_GetVersion()); + } else { + BT_LOGI("Python interpreter is already initialized: version=\"%s\"", + Py_GetVersion()); + } + + py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin"); + if (!py_bt2_py_plugin_mod) { + BT_LOGI_STR("Cannot import bt2.py_plugin Python module: Python plugin support is disabled."); + python_state = PYTHON_STATE_CANNOT_INITIALIZE; + goto end; + } + + py_try_load_plugin_module_func = + PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module"); + if (!py_try_load_plugin_module_func) { + BT_LOGI_STR("Cannot get _try_load_plugin_module attribute from bt2.py_plugin Python module: Python plugin support is disabled."); + python_state = PYTHON_STATE_CANNOT_INITIALIZE; + goto end; + } + + python_state = PYTHON_STATE_FULLY_INITIALIZED; + +end: +#ifndef __MINGW32__ + if (old_sigint != SIG_ERR) { + (void) signal(SIGINT, old_sigint); + } +#endif + + print_python_traceback_warn(); + pyerr_clear(); + Py_XDECREF(py_bt2_py_plugin_mod); + return; +} + +__attribute__((destructor)) static +void fini_python(void) { + if (Py_IsInitialized() && python_was_initialized_by_us) { + if (py_try_load_plugin_module_func) { + Py_DECREF(py_try_load_plugin_module_func); + py_try_load_plugin_module_func = NULL; + } + + Py_Finalize(); + BT_LOGI_STR("Finalized Python interpreter."); + } + + python_state = PYTHON_STATE_NOT_INITED; +} + +static +bt_plugin *bt_plugin_from_python_plugin_info(PyObject *plugin_info) +{ + bt_plugin *plugin = NULL; + PyObject *py_name = NULL; + PyObject *py_author = NULL; + PyObject *py_description = NULL; + PyObject *py_license = NULL; + PyObject *py_version = NULL; + PyObject *py_comp_class_addrs = NULL; + const char *name = NULL; + const char *author = NULL; + const char *description = NULL; + const char *license = NULL; + unsigned int major = 0, minor = 0, patch = 0; + const char *version_extra = NULL; + int ret; + + BT_ASSERT(plugin_info); + BT_ASSERT(python_state == PYTHON_STATE_FULLY_INITIALIZED); + py_name = PyObject_GetAttrString(plugin_info, "name"); + if (!py_name) { + BT_LOGW("Cannot find `name` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + py_author = PyObject_GetAttrString(plugin_info, "author"); + if (!py_author) { + BT_LOGW("Cannot find `author` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + py_description = PyObject_GetAttrString(plugin_info, "description"); + if (!py_description) { + BT_LOGW("Cannot find `desciption` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + py_license = PyObject_GetAttrString(plugin_info, "license"); + if (!py_license) { + BT_LOGW("Cannot find `license` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + py_version = PyObject_GetAttrString(plugin_info, "version"); + if (!py_version) { + BT_LOGW("Cannot find `version` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + py_comp_class_addrs = PyObject_GetAttrString(plugin_info, + "comp_class_addrs"); + if (!py_comp_class_addrs) { + BT_LOGW("Cannot find `comp_class_addrs` attribute in Python plugin info object: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + if (PyUnicode_Check(py_name)) { + name = PyUnicode_AsUTF8(py_name); + if (!name) { + BT_LOGW("Cannot decode Python plugin name string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } else { + /* Plugin name is mandatory */ + BT_LOGW("Plugin name is not a string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + + if (PyUnicode_Check(py_author)) { + author = PyUnicode_AsUTF8(py_author); + if (!author) { + BT_LOGW("Cannot decode Python plugin author string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } + + if (PyUnicode_Check(py_description)) { + description = PyUnicode_AsUTF8(py_description); + if (!description) { + BT_LOGW("Cannot decode Python plugin description string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } + + if (PyUnicode_Check(py_license)) { + license = PyUnicode_AsUTF8(py_license); + if (!license) { + BT_LOGW("Cannot decode Python plugin license string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } + + if (PyTuple_Check(py_version)) { + if (PyTuple_Size(py_version) >= 3) { + PyObject *py_major = PyTuple_GetItem(py_version, 0); + PyObject *py_minor = PyTuple_GetItem(py_version, 1); + PyObject *py_patch = PyTuple_GetItem(py_version, 2); + + BT_ASSERT(py_major); + BT_ASSERT(py_minor); + BT_ASSERT(py_patch); + + if (PyLong_Check(py_major)) { + major = PyLong_AsUnsignedLong(py_major); + } + + if (PyLong_Check(py_minor)) { + minor = PyLong_AsUnsignedLong(py_minor); + } + + if (PyLong_Check(py_patch)) { + patch = PyLong_AsUnsignedLong(py_patch); + } + + if (PyErr_Occurred()) { + /* Overflow error, most probably */ + BT_LOGW("Invalid Python plugin version format: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } + + if (PyTuple_Size(py_version) >= 4) { + PyObject *py_extra = PyTuple_GetItem(py_version, 3); + + BT_ASSERT(py_extra); + + if (PyUnicode_Check(py_extra)) { + version_extra = PyUnicode_AsUTF8(py_extra); + if (!version_extra) { + BT_LOGW("Cannot decode Python plugin version's extra string: " + "py-plugin-info-addr=%p", plugin_info); + goto error; + } + } + } + } + + plugin = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON); + if (!plugin) { + BT_LOGE_STR("Cannot create empty plugin object."); + goto error; + } + + bt_plugin_set_name(plugin, name); + + if (description) { + bt_plugin_set_description(plugin, description); + } + + if (author) { + bt_plugin_set_author(plugin, author); + } + + if (license) { + bt_plugin_set_license(plugin, license); + } + + bt_plugin_set_version(plugin, major, minor, patch, version_extra); + + if (PyList_Check(py_comp_class_addrs)) { + size_t i; + + for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) { + bt_component_class *comp_class; + PyObject *py_comp_class_addr; + + py_comp_class_addr = + PyList_GetItem(py_comp_class_addrs, i); + BT_ASSERT(py_comp_class_addr); + if (PyLong_Check(py_comp_class_addr)) { + comp_class = PyLong_AsVoidPtr(py_comp_class_addr); + } else { + BT_LOGW("Component class address is not an integer in Python plugin info object: " + "py-plugin-info-addr=%p, index=%zu", + plugin_info, i); + continue; + } + + ret = bt_plugin_add_component_class(plugin, comp_class); + if (ret < 0) { + BT_LOGE("Cannot add component class to plugin: " + "py-plugin-info-addr=%p, " + "plugin-addr=%p, plugin-name=\"%s\", " + "comp-class-addr=%p, " + "comp-class-name=\"%s\", " + "comp-class-type=%s", + plugin_info, + plugin, bt_plugin_get_name(plugin), + comp_class, + bt_component_class_get_name(comp_class), + bt_component_class_type_string( + bt_component_class_get_type(comp_class))); + continue; + } + } + } + + goto end; + +error: + print_python_traceback_warn(); + pyerr_clear(); + BT_OBJECT_PUT_REF_AND_RESET(plugin); + +end: + Py_XDECREF(py_name); + Py_XDECREF(py_author); + Py_XDECREF(py_description); + Py_XDECREF(py_license); + Py_XDECREF(py_version); + Py_XDECREF(py_comp_class_addrs); + return plugin; +} + +G_MODULE_EXPORT +bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path) +{ + bt_plugin_set *plugin_set = NULL; + bt_plugin *plugin = NULL; + PyObject *py_plugin_info = NULL; + gchar *basename = NULL; + size_t path_len; + + BT_ASSERT(path); + + if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) { + /* + * We do not even care about the rest of the function + * here because we already know Python cannot be fully + * initialized. + */ + goto error; + } + + BT_LOGD("Creating all Python plugins from file: path=\"%s\"", path); + path_len = strlen(path); + + /* File name ends with `.py` */ + if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN, + PYTHON_PLUGIN_FILE_EXT, + PYTHON_PLUGIN_FILE_EXT_LEN) != 0) { + BT_LOGD("Skipping non-Python file: path=\"%s\"", path); + goto error; + } + + /* File name starts with `bt_plugin_` */ + basename = g_path_get_basename(path); + if (!basename) { + BT_LOGW("Cannot get path's basename: path=\"%s\"", path); + goto error; + } + + if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX, + PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) { + BT_LOGD("Skipping Python file not starting with `%s`: " + "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX, path); + goto error; + } + + /* + * Initialize Python now. + * + * This is not done in the library contructor because the + * interpreter is somewhat slow to initialize. If you don't + * have any potential Python plugins, you don't need to endure + * this waiting time everytime you load the library. + */ + init_python(); + if (python_state != PYTHON_STATE_FULLY_INITIALIZED) { + /* + * For some reason we cannot initialize Python, + * import the required modules, and get the required + * attributes from them. + */ + BT_LOGI("Failed to initialize Python interpreter."); + goto error; + } + + /* + * Call bt2.py_plugin._try_load_plugin_module() with this path + * to get plugin info if the plugin is loadable and complete. + * This function returns None when there's an error, but just in + * case we also manually clear the last Python error state. + */ + BT_LOGD_STR("Getting Python plugin info object from Python module."); + py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func, + "(s)", path); + if (!py_plugin_info || py_plugin_info == Py_None) { + BT_LOGW("Cannot load Python plugin: path=\"%s\"", path); + print_python_traceback_warn(); + PyErr_Clear(); + goto error; + } + + /* + * Get bt_plugin from plugin info object. + */ + plugin = bt_plugin_from_python_plugin_info(py_plugin_info); + if (!plugin) { + BT_LOGW("Cannot create plugin object from Python plugin info object: " + "path=\"%s\", py-plugin-info-addr=%p", + path, py_plugin_info); + goto error; + } + + bt_plugin_set_path(plugin, path); + plugin_set = bt_plugin_set_create(); + if (!plugin_set) { + BT_LOGE_STR("Cannot create empty plugin set."); + goto error; + } + + bt_plugin_set_add_plugin(plugin_set, plugin); + BT_LOGD("Created all Python plugins from file: path=\"%s\", " + "plugin-addr=%p, plugin-name=\"%s\"", + path, plugin, bt_plugin_get_name(plugin)); + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(plugin_set); + +end: + bt_plugin_put_ref(plugin); + Py_XDECREF(py_plugin_info); + g_free(basename); + return plugin_set; +} diff --git a/src/python-plugin-provider/python-plugin-provider.h b/src/python-plugin-provider/python-plugin-provider.h new file mode 100644 index 00000000..1d7aa2ce --- /dev/null +++ b/src/python-plugin-provider/python-plugin-provider.h @@ -0,0 +1,31 @@ +#ifndef BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H +#define BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H + +/* + * Copyright 2017-2018 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +extern +struct bt_plugin_set *bt_plugin_python_create_all_from_file(const char *path); + +#endif /* BABELTRACE_PLUGIN_PYTHON_PLUGIN_PROVIDER_INTERNAL_H */ diff --git a/tests/bindings/python/bt2/test_python_bt2.in b/tests/bindings/python/bt2/test_python_bt2.in index d627714f..e9941018 100644 --- a/tests/bindings/python/bt2/test_python_bt2.in +++ b/tests/bindings/python/bt2/test_python_bt2.in @@ -25,18 +25,18 @@ check_coverage() { } export BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1 -export TEST_PLUGIN_PLUGINS_PATH="${BT_BUILD_PATH}/plugins" -export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/plugins/ctf:${BT_BUILD_PATH}/plugins/utils:${BT_BUILD_PATH}/plugins/text" +export TEST_PLUGIN_PLUGINS_PATH="${BT_BUILD_PATH}/src/plugins" +export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/src/plugins/ctf:${BT_BUILD_PATH}/src/plugins/utils:${BT_BUILD_PATH}/src/plugins/text" export TEST_CTF_TRACES_PATH="${BT_SRC_PATH}/tests/ctf-traces" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" TESTRUNNER_PY="${BT_SRC_PATH}/tests/utils/python/testrunner.py" THIS_DIR="${BT_SRC_PATH}/tests/bindings/python/bt2" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${LD_LIBRARY_PATH}" fi if test "x${TESTALL_COVERAGE}" = "x1"; then diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 9cb75b00..679fda2b 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -13,17 +13,17 @@ test_bitfield_LDADD = $(LIBTAP) $(builddir)/libtestcommon.la test_ctf_writer_LDADD = \ $(COMMON_TEST_LDADD) \ - $(top_builddir)/ctf-writer/libbabeltrace2-ctf-writer.la + $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la test_bt_values_LDADD = $(COMMON_TEST_LDADD) \ - $(top_builddir)/lib/libbabeltrace2.la + $(top_builddir)/src/lib/libbabeltrace2.la test_trace_ir_ref_LDADD = $(COMMON_TEST_LDADD) \ - $(top_builddir)/lib/libbabeltrace2.la \ - $(top_builddir)/ctf-writer/libbabeltrace2-ctf-writer.la + $(top_builddir)/src/lib/libbabeltrace2.la \ + $(top_builddir)/src/ctf-writer/libbabeltrace2-ctf-writer.la test_graph_topo_LDADD = $(COMMON_TEST_LDADD) \ - $(top_builddir)/lib/libbabeltrace2.la + $(top_builddir)/src/lib/libbabeltrace2.la noinst_PROGRAMS = test_bitfield test_ctf_writer test_bt_values \ test_trace_ir_ref test_graph_topo @@ -37,7 +37,7 @@ test_graph_topo_SOURCES = test_graph_topo.c if !ENABLE_BUILT_IN_PLUGINS noinst_PROGRAMS += test_plugin test_plugin_LDADD = $(COMMON_TEST_LDADD) \ - $(top_builddir)/lib/libbabeltrace2.la + $(top_builddir)/src/lib/libbabeltrace2.la test_plugin_SOURCES = test_plugin.c SUBDIRS += test-plugin-plugins endif diff --git a/tests/lib/test-plugin-plugins/Makefile.am b/tests/lib/test-plugin-plugins/Makefile.am index 3a185b27..c852655d 100644 --- a/tests/lib/test-plugin-plugins/Makefile.am +++ b/tests/lib/test-plugin-plugins/Makefile.am @@ -6,7 +6,7 @@ plugin_minimal_la_LDFLAGS = \ $(LT_NO_UNDEFINED) \ -rpath / -avoid-version -module plugin_minimal_la_LIBADD = \ - $(top_builddir)/lib/libbabeltrace2.la + $(top_builddir)/src/lib/libbabeltrace2.la # source/filter/sink plugin plugin_sfs_la_SOURCES = sfs.c @@ -14,4 +14,4 @@ plugin_sfs_la_LDFLAGS = \ $(LT_NO_UNDEFINED) \ -rpath / -avoid-version -module plugin_sfs_la_LIBADD = \ - $(top_builddir)/lib/libbabeltrace2.la + $(top_builddir)/src/lib/libbabeltrace2.la diff --git a/tests/lib/test-plugin-plugins/sfs.c b/tests/lib/test-plugin-plugins/sfs.c index d746afae..4431abc0 100644 --- a/tests/lib/test-plugin-plugins/sfs.c +++ b/tests/lib/test-plugin-plugins/sfs.c @@ -16,7 +16,7 @@ */ #include -#include +#include "common/assert.h" static bt_self_component_status sink_consume( bt_self_component_sink *self_comp) diff --git a/tests/lib/test_bitfield.c b/tests/lib/test_bitfield.c index 848d4bd3..b9bd6d40 100644 --- a/tests/lib/test_bitfield.c +++ b/tests/lib/test_bitfield.c @@ -19,7 +19,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include +#include "compat/bitfield.h" #include #include #include diff --git a/tests/lib/test_bt_values.c b/tests/lib/test_bt_values.c index f9526379..d16c67a5 100644 --- a/tests/lib/test_bt_values.c +++ b/tests/lib/test_bt_values.c @@ -21,7 +21,7 @@ */ #include -#include +#include "common/assert.h" #include #include "tap/tap.h" diff --git a/tests/lib/test_ctf_writer.c b/tests/lib/test_ctf_writer.c index 75b914ce..1ed4cc67 100644 --- a/tests/lib/test_ctf_writer.c +++ b/tests/lib/test_ctf_writer.c @@ -32,12 +32,12 @@ #include #include #include -#include +#include "compat/stdlib.h" #include -#include -#include +#include "compat/limits.h" +#include "compat/stdio.h" #include -#include +#include "common/assert.h" #include #include "tap/tap.h" #include diff --git a/tests/lib/test_graph_topo.c b/tests/lib/test_graph_topo.c index b0409d49..7a85f511 100644 --- a/tests/lib/test_graph_topo.c +++ b/tests/lib/test_graph_topo.c @@ -18,7 +18,7 @@ */ #include -#include +#include "common/assert.h" #include #include #include diff --git a/tests/lib/test_plugin.c b/tests/lib/test_plugin.c index 852b26fb..39b054e5 100644 --- a/tests/lib/test_plugin.c +++ b/tests/lib/test_plugin.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include "common/assert.h" #include #include "tap/tap.h" #include "common.h" diff --git a/tests/lib/test_trace_ir_ref.c b/tests/lib/test_trace_ir_ref.c index 99a1d374..10916fba 100644 --- a/tests/lib/test_trace_ir_ref.c +++ b/tests/lib/test_trace_ir_ref.c @@ -21,9 +21,9 @@ #include "tap/tap.h" #include -#include -#include -#include +#include "lib/object.h" +#include "compat/stdlib.h" +#include "common/assert.h" #include #include #include diff --git a/tests/lib/trace-ir/test_trace_ir.in b/tests/lib/trace-ir/test_trace_ir.in index e78b3445..b4d49dc6 100644 --- a/tests/lib/trace-ir/test_trace_ir.in +++ b/tests/lib/trace-ir/test_trace_ir.in @@ -20,15 +20,15 @@ NO_SH_TAP=1 . "@abs_top_builddir@/tests/utils/common.sh" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" TESTRUNNER_PY="${BT_SRC_PATH}/tests/utils/python/testrunner.py" THIS_DIR="${BT_SRC_PATH}/tests/lib/trace-ir" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${LD_LIBRARY_PATH}" fi PYTHONPATH="${PYTHON_BUILD_DIR}:${TESTS_UTILS_PYTHON_DIR}" \ diff --git a/tests/plugins/Makefile.am b/tests/plugins/Makefile.am index b5d584b7..015f775f 100644 --- a/tests/plugins/Makefile.am +++ b/tests/plugins/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(top_srcdir)/plugins +AM_CPPFLAGS += -I$(top_srcdir)/tests/utils -I$(top_srcdir)/src/plugins LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la @@ -16,19 +16,19 @@ if ENABLE_DEBUG_INFO noinst_PROGRAMS += test_dwarf test_bin_info test_dwarf_LDADD = \ - $(top_builddir)/plugins/lttng-utils/debug-info/libdebug-info.la \ - $(top_builddir)/fd-cache/libbabeltrace2-fd-cache.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ + $(top_builddir)/src/plugins/lttng-utils/debug-info/libdebug-info.la \ + $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ $(ELFUTILS_LIBS) \ $(LIBTAP) test_dwarf_SOURCES = test_dwarf.c test_bin_info_LDADD = \ - $(top_builddir)/plugins/lttng-utils/debug-info/libdebug-info.la \ - $(top_builddir)/fd-cache/libbabeltrace2-fd-cache.la \ - $(top_builddir)/logging/libbabeltrace2-logging.la \ - $(top_builddir)/common/libbabeltrace2-common.la \ + $(top_builddir)/src/plugins/lttng-utils/debug-info/libdebug-info.la \ + $(top_builddir)/src/fd-cache/libbabeltrace2-fd-cache.la \ + $(top_builddir)/src/logging/libbabeltrace2-logging.la \ + $(top_builddir)/src/common/libbabeltrace2-common.la \ $(ELFUTILS_LIBS) \ $(LIBTAP) test_bin_info_SOURCES = test_bin_info.c diff --git a/tests/plugins/test_bin_info.c b/tests/plugins/test_bin_info.c index 7f873b36..a4c95d05 100644 --- a/tests/plugins/test_bin_info.c +++ b/tests/plugins/test_bin_info.c @@ -27,8 +27,8 @@ #include #include -#include -#include +#include "common/babeltrace.h" +#include "common/assert.h" #include #include "tap/tap.h" diff --git a/tests/plugins/test_lttng_utils_debug_info.in b/tests/plugins/test_lttng_utils_debug_info.in index bb6b2d33..5d02aaee 100644 --- a/tests/plugins/test_lttng_utils_debug_info.in +++ b/tests/plugins/test_lttng_utils_debug_info.in @@ -20,17 +20,17 @@ NO_SH_TAP=1 . "@abs_top_builddir@/tests/utils/common.sh" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" TESTRUNNER_PY="${BT_SRC_PATH}/tests/utils/python/testrunner.py" THIS_DIR="${BT_SRC_PATH}/tests/plugins" -export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/plugins/utils:${BT_BUILD_PATH}/plugins/ctf:${BT_BUILD_PATH}/plugins/lttng-utils" +export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/src/plugins/utils:${BT_BUILD_PATH}/src/plugins/ctf:${BT_BUILD_PATH}/src/plugins/lttng-utils" export DEBUG_INFO_DATA_DIR="${BT_SRC_PATH}/tests/debug-info-data" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${LD_LIBRARY_PATH}" fi PYTHONPATH="${PYTHON_BUILD_DIR}:${TESTS_UTILS_PYTHON_DIR}" \ diff --git a/tests/python-plugin-provider/test_python_plugin_provider.in b/tests/python-plugin-provider/test_python_plugin_provider.in index 6f80f984..b6fc05a3 100644 --- a/tests/python-plugin-provider/test_python_plugin_provider.in +++ b/tests/python-plugin-provider/test_python_plugin_provider.in @@ -22,15 +22,15 @@ NO_SH_TAP=1 export PYTHON_PLUGIN_PROVIDER_TEST_PLUGIN_PATH="${BT_SRC_PATH}/tests/python-plugin-provider/bt_plugin_test_python_plugin_provider.py" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" TESTRUNNER_PY="${BT_SRC_PATH}/tests/utils/python/testrunner.py" THIS_DIR="${BT_SRC_PATH}/tests/python-plugin-provider" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${BT_BUILD_PATH}/python-plugin-provider/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${BT_BUILD_PATH}/src/python-plugin-provider/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${BT_BUILD_PATH}/python-plugin-provider/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${BT_BUILD_PATH}/src/python-plugin-provider/.libs:${LD_LIBRARY_PATH}" fi PYTHONPATH="${PYTHON_BUILD_DIR}:${TESTS_UTILS_PYTHON_DIR}" \ diff --git a/tests/python-plugin-provider/test_python_plugin_provider_env.in b/tests/python-plugin-provider/test_python_plugin_provider_env.in index 84307786..4d585f05 100644 --- a/tests/python-plugin-provider/test_python_plugin_provider_env.in +++ b/tests/python-plugin-provider/test_python_plugin_provider_env.in @@ -21,14 +21,14 @@ export PYTHON_PLUGIN_PROVIDER_TEST_PLUGIN_PATH="${BT_SRC_PATH}/tests/python-plugin-provider/bt_plugin_test_python_plugin_provider.py" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" export PYTHONPATH="${PYTHON_BUILD_DIR}:${TESTS_UTILS_PYTHON_DIR}" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${BT_BUILD_PATH}/python-plugin-provider/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${BT_BUILD_PATH}/src/python-plugin-provider/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${BT_BUILD_PATH}/python-plugin-provider/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${BT_BUILD_PATH}/src/python-plugin-provider/.libs:${LD_LIBRARY_PATH}" fi exec "$*" diff --git a/tests/utils/common.sh.in b/tests/utils/common.sh.in index 13de3679..976ef70e 100644 --- a/tests/utils/common.sh.in +++ b/tests/utils/common.sh.in @@ -3,7 +3,7 @@ BT_SRC_PATH="@abs_top_srcdir@" BT_BUILD_PATH="@abs_top_builddir@" -BT_BIN="${BT_BUILD_PATH}/cli/babeltrace2@EXEEXT@" +BT_BIN="${BT_BUILD_PATH}/src/cli/babeltrace2@EXEEXT@" BT_CTF_TRACES="${BT_SRC_PATH}/tests/ctf-traces" if [ "x${NO_SH_TAP}" = x ]; then diff --git a/tests/utils/test_python_bt2_env.in b/tests/utils/test_python_bt2_env.in index 03ac2522..d8e46b54 100644 --- a/tests/utils/test_python_bt2_env.in +++ b/tests/utils/test_python_bt2_env.in @@ -28,17 +28,17 @@ NO_SH_TAP=1 . "@abs_top_builddir@/tests/utils/common.sh" export BABELTRACE_PYTHON_BT2_NO_TRACEBACK=1 -export TEST_PLUGIN_PLUGINS_PATH="${BT_BUILD_PATH}/plugins" -export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/plugins/ctf:${BT_BUILD_PATH}/plugins/utils:${BT_BUILD_PATH}/plugins/text" +export TEST_PLUGIN_PLUGINS_PATH="${BT_BUILD_PATH}/src/plugins" +export BABELTRACE_PLUGIN_PATH="${BT_BUILD_PATH}/src/plugins/ctf:${BT_BUILD_PATH}/src/plugins/utils:${BT_BUILD_PATH}/src/plugins/text" export TEST_CTF_TRACES_PATH="${BT_SRC_PATH}/tests/ctf-traces" -PYTHON_BUILD_DIR="${BT_BUILD_PATH}/bindings/python/bt2/build/build_lib" +PYTHON_BUILD_DIR="${BT_BUILD_PATH}/src/bindings/python/bt2/build/build_lib" TESTS_UTILS_PYTHON_DIR="${BT_SRC_PATH}/tests/utils/python" export PYTHONPATH="${PYTHON_BUILD_DIR}:${TESTS_UTILS_PYTHON_DIR}" if [ "x${MSYSTEM}" != "x" ]; then - export PATH="${BT_BUILD_PATH}/lib/.libs:${PATH}" + export PATH="${BT_BUILD_PATH}/src/lib/.libs:${PATH}" else - export LD_LIBRARY_PATH="${BT_BUILD_PATH}/lib/.libs:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${BT_BUILD_PATH}/src/lib/.libs:${LD_LIBRARY_PATH}" fi exec "$@"